On 4/27/07, John Williams <[EMAIL PROTECTED]> wrote:
Grant,
Thanks for your work on the SystemACE driver - I'll be porting/merging
this across to MicroBlaze very shortly.
Very cool; I hope it works well.
Given that SysACE can be hooked up in any number of ways, bit widths,
endians, PPC/Microblaze, can you please offer your comments on the
attached patch?
Okay, after getting over my initial mirth about working on the *exact*
same thing and finishing it at the *exact* time that you sent me your
patch, I think I'm ready to make useful comments. :-)
It introduce a private ace_reg_ops structure, with various member
functions for the different kinds of accesses to the HW.
This patch should not change the functionality of your original driver
at all, it's just groundwork for what's to come.
I recognise that it adds indirection into the various access paths, and
potentially a little bloat. Whether this is better than #if
1...#else...#endif is debatable.
I'm not to concerned with the added redirection. On my 405 designs, I
find that bus overhead has a far greater impact than any of the
processing paths in the driver, so this shouldn't be a problem.
Besides, when we finally move to arch/powerpc, it will become very
feasable to have a single kernel image that will boot on multiple
ppc405 FPGA configurations; just change the device tree passed in.
Similar issues will arise for most (all?) of the Xilinx drivers that we
will share between PPC and MicroBlaze. Hopefully we can converge on a
nice consistent and clean way of handling these dual arch drivers.
I agree 100%
For your reading pleasure, I've attached the bus attachment changes
that I've made in my tree. I hope to get this driver accepted into
mainline during the 2.6.22 merge window; so please get any comments
you have back to me ASAP.
Cheers,
g.
From 10211a2cb23f9c7cc447f800a1f3828630cea819 Mon Sep 17 00:00:00 2001
From: Grant Likely <[email protected]>
Date: Fri, 27 Apr 2007 00:19:09 -0600
Subject: [PATCH] [SYSACE] Make bus binding selectable at runtime
The SystemACE can be wired up to many different bus arangements. This patch
defines 8, be16 and le16 bus attachments and adds the code to select them
at runtime.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/block/xsysace.c | 256 ++++++++++++++++++++++++++++++++++------------
1 files changed, 189 insertions(+), 67 deletions(-)
diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c
index e8b4cd4..5085770 100644
--- a/drivers/block/xsysace.c
+++ b/drivers/block/xsysace.c
@@ -80,7 +80,6 @@
*/
#undef DEBUG
-#undef DEBUG_ENDIAN /* Uncomment to debug register endinaness */
#include <linux/module.h>
#include <linux/ctype.h>
@@ -156,38 +155,11 @@ MODULE_LICENSE("GPL");
#define ACE_FATSTAT (0x1c)
#define ACE_NUM_MINORS 16
-#define ACE_BUF_PER_SECTOR (512 / 32) /* 512_byte_sector / 32_byte_fifo */
+#define ACE_SECTOR_SIZE (512)
+#define ACE_FIFO_SIZE (32)
+#define ACE_BUF_PER_SECTOR (ACE_SECTOR_SIZE / ACE_FIFO_SIZE)
-/* ---------------------------------------------------------------------
- * Low level register access
- */
-
-/* register access macros */
-#if 1 /* Little endian 16-bit regs */
-#define ace_reg_read8(ace, reg) in_8(ace->baseaddr + reg)
-#define ace_reg_read16(ace, reg) in_le16(ace->baseaddr + reg)
-#define ace_reg_readdata(ace, reg) in_be16(ace->baseaddr + reg)
-#define ace_reg_read32(ace, reg) ((in_le16(ace->baseaddr + reg+2) << 16) | \
- (in_le16(ace->baseaddr + reg)))
-#define ace_reg_write16(ace, reg, val) out_le16(ace->baseaddr + reg, val)
-#define ace_reg_writedata(ace, reg, val) out_be16(ace->baseaddr + reg, val)
-#define ace_reg_write32(ace, reg, val) { \
- out_le16(ace->baseaddr + reg+2, (val) >> 16); \
- out_le16(ace->baseaddr + reg, val); \
- }
-#else /* Big endian 16-bit regs */
-#define ace_reg_read8(ace, reg) in_8(ace->baseaddr + reg)
-#define ace_reg_read16(ace, reg) in_be16(ace->baseaddr + reg)
-#define ace_reg_readdata(ace, reg) in_le16(ace->baseaddr + reg)
-#define ace_reg_read32(ace, reg) ((in_be16(ace->baseaddr + reg+2) << 16) | \
- (in_be16(ace->baseaddr + reg)))
-#define ace_reg_write16(ace, reg, val) out_be16(ace->baseaddr + reg, val)
-#define ace_reg_writedata(ace, reg, val) out_le16(ace->baseaddr + reg, val)
-#define ace_reg_write32(ace, reg, val) { \
- out_be16(ace->baseaddr + reg+2, (val) >> 16); \
- out_be16(ace->baseaddr + reg, val); \
- }
-#endif
+struct ace_reg_ops;
struct ace_device {
/* driver state data */
@@ -220,6 +192,7 @@ struct ace_device {
void* baseaddr;
int irq;
int bus_width; /* 0 := 8 bit; 1 := 16 bit */
+ struct ace_reg_ops *reg_ops;
int lock_count;
/* Block device data structures */
@@ -236,6 +209,168 @@ static LIST_HEAD(ace_instances);
static int ace_major = 0;
/* ---------------------------------------------------------------------
+ * Low level register access
+ */
+
+struct ace_reg_ops {
+ uint16_t (*in)(struct ace_device *ace, ulong reg);
+ void (*out)(struct ace_device *ace, ulong reg, uint16_t val);
+ void (*identin)(struct ace_device *ace);
+ void (*datain)(struct ace_device *ace);
+ void (*dataout)(struct ace_device *ace);
+};
+
+/* 8 Bit bus width */
+static uint16_t ace_in_8(struct ace_device *ace, ulong reg)
+{
+ void* r = ace->baseaddr + reg;
+ return in_8(r) | (in_8(r+1) << 8);
+}
+
+static void ace_out_8(struct ace_device *ace, ulong reg, uint16_t val)
+{
+ void* r = ace->baseaddr + reg;
+ out_8(r, val);
+ out_8(r+1, val >> 8);
+}
+
+static void ace_identin_8(struct ace_device *ace)
+{
+ void* r = ace->baseaddr + 0x40;
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+#if defined(__BIG_ENDIAN)
+ *ace->data_ptr = (in_8(r)) | (in_8(r+1)<<8);
+#else
+ *ace->data_ptr = (in_8(r)<<8) | (in_8(r+1));
+#endif
+ ace->data_count--;
+}
+
+static void ace_datain_8(struct ace_device *ace)
+{
+ void* r = ace->baseaddr + 0x40;
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+#if defined(__BIG_ENDIAN)
+ *ace->data_ptr = (in_8(r)<<8) | (in_8(r+1));
+#else
+ *ace->data_ptr = (in_8(r)) | (in_8(r+1)<<8);
+#endif
+}
+
+static void ace_dataout_8(struct ace_device *ace)
+{
+ void* r = ace->baseaddr + 0x40;
+ int i = ACE_FIFO_SIZE/2;
+ while (i--) {
+#if defined(__BIG_ENDIAN)
+ out_8(r, *ace->data_ptr >> 8);
+ out_8(r+1, *ace->data_ptr);
+#else
+ out_8(r, *ace->data_ptr);
+ out_8(r+1, *ace->data_ptr >> 8);
+#endif
+ ace->data_ptr++;
+ }
+}
+
+static struct ace_reg_ops ace_reg_8_ops = {
+ .in = ace_in_8,
+ .out = ace_out_8,
+ .identin = ace_identin_8,
+ .datain = ace_datain_8,
+ .dataout = ace_dataout_8,
+};
+
+/* 16 bit big endian bus attachment */
+static uint16_t ace_in_be16(struct ace_device *ace, ulong reg)
+{
+ return in_be16(ace->baseaddr+reg);
+}
+
+static void ace_out_be16(struct ace_device *ace, ulong reg, uint16_t val)
+{
+ out_be16(ace->baseaddr+reg, val);
+}
+
+static void ace_fifoin_be16(struct ace_device *ace)
+{
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+ *ace->data_ptr++ = in_be16(ace->baseaddr + 0x40);
+}
+
+static void ace_fifoout_be16(struct ace_device *ace)
+{
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+ out_be16(ace->baseaddr + 0x40, *ace->data_ptr++);
+}
+
+/* 16 bit little endian bus attachment */
+static uint16_t ace_in_le16(struct ace_device *ace, ulong reg)
+{
+ return in_le16(ace->baseaddr+reg);
+}
+
+static void ace_out_le16(struct ace_device *ace, ulong reg, uint16_t val)
+{
+ out_le16(ace->baseaddr+reg, val);
+}
+
+static void ace_fifoin_le16(struct ace_device *ace)
+{
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+ *ace->data_ptr++ = in_le16(ace->baseaddr + 0x40);
+}
+
+static void ace_fifoout_le16(struct ace_device *ace)
+{
+ int i = ACE_FIFO_SIZE/2;
+ while (i--)
+ out_le16(ace->baseaddr + 0x40, *ace->data_ptr++);
+}
+
+static struct ace_reg_ops ace_reg_be16_ops = {
+ .in = ace_in_be16,
+ .out = ace_out_be16,
+ .identin = ace_fifoin_be16,
+ .datain = ace_fifoin_le16, /* little endian! */
+ .dataout = ace_fifoout_le16, /* little endian! */
+};
+
+static struct ace_reg_ops ace_reg_le16_ops = {
+ .in = ace_in_le16,
+ .out = ace_out_le16,
+ .identin = ace_fifoin_le16,
+ .datain = ace_fifoin_be16, /* big endian! */
+ .dataout = ace_fifoout_be16, /* big endian! */
+};
+
+#define ace_in(ace, reg) ace->reg_ops->in(ace, reg)
+static inline uint32_t ace_in32(struct ace_device *ace, ulong reg)
+{
+ return ace_in(ace, reg) | (ace_in(ace, reg+2) << 16);
+}
+#define ace_out(ace, reg, val) ace->reg_ops->out(ace, reg, val)
+static inline void ace_out32(struct ace_device *ace, ulong reg, uint32_t val)
+{
+ ace_out(ace, reg, val);
+ ace_out(ace, reg+2, val >> 16);
+}
+#define ace_identin(ace) ace->reg_ops->identin(ace)
+#define ace_datain(ace) ace->reg_ops->datain(ace)
+#define ace_dataout(ace) ace->reg_ops->dataout(ace)
+
+/* legacy macros, to be removed */
+#define ace_reg_read16(ace, reg) ace_in(ace, reg)
+#define ace_reg_read32(ace, reg) ace_in32(ace, reg)
+#define ace_reg_write16(ace, reg, val) ace_out(ace, reg, val)
+#define ace_reg_write32(ace, reg, val) ace_out32(ace, reg, val)
+
+/* ---------------------------------------------------------------------
* Debug support functions
*/
@@ -270,31 +405,6 @@ static inline void ace_dump_mem(void* base, int len) {}
static void ace_dump_regs(struct ace_device *ace)
{
-
-#if defined(DEBUG_ENDIAN)
- /* some test routines to see if 8 16 and 32 bit access is working */
- ace_info(ace, "register dump:"); /* No '\n' needed! */
- {
- int i;
- for (i = 0; i < 0x20; i++) {
- if (!(i % 16))
- printk("\n" KERN_INFO " %.2x:", i);
- printk(" %.2x", ace_reg_read8(ace, i));
- }
- for (i = 0; i < 0x20; i+=2) {
- if (!(i % 16))
- printk("\n" KERN_INFO " %.2x:", i);
- printk(" %.4x ", ace_reg_read16(ace, i));
- }
- for (i = 0; i < 0x20; i+=4) {
- if (!(i % 16))
- printk("\n" KERN_INFO " %.2x:", i);
- printk(" %.8x ", ace_reg_read32(ace, i));
- }
- }
- printk("\n");
-#endif
-
ace_info(ace, " ctrl: %.8x seccnt/cmd: %.4x ver:%.4x\n"
" status:%.8x mpu_lba:%.8x busmode:%4x\n"
" error: %.8x cfg_lba:%.8x fatstat:%.4x\n",
@@ -348,7 +458,8 @@ void ace_fix_driveid (struct hd_driveid *id)
#define ACE_FSM_STATE_REQ_PREPARE 7
#define ACE_FSM_STATE_REQ_TRANSFER 8
#define ACE_FSM_STATE_REQ_COMPLETE 9
-#define ACE_FSM_NUM_STATES 10
+#define ACE_FSM_STATE_ERROR 10
+#define ACE_FSM_NUM_STATES 11
#if defined(DEBUG)
const char* ace_statenames[ACE_FSM_NUM_STATES] = {
@@ -497,9 +608,7 @@ static void ace_fsm_dostate(struct ace_device *ace)
}
/* Transfer the next buffer */
- i = 16;
- while (i--)
- *ace->data_ptr++ = ace_reg_read16(ace, 0x40);
+ ace_identin(ace);
ace->data_count--;
/* If there are still buffers to be transfers; jump out here */
@@ -608,11 +717,9 @@ static void ace_fsm_dostate(struct ace_device *ace)
/* Transfer the next buffer */
i = 16;
if (ace->fsm_task == ACE_TASK_WRITE)
- while (i--)
- ace_reg_writedata(ace, 0x40, *ace->data_ptr++);
+ ace_dataout(ace);
else
- while (i--)
- *ace->data_ptr++ = ace_reg_readdata(ace, 0x40);
+ ace_datain(ace);
ace->data_count--;
/* If there are still buffers to be transfers; jump out here */
@@ -906,8 +1013,20 @@ static int ace_setup(struct ace_device *ace)
snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a');
device_rename(ace->dev, ace->gd->disk_name);
- /* set 16-bit bus width */
- ace_reg_write16(ace, ACE_BUSMODE, 0x0001);
+ /* set bus width */
+ if (ace->bus_width == 1) {
+ /* 0x0101 should work regardless of endianess */
+ ace_out_le16(ace, ACE_BUSMODE, 0x0101);
+
+ /* read it back to determine endianess */
+ if (ace_in_le16(ace, ACE_BUSMODE) == 0x0001)
+ ace->reg_ops = &ace_reg_le16_ops;
+ else
+ ace->reg_ops = &ace_reg_be16_ops;
+ } else {
+ ace_out_8(ace, ACE_BUSMODE, 0x00);
+ ace->reg_ops = &ace_reg_8_ops;
+ }
/* Make sure version register is sane */
version = ace_reg_read16(ace, ACE_VERSION);
@@ -1001,6 +1120,9 @@ static int ace_probe(struct device *device)
if (dev->resource[i].flags & IORESOURCE_IRQ)
ace->irq = dev->resource[i].start;
}
+
+ /* FIXME: Should get bus_width from the platform_device struct */
+ ace->bus_width = 1;
dev_set_drvdata(&dev->dev, ace);
--
1.5.1
_______________________________________________
Linuxppc-embedded mailing list
[email protected]
https://ozlabs.org/mailman/listinfo/linuxppc-embedded