<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<META NAME="Generator" CONTENT="MS Exchange Server version 6.5.7652.24">
<TITLE>RE: [PATCH] Xilinx SystemACE device driver</TITLE>
</HEAD>
<BODY>
<!-- Converted from text/plain format -->
<P><FONT SIZE=2>Hi,<BR>
<BR>
Ok, so outlook is a problem. The horror is that, where I work thats all I can use. They block all the outside systems like gmail, yahoo, etc. So under linux, I have to use the web access for outlook. ugh. <BR>
Why I work here I don't know, they think all software HAS to be bought.<BR>
<BR>
Um, so your patch creates another xsysace.c file, all by itself, which is a NEW driver? <BR>
This replaces the 8 files of the previous driver? What happens to all the low level funcs?<BR>
<BR>
Where can I get your 2.6.22 tree to see how this is all supposed to go together? Is there a tar.bz2 package?<BR>
<BR>
Thanks for your patience.<BR>
<BR>
<BR>
Joe Robertson<BR>
x8259<BR>
Joseph.Robertson@sanmina-sci.com<BR>
<BR>
<BR>
<BR>
-----Original Message-----<BR>
From: glikely@secretlab.ca on behalf of Grant Likely<BR>
Sent: Fri 7/13/2007 10:41 AM<BR>
To: Robertson, Joseph M.; Linux PPC Linux PPC<BR>
Subject: Re: [PATCH] Xilinx SystemACE device driver<BR>
<BR>
On 7/13/07, Robertson, Joseph M. <joseph.robertson@sanmina-sci.com> wrote:<BR>
><BR>
><BR>
><BR>
> Hi,<BR>
><BR>
> I apologize if I am just being dense, but cannot get this patch to work.<BR>
<BR>
BTW, please make sure you CC the mailing list when asking questions.<BR>
That way more people than just me can offer answers.<BR>
<BR>
You will probably get conflicts when applying the patch which you need<BR>
to fixup, but the conflicts should be limited to Kconfig and Makefile.<BR>
<BR>
> I get the same problem.<BR>
> What command do you use to apply the patch? patch -p1 < filename<BR>
> I put file in kernel root and change b/drivers/block/xsysace.c -><BR>
> b/drivers/block/xilinx_sysace/xsysace.c (the 'old'<BR>
> location?)<BR>
<BR>
Yes, 'patch -p1 < file' is the correct command. Make sure you start<BR>
at the top of the Linux kernel source tree. Also make sure that you<BR>
are using the raw email that I sent you. Do *NOT* try to copy and<BR>
paste from your mail client. It will not work. Outlook is<BR>
particularly bad for working with patches, so if you can change mail<BR>
clients, or start using a gmail account, your life will get easier.<BR>
<BR>
> I thought I had a too old version of patch, but its actually later than the<BR>
> gnu website(2.5.4), I have 2.5.9<BR>
<BR>
The patch format is *very* stable. This is certainly not your problem.<BR>
><BR>
> Can you post the complete file for xsysace v1.01a somewhere?<BR>
> Or tell me, does the xsysace.c code completely replace the previous<BR>
> xsysace.c file?<BR>
<BR>
The new driver is a 100% rewrite. In fact, the old and new drivers<BR>
can coexist side-by-side. The new driver is fully contained in a<BR>
single source file (xsysace.c).<BR>
<BR>
><BR>
> Or perhaps point out what xsysace bug we need to fix?<BR>
<BR>
I have no idea. I cannot say without trying to reproduce the problem,<BR>
and 2.6.17 is far to old for me to want to twiddle with.<BR>
<BR>
What xilinx devices are you using? TEMAC, SystemACE I know. Any others?<BR>
<BR>
><BR>
> Thanks a lot. I hope to be able to contribute one day.<BR>
<BR>
:-)<BR>
<BR>
><BR>
> Joe Robertson<BR>
> x8259<BR>
> Joseph.Robertson@sanmina-sci.com<BR>
><BR>
><BR>
><BR>
> -----Original Message-----<BR>
> From: Grant Likely [<A HREF="mailto:grant.likely@secretlab.ca">mailto:grant.likely@secretlab.ca</A>]<BR>
> Sent: Thu 7/12/2007 5:51 PM<BR>
> To: Robertson@secretlab.ca; Robertson, Joseph M.<BR>
> Subject: [PATCH] Xilinx SystemACE device driver<BR>
><BR>
> From: Grant Likely <grant.likely@secretlab.ca><BR>
><BR>
> Add support for block device access to the Xilinx SystemACE Compact<BR>
> flash interface<BR>
><BR>
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca><BR>
> ---<BR>
><BR>
> drivers/block/Kconfig | 6<BR>
> drivers/block/Makefile | 1<BR>
> drivers/block/xsysace.c | 1167<BR>
> +++++++++++++++++++++++++++++++++++++++++++++++<BR>
> 3 files changed, 1174 insertions(+), 0 deletions(-)<BR>
><BR>
> diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig<BR>
> index b4c8319..184b30d 100644<BR>
> --- a/drivers/block/Kconfig<BR>
> +++ b/drivers/block/Kconfig<BR>
> @@ -453,6 +453,12 @@ config ATA_OVER_ETH<BR>
><BR>
> source "drivers/s390/block/Kconfig"<BR>
><BR>
> +config XILINX_SYSACE<BR>
> + tristate "Xilinx SystemACE support"<BR>
> + depends on 4xx<BR>
> + help<BR>
> + Include support for the Xilinx SystemACE CompactFlash interface<BR>
> +<BR>
> endmenu<BR>
><BR>
> endif<BR>
> diff --git a/drivers/block/Makefile b/drivers/block/Makefile<BR>
> index dd88e33..31ea323 100644<BR>
> --- a/drivers/block/Makefile<BR>
> +++ b/drivers/block/Makefile<BR>
> @@ -28,4 +28,5 @@ obj-$(CONFIG_BLK_DEV_CRYPTOLOOP) +=<BR>
> cryptoloop.o<BR>
> obj-$(CONFIG_VIODASD) += viodasd.o<BR>
> obj-$(CONFIG_BLK_DEV_SX8) += sx8.o<BR>
> obj-$(CONFIG_BLK_DEV_UB) += ub.o<BR>
> +obj-$(CONFIG_XILINX_SYSACE) += xsysace.o<BR>
><BR>
> diff --git a/drivers/block/xsysace.c b/drivers/block/xsysace.c<BR>
> new file mode 100644<BR>
> index 0000000..f8602e6<BR>
> --- /dev/null<BR>
> +++ b/drivers/block/xsysace.c<BR>
> @@ -0,0 +1,1167 @@<BR>
> +/*<BR>
> + * Xilinx SystemACE device driver<BR>
> + *<BR>
> + * Copyright 2007 Secret Lab Technologies Ltd.<BR>
> + *<BR>
> + * This program is free software; you can redistribute it and/or modify it<BR>
> + * under the terms of the GNU General Public License version 2 as<BR>
> published<BR>
> + * by the Free Software Foundation.<BR>
> + */<BR>
> +<BR>
> +/*<BR>
> + * The SystemACE chip is designed to configure FPGAs by loading an FPGA<BR>
> + * bitstream from a file on a CF card and squirting it into FPGAs<BR>
> connected<BR>
> + * to the SystemACE JTAG chain. It also has the advantage of providing an<BR>
> + * MPU interface which can be used to control the FPGA configuration<BR>
> process<BR>
> + * and to use the attached CF card for general purpose storage.<BR>
> + *<BR>
> + * This driver is a block device driver for the SystemACE.<BR>
> + *<BR>
> + * Initialization:<BR>
> + * The driver registers itself as a platform_device driver at module<BR>
> + * load time. The platform bus will take care of calling the<BR>
> + * ace_probe() method for all SystemACE instances in the system. Any<BR>
> + * number of SystemACE instances are supported. ace_probe() calls<BR>
> + * ace_setup() which initialized all data structures, reads the CF<BR>
> + * id structure and registers the device.<BR>
> + *<BR>
> + * Processing:<BR>
> + * Just about all of the heavy lifting in this driver is performed by<BR>
> + * a Finite State Machine (FSM). The driver needs to wait on a number<BR>
> + * of events; some raised by interrupts, some which need to be polled<BR>
> + * for. Describing all of the behaviour in a FSM seems to be the<BR>
> + * easiest way to keep the complexity low and make it easy to<BR>
> + * understand what the driver is doing. If the block ops or the<BR>
> + * request function need to interact with the hardware, then they<BR>
> + * simply need to flag the request and kick of FSM processing.<BR>
> + *<BR>
> + * The FSM itself is atomic-safe code which can be run from any<BR>
> + * context. The general process flow is:<BR>
> + * 1. obtain the ace->lock spinlock.<BR>
> + * 2. loop on ace_fsm_dostate() until the ace->fsm_continue flag is<BR>
> + * cleared.<BR>
> + * 3. release the lock.<BR>
> + *<BR>
> + * Individual states do not sleep in any way. If a condition needs to<BR>
> + * be waited for then the state much clear the fsm_continue flag and<BR>
> + * either schedule the FSM to be run again at a later time, or expect<BR>
> + * an interrupt to call the FSM when the desired condition is met.<BR>
> + *<BR>
> + * In normal operation, the FSM is processed at interrupt context<BR>
> + * either when the driver's tasklet is scheduled, or when an irq is<BR>
> + * raised by the hardware. The tasklet can be scheduled at any time.<BR>
> + * The request method in particular schedules the tasklet when a new<BR>
> + * request has been indicated by the block layer. Once started, the<BR>
> + * FSM proceeds as far as it can processing the request until it<BR>
> + * needs on a hardware event. At this point, it must yield execution.<BR>
> + *<BR>
> + * A state has two options when yielding execution:<BR>
> + * 1. ace_fsm_yield()<BR>
> + * - Call if need to poll for event.<BR>
> + * - clears the fsm_continue flag to exit the processing loop<BR>
> + * - reschedules the tasklet to run again as soon as possible<BR>
> + * 2. ace_fsm_yieldirq()<BR>
> + * - Call if an irq is expected from the HW<BR>
> + * - clears the fsm_continue flag to exit the processing loop<BR>
> + * - does not reschedule the tasklet so the FSM will not be<BR>
> processed<BR>
> + * again until an irq is received.<BR>
> + * After calling a yield function, the state must return control back<BR>
> + * to the FSM main loop.<BR>
> + *<BR>
> + * Additionally, the driver maintains a kernel timer which can process<BR>
> + * the FSM. If the FSM gets stalled, typically due to a missed<BR>
> + * interrupt, then the kernel timer will expire and the driver can<BR>
> + * continue where it left off.<BR>
> + *<BR>
> + * To Do:<BR>
> + * - Add FPGA configuration control interface.<BR>
> + * - Request major number from lanana<BR>
> + */<BR>
> +<BR>
> +#undef DEBUG<BR>
> +<BR>
> +#include <linux/module.h><BR>
> +#include <linux/ctype.h><BR>
> +#include <linux/init.h><BR>
> +#include <linux/interrupt.h><BR>
> +#include <linux/errno.h><BR>
> +#include <linux/kernel.h><BR>
> +#include <linux/delay.h><BR>
> +#include <linux/slab.h><BR>
> +#include <linux/blkdev.h><BR>
> +#include <linux/hdreg.h><BR>
> +#include <linux/platform_device.h><BR>
> +<BR>
> +MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");<BR>
> +MODULE_DESCRIPTION("Xilinx SystemACE device driver");<BR>
> +MODULE_LICENSE("GPL");<BR>
> +<BR>
> +/* SystemACE register definitions */<BR>
> +#define ACE_BUSMODE (0x00)<BR>
> +<BR>
> +#define ACE_STATUS (0x04)<BR>
> +#define ACE_STATUS_CFGLOCK (0x00000001)<BR>
> +#define ACE_STATUS_MPULOCK (0x00000002)<BR>
> +#define ACE_STATUS_CFGERROR (0x00000004) /* config controller error<BR>
> */<BR>
> +#define ACE_STATUS_CFCERROR (0x00000008) /* CF controller error */<BR>
> +#define ACE_STATUS_CFDETECT (0x00000010)<BR>
> +#define ACE_STATUS_DATABUFRDY (0x00000020)<BR>
> +#define ACE_STATUS_DATABUFMODE (0x00000040)<BR>
> +#define ACE_STATUS_CFGDONE (0x00000080)<BR>
> +#define ACE_STATUS_RDYFORCFCMD (0x00000100)<BR>
> +#define ACE_STATUS_CFGMODEPIN (0x00000200)<BR>
> +#define ACE_STATUS_CFGADDR_MASK (0x0000e000)<BR>
> +#define ACE_STATUS_CFBSY (0x00020000)<BR>
> +#define ACE_STATUS_CFRDY (0x00040000)<BR>
> +#define ACE_STATUS_CFDWF (0x00080000)<BR>
> +#define ACE_STATUS_CFDSC (0x00100000)<BR>
> +#define ACE_STATUS_CFDRQ (0x00200000)<BR>
> +#define ACE_STATUS_CFCORR (0x00400000)<BR>
> +#define ACE_STATUS_CFERR (0x00800000)<BR>
> +<BR>
> +#define ACE_ERROR (0x08)<BR>
> +#define ACE_CFGLBA (0x0c)<BR>
> +#define ACE_MPULBA (0x10)<BR>
> +<BR>
> +#define ACE_SECCNTCMD (0x14)<BR>
> +#define ACE_SECCNTCMD_RESET (0x0100)<BR>
> +#define ACE_SECCNTCMD_IDENTIFY (0x0200)<BR>
> +#define ACE_SECCNTCMD_READ_DATA (0x0300)<BR>
> +#define ACE_SECCNTCMD_WRITE_DATA (0x0400)<BR>
> +#define ACE_SECCNTCMD_ABORT (0x0600)<BR>
> +<BR>
> +#define ACE_VERSION (0x16)<BR>
> +#define ACE_VERSION_REVISION_MASK (0x00FF)<BR>
> +#define ACE_VERSION_MINOR_MASK (0x0F00)<BR>
> +#define ACE_VERSION_MAJOR_MASK (0xF000)<BR>
> +<BR>
> +#define ACE_CTRL (0x18)<BR>
> +#define ACE_CTRL_FORCELOCKREQ (0x0001)<BR>
> +#define ACE_CTRL_LOCKREQ (0x0002)<BR>
> +#define ACE_CTRL_FORCECFGADDR (0x0004)<BR>
> +#define ACE_CTRL_FORCECFGMODE (0x0008)<BR>
> +#define ACE_CTRL_CFGMODE (0x0010)<BR>
> +#define ACE_CTRL_CFGSTART (0x0020)<BR>
> +#define ACE_CTRL_CFGSEL (0x0040)<BR>
> +#define ACE_CTRL_CFGRESET (0x0080)<BR>
> +#define ACE_CTRL_DATABUFRDYIRQ (0x0100)<BR>
> +#define ACE_CTRL_ERRORIRQ (0x0200)<BR>
> +#define ACE_CTRL_CFGDONEIRQ (0x0400)<BR>
> +#define ACE_CTRL_RESETIRQ (0x0800)<BR>
> +#define ACE_CTRL_CFGPROG (0x1000)<BR>
> +#define ACE_CTRL_CFGADDR_MASK (0xe000)<BR>
> +<BR>
> +#define ACE_FATSTAT (0x1c)<BR>
> +<BR>
> +#define ACE_NUM_MINORS 16<BR>
> +#define ACE_SECTOR_SIZE (512)<BR>
> +#define ACE_FIFO_SIZE (32)<BR>
> +#define ACE_BUF_PER_SECTOR (ACE_SECTOR_SIZE / ACE_FIFO_SIZE)<BR>
> +<BR>
> +struct ace_reg_ops;<BR>
> +<BR>
> +struct ace_device {<BR>
> + /* driver state data */<BR>
> + int id;<BR>
> + int media_change;<BR>
> + int users;<BR>
> + struct list_head list;<BR>
> +<BR>
> + /* finite state machine data */<BR>
> + struct tasklet_struct fsm_tasklet;<BR>
> + uint fsm_task; /* Current activity (ACE_TASK_*) */<BR>
> + uint fsm_state; /* Current state (ACE_FSM_STATE_*) */<BR>
> + uint fsm_continue_flag; /* cleared to exit FSM mainloop */<BR>
> + uint fsm_iter_num;<BR>
> + struct timer_list stall_timer;<BR>
> +<BR>
> + /* Transfer state/result, use for both id and block request */<BR>
> + struct request *req; /* request being processed */<BR>
> + void *data_ptr; /* pointer to I/O buffer */<BR>
> + int data_count; /* number of buffers remaining */<BR>
> + int data_result; /* Result of transfer; 0 := success */<BR>
> +<BR>
> + int id_req_count; /* count of id requests */<BR>
> + int id_result;<BR>
> + struct completion id_completion; /* used when id req<BR>
> finishes */<BR>
> + int in_irq;<BR>
> +<BR>
> + /* Details of hardware device */<BR>
> + unsigned long physaddr;<BR>
> + void *baseaddr;<BR>
> + int irq;<BR>
> + int bus_width; /* 0 := 8 bit; 1 := 16 bit */<BR>
> + struct ace_reg_ops *reg_ops;<BR>
> + int lock_count;<BR>
> +<BR>
> + /* Block device data structures */<BR>
> + spinlock_t lock;<BR>
> + struct device *dev;<BR>
> + struct request_queue *queue;<BR>
> + struct gendisk *gd;<BR>
> +<BR>
> + /* Inserted CF card parameters */<BR>
> + struct hd_driveid cf_id;<BR>
> +};<BR>
> +<BR>
> +static int ace_major;<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Low level register access<BR>
> + */<BR>
> +<BR>
> +struct ace_reg_ops {<BR>
> + u16(*in) (struct ace_device * ace, int reg);<BR>
> + void (*out) (struct ace_device * ace, int reg, u16 val);<BR>
> + void (*datain) (struct ace_device * ace);<BR>
> + void (*dataout) (struct ace_device * ace);<BR>
> +};<BR>
> +<BR>
> +/* 8 Bit bus width */<BR>
> +static u16 ace_in_8(struct ace_device *ace, int reg)<BR>
> +{<BR>
> + void *r = ace->baseaddr + reg;<BR>
> + return in_8(r) | (in_8(r + 1) << 8);<BR>
> +}<BR>
> +<BR>
> +static void ace_out_8(struct ace_device *ace, int reg, u16 val)<BR>
> +{<BR>
> + void *r = ace->baseaddr + reg;<BR>
> + out_8(r, val);<BR>
> + out_8(r + 1, val >> 8);<BR>
> +}<BR>
> +<BR>
> +static void ace_datain_8(struct ace_device *ace)<BR>
> +{<BR>
> + void *r = ace->baseaddr + 0x40;<BR>
> + u8 *dst = ace->data_ptr;<BR>
> + int i = ACE_FIFO_SIZE;<BR>
> + while (i--)<BR>
> + *dst++ = in_8(r++);<BR>
> + ace->data_ptr = dst;<BR>
> +}<BR>
> +<BR>
> +static void ace_dataout_8(struct ace_device *ace)<BR>
> +{<BR>
> + void *r = ace->baseaddr + 0x40;<BR>
> + u8 *src = ace->data_ptr;<BR>
> + int i = ACE_FIFO_SIZE;<BR>
> + while (i--)<BR>
> + out_8(r++, *src++);<BR>
> + ace->data_ptr = src;<BR>
> +}<BR>
> +<BR>
> +static struct ace_reg_ops ace_reg_8_ops = {<BR>
> + .in = ace_in_8,<BR>
> + .out = ace_out_8,<BR>
> + .datain = ace_datain_8,<BR>
> + .dataout = ace_dataout_8,<BR>
> +};<BR>
> +<BR>
> +/* 16 bit big endian bus attachment */<BR>
> +static u16 ace_in_be16(struct ace_device *ace, int reg)<BR>
> +{<BR>
> + return in_be16(ace->baseaddr + reg);<BR>
> +}<BR>
> +<BR>
> +static void ace_out_be16(struct ace_device *ace, int reg, u16 val)<BR>
> +{<BR>
> + out_be16(ace->baseaddr + reg, val);<BR>
> +}<BR>
> +<BR>
> +static void ace_datain_be16(struct ace_device *ace)<BR>
> +{<BR>
> + int i = ACE_FIFO_SIZE / 2;<BR>
> + u16 *dst = ace->data_ptr;<BR>
> + while (i--)<BR>
> + *dst++ = in_le16(ace->baseaddr + 0x40);<BR>
> + ace->data_ptr = dst;<BR>
> +}<BR>
> +<BR>
> +static void ace_dataout_be16(struct ace_device *ace)<BR>
> +{<BR>
> + int i = ACE_FIFO_SIZE / 2;<BR>
> + u16 *src = ace->data_ptr;<BR>
> + while (i--)<BR>
> + out_le16(ace->baseaddr + 0x40, *src++);<BR>
> + ace->data_ptr = src;<BR>
> +}<BR>
> +<BR>
> +/* 16 bit little endian bus attachment */<BR>
> +static u16 ace_in_le16(struct ace_device *ace, int reg)<BR>
> +{<BR>
> + return in_le16(ace->baseaddr + reg);<BR>
> +}<BR>
> +<BR>
> +static void ace_out_le16(struct ace_device *ace, int reg, u16 val)<BR>
> +{<BR>
> + out_le16(ace->baseaddr + reg, val);<BR>
> +}<BR>
> +<BR>
> +static void ace_datain_le16(struct ace_device *ace)<BR>
> +{<BR>
> + int i = ACE_FIFO_SIZE / 2;<BR>
> + u16 *dst = ace->data_ptr;<BR>
> + while (i--)<BR>
> + *dst++ = in_be16(ace->baseaddr + 0x40);<BR>
> + ace->data_ptr = dst;<BR>
> +}<BR>
> +<BR>
> +static void ace_dataout_le16(struct ace_device *ace)<BR>
> +{<BR>
> + int i = ACE_FIFO_SIZE / 2;<BR>
> + u16 *src = ace->data_ptr;<BR>
> + while (i--)<BR>
> + out_be16(ace->baseaddr + 0x40, *src++);<BR>
> + ace->data_ptr = src;<BR>
> +}<BR>
> +<BR>
> +static struct ace_reg_ops ace_reg_be16_ops = {<BR>
> + .in = ace_in_be16,<BR>
> + .out = ace_out_be16,<BR>
> + .datain = ace_datain_be16,<BR>
> + .dataout = ace_dataout_be16,<BR>
> +};<BR>
> +<BR>
> +static struct ace_reg_ops ace_reg_le16_ops = {<BR>
> + .in = ace_in_le16,<BR>
> + .out = ace_out_le16,<BR>
> + .datain = ace_datain_le16,<BR>
> + .dataout = ace_dataout_le16,<BR>
> +};<BR>
> +<BR>
> +static inline u16 ace_in(struct ace_device *ace, int reg)<BR>
> +{<BR>
> + return ace->reg_ops->in(ace, reg);<BR>
> +}<BR>
> +<BR>
> +static inline u32 ace_in32(struct ace_device *ace, int reg)<BR>
> +{<BR>
> + return ace_in(ace, reg) | (ace_in(ace, reg + 2) << 16);<BR>
> +}<BR>
> +<BR>
> +static inline void ace_out(struct ace_device *ace, int reg, u16 val)<BR>
> +{<BR>
> + ace->reg_ops->out(ace, reg, val);<BR>
> +}<BR>
> +<BR>
> +static inline void ace_out32(struct ace_device *ace, int reg, u32 val)<BR>
> +{<BR>
> + ace_out(ace, reg, val);<BR>
> + ace_out(ace, reg + 2, val >> 16);<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Debug support functions<BR>
> + */<BR>
> +<BR>
> +#if defined(DEBUG)<BR>
> +static void ace_dump_mem(void *base, int len)<BR>
> +{<BR>
> + const char *ptr = base;<BR>
> + int i, j;<BR>
> +<BR>
> + for (i = 0; i < len; i += 16) {<BR>
> + printk(KERN_INFO "%.8x:", i);<BR>
> + for (j = 0; j < 16; j++) {<BR>
> + if (!(j % 4))<BR>
> + printk(" ");<BR>
> + printk("%.2x", ptr[i + j]);<BR>
> + }<BR>
> + printk(" ");<BR>
> + for (j = 0; j < 16; j++)<BR>
> + printk("%c", isprint(ptr[i + j]) ? ptr[i + j] :<BR>
> '.');<BR>
> + printk("\n");<BR>
> + }<BR>
> +}<BR>
> +#else<BR>
> +static inline void ace_dump_mem(void *base, int len)<BR>
> +{<BR>
> +}<BR>
> +#endif<BR>
> +<BR>
> +static void ace_dump_regs(struct ace_device *ace)<BR>
> +{<BR>
> + dev_info(ace->dev, " ctrl: %.8x seccnt/cmd: %.4x<BR>
> ver:%.4x\n"<BR>
> + " status:%.8x mpu_lba:%.8x busmode:%4x\n"<BR>
> + " error: %.8x cfg_lba:%.8x fatstat:%.4x\n",<BR>
> + ace_in32(ace, ACE_CTRL),<BR>
> + ace_in(ace, ACE_SECCNTCMD),<BR>
> + ace_in(ace, ACE_VERSION),<BR>
> + ace_in32(ace, ACE_STATUS),<BR>
> + ace_in32(ace, ACE_MPULBA),<BR>
> + ace_in(ace, ACE_BUSMODE),<BR>
> + ace_in32(ace, ACE_ERROR),<BR>
> + ace_in32(ace, ACE_CFGLBA), ace_in(ace, ACE_FATSTAT));<BR>
> +}<BR>
> +<BR>
> +void ace_fix_driveid(struct hd_driveid *id)<BR>
> +{<BR>
> +#if defined(__BIG_ENDIAN)<BR>
> + u16 *buf = (void *)id;<BR>
> + int i;<BR>
> +<BR>
> + /* All half words have wrong byte order; swap the bytes */<BR>
> + for (i = 0; i < sizeof(struct hd_driveid); i += 2, buf++)<BR>
> + *buf = le16_to_cpu(*buf);<BR>
> +<BR>
> + /* Some of the data values are 32bit; swap the half words */<BR>
> + id->lba_capacity = ((id->lba_capacity >> 16) & 0x0000FFFF) |<BR>
> + ((id->lba_capacity << 16) & 0xFFFF0000);<BR>
> + id->spg = ((id->spg >> 16) & 0x0000FFFF) |<BR>
> + ((id->spg << 16) & 0xFFFF0000);<BR>
> +#endif<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Finite State Machine (FSM) implementation<BR>
> + */<BR>
> +<BR>
> +/* FSM tasks; used to direct state transitions */<BR>
> +#define ACE_TASK_IDLE 0<BR>
> +#define ACE_TASK_IDENTIFY 1<BR>
> +#define ACE_TASK_READ 2<BR>
> +#define ACE_TASK_WRITE 3<BR>
> +#define ACE_FSM_NUM_TASKS 4<BR>
> +<BR>
> +/* FSM state definitions */<BR>
> +#define ACE_FSM_STATE_IDLE 0<BR>
> +#define ACE_FSM_STATE_REQ_LOCK 1<BR>
> +#define ACE_FSM_STATE_WAIT_LOCK 2<BR>
> +#define ACE_FSM_STATE_WAIT_CFREADY 3<BR>
> +#define ACE_FSM_STATE_IDENTIFY_PREPARE 4<BR>
> +#define ACE_FSM_STATE_IDENTIFY_TRANSFER 5<BR>
> +#define ACE_FSM_STATE_IDENTIFY_COMPLETE 6<BR>
> +#define ACE_FSM_STATE_REQ_PREPARE 7<BR>
> +#define ACE_FSM_STATE_REQ_TRANSFER 8<BR>
> +#define ACE_FSM_STATE_REQ_COMPLETE 9<BR>
> +#define ACE_FSM_STATE_ERROR 10<BR>
> +#define ACE_FSM_NUM_STATES 11<BR>
> +<BR>
> +/* Set flag to exit FSM loop and reschedule tasklet */<BR>
> +static inline void ace_fsm_yield(struct ace_device *ace)<BR>
> +{<BR>
> + dev_dbg(ace->dev, "ace_fsm_yield()\n");<BR>
> + tasklet_schedule(&ace->fsm_tasklet);<BR>
> + ace->fsm_continue_flag = 0;<BR>
> +}<BR>
> +<BR>
> +/* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */<BR>
> +static inline void ace_fsm_yieldirq(struct ace_device *ace)<BR>
> +{<BR>
> + dev_dbg(ace->dev, "ace_fsm_yieldirq()\n");<BR>
> +<BR>
> + if (ace->irq == NO_IRQ)<BR>
> + /* No IRQ assigned, so need to poll */<BR>
> + tasklet_schedule(&ace->fsm_tasklet);<BR>
> + ace->fsm_continue_flag = 0;<BR>
> +}<BR>
> +<BR>
> +/* Get the next read/write request; ending requests that we don't handle<BR>
> */<BR>
> +struct request *ace_get_next_request(request_queue_t * q)<BR>
> +{<BR>
> + struct request *req;<BR>
> +<BR>
> + while ((req = elv_next_request(q)) != NULL) {<BR>
> + if (blk_fs_request(req))<BR>
> + break;<BR>
> + end_request(req, 0);<BR>
> + }<BR>
> + return req;<BR>
> +}<BR>
> +<BR>
> +static void ace_fsm_dostate(struct ace_device *ace)<BR>
> +{<BR>
> + struct request *req;<BR>
> + u32 status;<BR>
> + u16 val;<BR>
> + int count;<BR>
> + int i;<BR>
> +<BR>
> +#if defined(DEBUG)<BR>
> + dev_dbg(ace->dev, "fsm_state=%i, id_req_count=%i\n",<BR>
> + ace->fsm_state, ace->id_req_count);<BR>
> +#endif<BR>
> +<BR>
> + switch (ace->fsm_state) {<BR>
> + case ACE_FSM_STATE_IDLE:<BR>
> + /* See if there is anything to do */<BR>
> + if (ace->id_req_count ||<BR>
> ace_get_next_request(ace->queue)) {<BR>
> + ace->fsm_iter_num++;<BR>
> + ace->fsm_state = ACE_FSM_STATE_REQ_LOCK;<BR>
> + mod_timer(&ace->stall_timer, jiffies + HZ);<BR>
> + if<BR>
> (!timer_pending(&ace->stall_timer))<BR>
> + add_timer(&ace->stall_timer);<BR>
> + break;<BR>
> + }<BR>
> + del_timer(&ace->stall_timer);<BR>
> + ace->fsm_continue_flag = 0;<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_REQ_LOCK:<BR>
> + if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {<BR>
> + /* Already have the lock, jump to next state */<BR>
> + ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* Request the lock */<BR>
> + val = ace_in(ace, ACE_CTRL);<BR>
> + ace_out(ace, ACE_CTRL, val | ACE_CTRL_LOCKREQ);<BR>
> + ace->fsm_state = ACE_FSM_STATE_WAIT_LOCK;<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_WAIT_LOCK:<BR>
> + if (ace_in(ace, ACE_STATUS) & ACE_STATUS_MPULOCK) {<BR>
> + /* got the lock; move to next state */<BR>
> + ace->fsm_state = ACE_FSM_STATE_WAIT_CFREADY;<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* wait a bit for the lock */<BR>
> + ace_fsm_yield(ace);<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_WAIT_CFREADY:<BR>
> + status = ace_in32(ace, ACE_STATUS);<BR>
> + if (!(status & ACE_STATUS_RDYFORCFCMD) ||<BR>
> + (status & ACE_STATUS_CFBSY)) {<BR>
> + /* CF card isn't ready; it needs to be polled */<BR>
> + ace_fsm_yield(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* Device is ready for command; determine what to do next<BR>
> */<BR>
> + if (ace->id_req_count)<BR>
> + ace->fsm_state = ACE_FSM_STATE_IDENTIFY_PREPARE;<BR>
> + else<BR>
> + ace->fsm_state = ACE_FSM_STATE_REQ_PREPARE;<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_IDENTIFY_PREPARE:<BR>
> + /* Send identify command */<BR>
> + ace->fsm_task = ACE_TASK_IDENTIFY;<BR>
> + ace->data_ptr = &ace->cf_id;<BR>
> + ace->data_count = ACE_BUF_PER_SECTOR;<BR>
> + ace_out(ace, ACE_SECCNTCMD, ACE_SECCNTCMD_IDENTIFY);<BR>
> +<BR>
> + /* As per datasheet, put config controller in reset */<BR>
> + val = ace_in(ace, ACE_CTRL);<BR>
> + ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);<BR>
> +<BR>
> + /* irq handler takes over from this point; wait for the<BR>
> + * transfer to complete */<BR>
> + ace->fsm_state =<BR>
> ACE_FSM_STATE_IDENTIFY_TRANSFER;<BR>
> + ace_fsm_yieldirq(ace);<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_IDENTIFY_TRANSFER:<BR>
> + /* Check that the sysace is ready to receive data */<BR>
> + status = ace_in32(ace, ACE_STATUS);<BR>
> + if (status & ACE_STATUS_CFBSY) {<BR>
> + dev_dbg(ace->dev, "CFBSY set; t=%i iter=%i<BR>
> dc=%i\n",<BR>
> + ace->fsm_task, ace->fsm_iter_num,<BR>
> + ace->data_count);<BR>
> + ace_fsm_yield(ace);<BR>
> + break;<BR>
> + }<BR>
> + if (!(status & ACE_STATUS_DATABUFRDY)) {<BR>
> + ace_fsm_yield(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* Transfer the next buffer */<BR>
> + ace->reg_ops->datain(ace);<BR>
> + ace->data_count--;<BR>
> +<BR>
> + /* If there are still buffers to be transfers; jump out<BR>
> here */<BR>
> + if (ace->data_count != 0) {<BR>
> + ace_fsm_yieldirq(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* transfer finished; kick state machine */<BR>
> + dev_dbg(ace->dev, "identify finished\n");<BR>
> + ace->fsm_state =<BR>
> ACE_FSM_STATE_IDENTIFY_COMPLETE;<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_IDENTIFY_COMPLETE:<BR>
> + ace_fix_driveid(&ace->cf_id);<BR>
> + ace_dump_mem(&ace->cf_id, 512); /* Debug: Dump out disk ID<BR>
> */<BR>
> +<BR>
> + if (ace->data_result) {<BR>
> + /* Error occured, disable the disk */<BR>
> + ace->media_change = 1;<BR>
> + set_capacity(ace->gd, 0);<BR>
> + dev_err(ace->dev, "error fetching CF id (%i)\n",<BR>
> + ace->data_result);<BR>
> + } else {<BR>
> + ace->media_change = 0;<BR>
> +<BR>
> + /* Record disk parameters */<BR>
> + set_capacity(ace->gd, ace->cf_id.lba_capacity);<BR>
> + dev_info(ace->dev, "capacity: %i sectors\n",<BR>
> + ace->cf_id.lba_capacity);<BR>
> + }<BR>
> +<BR>
> + /* We're done, drop to IDLE state and notify waiters */<BR>
> + ace->fsm_state = ACE_FSM_STATE_IDLE;<BR>
> + ace->id_result = ace->data_result;<BR>
> + while (ace->id_req_count) {<BR>
> + complete(&ace->id_completion);<BR>
> + ace->id_req_count--;<BR>
> + }<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_REQ_PREPARE:<BR>
> + req = ace_get_next_request(ace->queue);<BR>
> + if (!req) {<BR>
> + ace->fsm_state = ACE_FSM_STATE_IDLE;<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* Okay, it's a data request, set it up for transfer */<BR>
> + dev_dbg(ace->dev,<BR>
> + "request: sec=%lx hcnt=%lx, ccnt=%x, dir=%i\n",<BR>
> + req->sector, req->hard_nr_sectors,<BR>
> + req->current_nr_sectors, rq_data_dir(req));<BR>
> +<BR>
> + ace->req = req;<BR>
> + ace->data_ptr = req->buffer;<BR>
> + ace->data_count = req->current_nr_sectors *<BR>
> ACE_BUF_PER_SECTOR;<BR>
> + ace_out32(ace, ACE_MPULBA, req->sector & 0x0FFFFFFF);<BR>
> +<BR>
> + count = req->hard_nr_sectors;<BR>
> + if (rq_data_dir(req)) {<BR>
> + /* Kick off write request */<BR>
> + dev_dbg(ace->dev, "write data\n");<BR>
> + ace->fsm_task = ACE_TASK_WRITE;<BR>
> + ace_out(ace, ACE_SECCNTCMD,<BR>
> + count | ACE_SECCNTCMD_WRITE_DATA);<BR>
> + } else {<BR>
> + /* Kick off read request */<BR>
> + dev_dbg(ace->dev, "read data\n");<BR>
> + ace->fsm_task = ACE_TASK_READ;<BR>
> + ace_out(ace, ACE_SECCNTCMD,<BR>
> + count | ACE_SECCNTCMD_READ_DATA);<BR>
> + }<BR>
> +<BR>
> + /* As per datasheet, put config controller in reset */<BR>
> + val = ace_in(ace, ACE_CTRL);<BR>
> + ace_out(ace, ACE_CTRL, val | ACE_CTRL_CFGRESET);<BR>
> +<BR>
> + /* Move to the transfer state. The systemace will raise<BR>
> + * an interrupt once there is something to do<BR>
> + */<BR>
> + ace->fsm_state = ACE_FSM_STATE_REQ_TRANSFER;<BR>
> + if (ace->fsm_task == ACE_TASK_READ)<BR>
> + ace_fsm_yieldirq(ace); /* wait for data ready */<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_REQ_TRANSFER:<BR>
> + /* Check that the sysace is ready to receive data */<BR>
> + status = ace_in32(ace, ACE_STATUS);<BR>
> + if (status & ACE_STATUS_CFBSY) {<BR>
> + dev_dbg(ace->dev,<BR>
> + "CFBSY set; t=%i iter=%i c=%i dc=%i<BR>
> irq=%i\n",<BR>
> + ace->fsm_task, ace->fsm_iter_num,<BR>
> + ace->req->current_nr_sectors * 16,<BR>
> + ace->data_count, ace->in_irq);<BR>
> + ace_fsm_yield(ace); /* need to poll CFBSY bit<BR>
> */<BR>
> + break;<BR>
> + }<BR>
> + if (!(status & ACE_STATUS_DATABUFRDY)) {<BR>
> + dev_dbg(ace->dev,<BR>
> + "DATABUF not set; t=%i iter=%i c=%i dc=%i<BR>
> irq=%i\n",<BR>
> + ace->fsm_task, ace->fsm_iter_num,<BR>
> + ace->req->current_nr_sectors * 16,<BR>
> + ace->data_count, ace->in_irq);<BR>
> + ace_fsm_yieldirq(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* Transfer the next buffer */<BR>
> + i = 16;<BR>
> + if (ace->fsm_task == ACE_TASK_WRITE)<BR>
> + ace->reg_ops->dataout(ace);<BR>
> + else<BR>
> + ace->reg_ops->datain(ace);<BR>
> + ace->data_count--;<BR>
> +<BR>
> + /* If there are still buffers to be transfers; jump out<BR>
> here */<BR>
> + if (ace->data_count != 0) {<BR>
> + ace_fsm_yieldirq(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + /* bio finished; is there another one? */<BR>
> + i = ace->req->current_nr_sectors;<BR>
> + if (end_that_request_first(ace->req, 1,<BR>
> i)) {<BR>
> + /* dev_dbg(ace->dev, "next block; h=%li c=%i\n",<BR>
> + * ace->req->hard_nr_sectors,<BR>
> + * ace->req->current_nr_sectors);<BR>
> + */<BR>
> + ace->data_ptr = ace->req->buffer;<BR>
> + ace->data_count = ace->req->current_nr_sectors *<BR>
> 16;<BR>
> + ace_fsm_yieldirq(ace);<BR>
> + break;<BR>
> + }<BR>
> +<BR>
> + ace->fsm_state = ACE_FSM_STATE_REQ_COMPLETE;<BR>
> + break;<BR>
> +<BR>
> + case ACE_FSM_STATE_REQ_COMPLETE:<BR>
> + /* Complete the block request */<BR>
> + blkdev_dequeue_request(ace->req);<BR>
> + end_that_request_last(ace->req, 1);<BR>
> + ace->req = NULL;<BR>
> +<BR>
> + /* Finished request; go to idle state */<BR>
> + ace->fsm_state = ACE_FSM_STATE_IDLE;<BR>
> + break;<BR>
> +<BR>
> + default:<BR>
> + ace->fsm_state = ACE_FSM_STATE_IDLE;<BR>
> + break;<BR>
> + }<BR>
> +}<BR>
> +<BR>
> +static void ace_fsm_tasklet(unsigned long data)<BR>
> +{<BR>
> + struct ace_device *ace = (void *)data;<BR>
> + unsigned long flags;<BR>
> +<BR>
> + spin_lock_irqsave(&ace->lock, flags);<BR>
> +<BR>
> + /* Loop over state machine until told to stop */<BR>
> + ace->fsm_continue_flag = 1;<BR>
> + while (ace->fsm_continue_flag)<BR>
> + ace_fsm_dostate(ace);<BR>
> +<BR>
> + spin_unlock_irqrestore(&ace->lock, flags);<BR>
> +}<BR>
> +<BR>
> +static void ace_stall_timer(unsigned long data)<BR>
> +{<BR>
> + struct ace_device *ace = (void *)data;<BR>
> + unsigned long flags;<BR>
> +<BR>
> + dev_warn(ace->dev,<BR>
> + "kicking stalled fsm; state=%i task=%i iter=%i dc=%i\n",<BR>
> + ace->fsm_state, ace->fsm_task, ace->fsm_iter_num,<BR>
> + ace->data_count);<BR>
> + spin_lock_irqsave(&ace->lock, flags);<BR>
> +<BR>
> + /* Rearm the stall timer *before* entering FSM (which may then<BR>
> + * delete the timer) */<BR>
> + mod_timer(&ace->stall_timer, jiffies + HZ);<BR>
> +<BR>
> + /* Loop over state machine until told to stop */<BR>
> + ace->fsm_continue_flag = 1;<BR>
> + while (ace->fsm_continue_flag)<BR>
> + ace_fsm_dostate(ace);<BR>
> +<BR>
> + spin_unlock_irqrestore(&ace->lock, flags);<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Interrupt handling routines<BR>
> + */<BR>
> +static int ace_interrupt_checkstate(struct ace_device *ace)<BR>
> +{<BR>
> + u32 sreg = ace_in32(ace, ACE_STATUS);<BR>
> + u16 creg = ace_in(ace, ACE_CTRL);<BR>
> +<BR>
> + /* Check for error occurance */<BR>
> + if ((sreg & (ACE_STATUS_CFGERROR | ACE_STATUS_CFCERROR)) &&<BR>
> + (creg & ACE_CTRL_ERRORIRQ)) {<BR>
> + dev_err(ace->dev, "transfer failure\n");<BR>
> + ace_dump_regs(ace);<BR>
> + return -EIO;<BR>
> + }<BR>
> +<BR>
> + return 0;<BR>
> +}<BR>
> +<BR>
> +static irqreturn_t ace_interrupt(int irq, void *dev_id)<BR>
> +{<BR>
> + u16 creg;<BR>
> + struct ace_device *ace = dev_id;<BR>
> +<BR>
> + /* be safe and get the lock */<BR>
> + spin_lock(&ace->lock);<BR>
> + ace->in_irq = 1;<BR>
> +<BR>
> + /* clear the interrupt */<BR>
> + creg = ace_in(ace, ACE_CTRL);<BR>
> + ace_out(ace, ACE_CTRL, creg | ACE_CTRL_RESETIRQ);<BR>
> + ace_out(ace, ACE_CTRL, creg);<BR>
> +<BR>
> + /* check for IO failures */<BR>
> + if (ace_interrupt_checkstate(ace))<BR>
> + ace->data_result = -EIO;<BR>
> +<BR>
> + if (ace->fsm_task == 0) {<BR>
> + dev_err(ace->dev,<BR>
> + "spurious irq; stat=%.8x ctrl=%.8x cmd=%.4x\n",<BR>
> + ace_in32(ace, ACE_STATUS), ace_in32(ace, ACE_CTRL),<BR>
> + ace_in(ace, ACE_SECCNTCMD));<BR>
> + dev_err(ace->dev, "fsm_task=%i fsm_state=%i<BR>
> data_count=%i\n",<BR>
> + ace->fsm_task, ace->fsm_state, ace->data_count);<BR>
> + }<BR>
> +<BR>
> + /* Loop over state machine until told to stop */<BR>
> + ace->fsm_continue_flag = 1;<BR>
> + while (ace->fsm_continue_flag)<BR>
> + ace_fsm_dostate(ace);<BR>
> +<BR>
> + /* done with interrupt; drop the lock */<BR>
> + ace->in_irq = 0;<BR>
> + spin_unlock(&ace->lock);<BR>
> +<BR>
> + return IRQ_HANDLED;<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Block ops<BR>
> + */<BR>
> +static void ace_request(request_queue_t * q)<BR>
> +{<BR>
> + struct request *req;<BR>
> + struct ace_device *ace;<BR>
> +<BR>
> + req = ace_get_next_request(q);<BR>
> +<BR>
> + if (req) {<BR>
> + ace = req->rq_disk->private_data;<BR>
> + tasklet_schedule(&ace->fsm_tasklet);<BR>
> + }<BR>
> +}<BR>
> +<BR>
> +static int ace_media_changed(struct gendisk *gd)<BR>
> +{<BR>
> + struct ace_device *ace = gd->private_data;<BR>
> + dev_dbg(ace->dev, "ace_media_changed(): %i\n", ace->media_change);<BR>
> +<BR>
> + return ace->media_change;<BR>
> +}<BR>
> +<BR>
> +static int ace_revalidate_disk(struct gendisk *gd)<BR>
> +{<BR>
> + struct ace_device *ace = gd->private_data;<BR>
> + unsigned long flags;<BR>
> +<BR>
> + dev_dbg(ace->dev, "ace_revalidate_disk()\n");<BR>
> +<BR>
> + if (ace->media_change) {<BR>
> + dev_dbg(ace->dev, "requesting cf id and scheduling<BR>
> tasklet\n");<BR>
> +<BR>
> + spin_lock_irqsave(&ace->lock, flags);<BR>
> + ace->id_req_count++;<BR>
> + spin_unlock_irqrestore(&ace->lock, flags);<BR>
> +<BR>
> + tasklet_schedule(&ace->fsm_tasklet);<BR>
> + wait_for_completion(&ace->id_completion);<BR>
> + }<BR>
> +<BR>
> + dev_dbg(ace->dev, "revalidate complete\n");<BR>
> + return ace->id_result;<BR>
> +}<BR>
> +<BR>
> +static int ace_open(struct inode *inode, struct file *filp)<BR>
> +{<BR>
> + struct ace_device *ace =<BR>
> inode->i_bdev->bd_disk->private_data;<BR>
> + unsigned long flags;<BR>
> +<BR>
> + dev_dbg(ace->dev, "ace_open() users=%i\n", ace->users + 1);<BR>
> +<BR>
> + filp->private_data = ace;<BR>
> + spin_lock_irqsave(&ace->lock, flags);<BR>
> + ace->users++;<BR>
> + spin_unlock_irqrestore(&ace->lock, flags);<BR>
> +<BR>
> + check_disk_change(inode->i_bdev);<BR>
> + return 0;<BR>
> +}<BR>
> +<BR>
> +static int ace_release(struct inode *inode, struct file *filp)<BR>
> +{<BR>
> + struct ace_device *ace =<BR>
> inode->i_bdev->bd_disk->private_data;<BR>
> + unsigned long flags;<BR>
> + u16 val;<BR>
> +<BR>
> + dev_dbg(ace->dev, "ace_release() users=%i\n", ace->users - 1);<BR>
> +<BR>
> + spin_lock_irqsave(&ace->lock, flags);<BR>
> + ace->users--;<BR>
> + if (ace->users == 0) {<BR>
> + val = ace_in(ace, ACE_CTRL);<BR>
> + ace_out(ace, ACE_CTRL, val & ~ACE_CTRL_LOCKREQ);<BR>
> + }<BR>
> + spin_unlock_irqrestore(&ace->lock, flags);<BR>
> + return 0;<BR>
> +}<BR>
> +<BR>
> +static int ace_ioctl(struct inode *inode, struct file *filp,<BR>
> + unsigned int cmd, unsigned long arg)<BR>
> +{<BR>
> + struct ace_device *ace =<BR>
> inode->i_bdev->bd_disk->private_data;<BR>
> + struct hd_geometry __user *geo = (struct hd_geometry __user *)arg;<BR>
> + struct hd_geometry g;<BR>
> + dev_dbg(ace->dev, "ace_ioctl()\n");<BR>
> +<BR>
> + switch (cmd) {<BR>
> + case HDIO_GETGEO:<BR>
> + g.heads = ace->cf_id.heads;<BR>
> + g.sectors = ace->cf_id.sectors;<BR>
> + g.cylinders = ace->cf_id.cyls;<BR>
> + g.start = 0;<BR>
> + return copy_to_user(geo, &g, sizeof(g)) ? -EFAULT : 0;<BR>
> +<BR>
> + default:<BR>
> + return -ENOTTY;<BR>
> + }<BR>
> + return -ENOTTY;<BR>
> +}<BR>
> +<BR>
> +static struct block_device_operations ace_fops = {<BR>
> + .owner = THIS_MODULE,<BR>
> + .open = ace_open,<BR>
> + .release = ace_release,<BR>
> + .media_changed = ace_media_changed,<BR>
> + .revalidate_disk = ace_revalidate_disk,<BR>
> + .ioctl = ace_ioctl,<BR>
> +};<BR>
> +<BR>
> +/*<BR>
> --------------------------------------------------------------------<BR>
> + * SystemACE device setup/teardown code<BR>
> + */<BR>
> +static int __devinit ace_setup(struct ace_device *ace)<BR>
> +{<BR>
> + u16 version;<BR>
> + u16 val;<BR>
> +<BR>
> + int rc;<BR>
> +<BR>
> + spin_lock_init(&ace->lock);<BR>
> + init_completion(&ace->id_completion);<BR>
> +<BR>
> + /*<BR>
> + * Map the device<BR>
> + */<BR>
> + ace->baseaddr = ioremap(ace->physaddr, 0x80);<BR>
> + if (!ace->baseaddr)<BR>
> + goto err_ioremap;<BR>
> +<BR>
> + if (ace->irq != NO_IRQ) {<BR>
> + rc = request_irq(ace->irq, ace_interrupt, 0, "systemace",<BR>
> ace);<BR>
> + if (rc) {<BR>
> + /* Failure - fall back to polled mode */<BR>
> + dev_err(ace->dev, "request_irq failed\n");<BR>
> + ace->irq = NO_IRQ;<BR>
> + }<BR>
> + }<BR>
> +<BR>
> + /*<BR>
> + * Initialize the state machine tasklet and stall timer<BR>
> + */<BR>
> + tasklet_init(&ace->fsm_tasklet, ace_fsm_tasklet, (unsigned<BR>
> long)ace);<BR>
> + setup_timer(&ace->stall_timer, ace_stall_timer, (unsigned<BR>
> long)ace);<BR>
> +<BR>
> + /*<BR>
> + * Initialize the request queue<BR>
> + */<BR>
> + ace->queue = blk_init_queue(ace_request, &ace->lock);<BR>
> + if (ace->queue == NULL)<BR>
> + goto err_blk_initq;<BR>
> + blk_queue_hardsect_size(ace->queue, 512);<BR>
> +<BR>
> + /*<BR>
> + * Allocate and initialize GD structure<BR>
> + */<BR>
> + ace->gd = alloc_disk(ACE_NUM_MINORS);<BR>
> + if (!ace->gd)<BR>
> + goto err_alloc_disk;<BR>
> +<BR>
> + ace->gd->major = ace_major;<BR>
> + ace->gd->first_minor = ace->id * ACE_NUM_MINORS;<BR>
> + ace->gd->fops = &ace_fops;<BR>
> + ace->gd->queue = ace->queue;<BR>
> + ace->gd->private_data = ace;<BR>
> + snprintf(ace->gd->disk_name, 32, "xs%c", ace->id + 'a');<BR>
> + device_rename(ace->dev, ace->gd->disk_name);<BR>
> +<BR>
> + /* set bus width */<BR>
> + if (ace->bus_width == 1) {<BR>
> + /* 0x0101 should work regardless of endianess */<BR>
> + ace_out_le16(ace, ACE_BUSMODE, 0x0101);<BR>
> +<BR>
> + /* read it back to determine endianess */<BR>
> + if (ace_in_le16(ace, ACE_BUSMODE) == 0x0001)<BR>
> + ace->reg_ops = &ace_reg_le16_ops;<BR>
> + else<BR>
> + ace->reg_ops = &ace_reg_be16_ops;<BR>
> + } else {<BR>
> + ace_out_8(ace, ACE_BUSMODE, 0x00);<BR>
> + ace->reg_ops = &ace_reg_8_ops;<BR>
> + }<BR>
> +<BR>
> + /* Make sure version register is sane */<BR>
> + version = ace_in(ace, ACE_VERSION);<BR>
> + if ((version == 0) || (version == 0xFFFF))<BR>
> + goto err_read;<BR>
> +<BR>
> + /* Put sysace in a sane state by clearing most control reg bits */<BR>
> + ace_out(ace, ACE_CTRL, ACE_CTRL_FORCECFGMODE |<BR>
> + ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ);<BR>
> +<BR>
> + /* Enable interrupts */<BR>
> + val = ace_in(ace, ACE_CTRL);<BR>
> + val |= ACE_CTRL_DATABUFRDYIRQ | ACE_CTRL_ERRORIRQ;<BR>
> + ace_out(ace, ACE_CTRL, val);<BR>
> +<BR>
> + /* Print the identification */<BR>
> + dev_info(ace->dev, "Xilinx SystemACE revision %i.%i.%i\n",<BR>
> + (version >> 12) & 0xf, (version >> 8) & 0x0f, version &<BR>
> 0xff);<BR>
> + dev_dbg(ace->dev, "physaddr 0x%lx, mapped to 0x%p, irq=%i\n",<BR>
> + ace->physaddr, ace->baseaddr, ace->irq);<BR>
> +<BR>
> + ace->media_change = 1;<BR>
> + ace_revalidate_disk(ace->gd);<BR>
> +<BR>
> + /* Make the sysace device 'live' */<BR>
> + add_disk(ace->gd);<BR>
> +<BR>
> + return 0;<BR>
> +<BR>
> + err_read:<BR>
> + put_disk(ace->gd);<BR>
> + err_alloc_disk:<BR>
> + blk_cleanup_queue(ace->queue);<BR>
> + err_blk_initq:<BR>
> + iounmap(ace->baseaddr);<BR>
> + if (ace->irq != NO_IRQ)<BR>
> + free_irq(ace->irq, ace);<BR>
> + err_ioremap:<BR>
> + printk(KERN_INFO "xsysace: error initializing device at 0x%lx\n",<BR>
> + ace->physaddr);<BR>
> + return -ENOMEM;<BR>
> +}<BR>
> +<BR>
> +static void __devexit ace_teardown(struct ace_device *ace)<BR>
> +{<BR>
> + if (ace->gd) {<BR>
> + del_gendisk(ace->gd);<BR>
> + put_disk(ace->gd);<BR>
> + }<BR>
> +<BR>
> + if (ace->queue)<BR>
> + blk_cleanup_queue(ace->queue);<BR>
> +<BR>
> + tasklet_kill(&ace->fsm_tasklet);<BR>
> +<BR>
> + if (ace->irq != NO_IRQ)<BR>
> + free_irq(ace->irq, ace);<BR>
> +<BR>
> + iounmap(ace->baseaddr);<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Platform Bus Support<BR>
> + */<BR>
> +<BR>
> +static int __devinit ace_probe(struct device *device)<BR>
> +{<BR>
> + struct platform_device *dev = to_platform_device(device);<BR>
> + struct ace_device *ace;<BR>
> + int i;<BR>
> +<BR>
> + dev_dbg(device, "ace_probe(%p)\n", device);<BR>
> +<BR>
> + /*<BR>
> + * Allocate the ace device structure<BR>
> + */<BR>
> + ace = kzalloc(sizeof(struct ace_device), GFP_KERNEL);<BR>
> + if (!ace)<BR>
> + goto err_alloc;<BR>
> +<BR>
> + ace->dev = device;<BR>
> + ace->id = dev->id;<BR>
> + ace->irq = NO_IRQ;<BR>
> +<BR>
> + for (i = 0; i < dev->num_resources; i++) {<BR>
> + if (dev->resource[i].flags & IORESOURCE_MEM)<BR>
> + ace->physaddr = dev->resource[i].start;<BR>
> + if (dev->resource[i].flags & IORESOURCE_IRQ)<BR>
> + ace->irq = dev->resource[i].start;<BR>
> + }<BR>
> +<BR>
> + /* FIXME: Should get bus_width from the platform_device struct */<BR>
> + ace->bus_width = 1;<BR>
> +<BR>
> + dev_set_drvdata(&dev->dev, ace);<BR>
> +<BR>
> + /* Call the bus-independant setup code */<BR>
> + if (ace_setup(ace) != 0)<BR>
> + goto err_setup;<BR>
> +<BR>
> + return 0;<BR>
> +<BR>
> + err_setup:<BR>
> + dev_set_drvdata(&dev->dev, NULL);<BR>
> + kfree(ace);<BR>
> + err_alloc:<BR>
> + printk(KERN_ERR "xsysace: could not initialize device\n");<BR>
> + return -ENOMEM;<BR>
> +}<BR>
> +<BR>
> +/*<BR>
> + * Platform bus remove() method<BR>
> + */<BR>
> +static int __devexit ace_remove(struct device *device)<BR>
> +{<BR>
> + struct ace_device *ace = dev_get_drvdata(device);<BR>
> +<BR>
> + dev_dbg(device, "ace_remove(%p)\n", device);<BR>
> +<BR>
> + if (ace) {<BR>
> + ace_teardown(ace);<BR>
> + kfree(ace);<BR>
> + }<BR>
> +<BR>
> + return 0;<BR>
> +}<BR>
> +<BR>
> +static struct device_driver ace_driver = {<BR>
> + .name = "xsysace",<BR>
> + .bus = &platform_bus_type,<BR>
> + .probe = ace_probe,<BR>
> + .remove = __devexit_p(ace_remove),<BR>
> +};<BR>
> +<BR>
> +/*<BR>
> ---------------------------------------------------------------------<BR>
> + * Module init/exit routines<BR>
> + */<BR>
> +static int __init ace_init(void)<BR>
> +{<BR>
> + ace_major = register_blkdev(ace_major, "xsysace");<BR>
> + if (ace_major <= 0) {<BR>
> + printk(KERN_WARNING "xsysace: register_blkdev() failed\n");<BR>
> + return ace_major;<BR>
> + }<BR>
> +<BR>
> + pr_debug("Registering Xilinx SystemACE driver, major=%i\n",<BR>
> ace_major);<BR>
> + return driver_register(&ace_driver);<BR>
> +}<BR>
> +<BR>
> +static void __exit ace_exit(void)<BR>
> +{<BR>
> + pr_debug("Unregistering Xilinx SystemACE driver\n");<BR>
> + driver_unregister(&ace_driver);<BR>
> + if (unregister_blkdev(ace_major, "xsysace"))<BR>
> + printk(KERN_WARNING "systemace unregister_blkdev(%i)<BR>
> failed\n",<BR>
> + ace_major);<BR>
> +}<BR>
> +<BR>
> +module_init(ace_init);<BR>
> +module_exit(ace_exit);<BR>
><BR>
><BR>
> CONFIDENTIALITY<BR>
> This e-mail message and any attachments thereto, is intended only for use<BR>
> by the addressee(s) named herein and may contain legally privileged and/or<BR>
> confidential information. If you are not the intended recipient of this<BR>
> e-mail message, you are hereby notified that any dissemination, distribution<BR>
> or copying of this e-mail message, and any attachments thereto, is strictly<BR>
> prohibited. If you have received this e-mail message in error, please<BR>
> immediately notify the sender and permanently delete the original and any<BR>
> copies of this email and any prints thereof.<BR>
> ABSENT AN EXPRESS STATEMENT TO THE CONTRARY HEREINABOVE, THIS E-MAIL IS NOT<BR>
> INTENDED AS A SUBSTITUTE FOR A WRITING. Notwithstanding the Uniform<BR>
> Electronic Transactions Act or the applicability of any other law of similar<BR>
> substance and effect, absent an express statement to the contrary<BR>
> hereinabove, this e-mail message its contents, and any attachments hereto<BR>
> are not intended to represent an offer or acceptance to enter into a<BR>
> contract and are not otherwise intended to bind the sender, Sanmina-SCI<BR>
> Corporation (or any of its subsidiaries), or any other person or entity.<BR>
><BR>
<BR>
<BR>
--<BR>
Grant Likely, B.Sc., P.Eng.<BR>
Secret Lab Technologies Ltd.<BR>
grant.likely@secretlab.ca<BR>
(403) 399-0195<BR>
<BR>
</FONT>
</P>
<BR>
CONFIDENTIALITY<BR>
This e-mail message and any attachments thereto, is intended only for use by the addressee(s) named herein and may contain legally privileged and/or confidential information. If you are not the intended recipient of this e-mail message, you are hereby notified that any dissemination, distribution or copying of this e-mail message, and any attachments thereto, is strictly prohibited. If you have received this e-mail message in error, please immediately notify the sender and permanently delete the original and any copies of this email and any prints thereof.<BR>
ABSENT AN EXPRESS STATEMENT TO THE CONTRARY HEREINABOVE, THIS E-MAIL IS NOT INTENDED AS A SUBSTITUTE FOR A WRITING. Notwithstanding the Uniform Electronic Transactions Act or the applicability of any other law of similar substance and effect, absent an express statement to the contrary hereinabove, this e-mail message its contents, and any attachments hereto are not intended to represent an offer or acceptance to enter into a contract and are not otherwise intended to bind the sender, Sanmina-SCI Corporation (or any of its subsidiaries), or any other person or entity.<BR>
</BODY>
</HTML>