1246 lines · 32.6 KB · C
RawDownload
/* * Xilinx SystemACE device driver * * Copyright 2007 Secret Lab Technologies Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. */ /* * The SystemACE chip is designed to configure FPGAs by loading an FPGA * bitstream from a file on a CF card and squirting it into FPGAs connected * to the SystemACE JTAG chain.  It also has the advantage of providing an * MPU interface which can be used to control the FPGA configuration process * and to use the attached CF card for general purpose storage. * * This driver is a block device driver for the SystemACE. * * Initialization: *    The driver registers itself as a platform_device driver at module *    load time.  The platform bus will take care of calling the *    ace_probe() method for all SystemACE instances in the system.  Any *    number of SystemACE instances are supported.  ace_probe() calls *    ace_setup() which initialized all data structures, reads the CF *    id structure and registers the device. * * Processing: *    Just about all of the heavy lifting in this driver is performed by *    a Finite State Machine (FSM).  The driver needs to wait on a number *    of events; some raised by interrupts, some which need to be polled *    for.  Describing all of the behaviour in a FSM seems to be the *    easiest way to keep the complexity low and make it easy to *    understand what the driver is doing.  If the block ops or the *    request function need to interact with the hardware, then they *    simply need to flag the request and kick of FSM processing. * *    The FSM itself is atomic-safe code which can be run from any *    context.  The general process flow is: *    1. obtain the ace->lock spinlock. *    2. loop on ace_fsm_dostate() until the ace->fsm_continue flag is *       cleared. *    3. release the lock. * *    Individual states do not sleep in any way.  If a condition needs to *    be waited for then the state much clear the fsm_continue flag and *    either schedule the FSM to be run again at a later time, or expect *    an interrupt to call the FSM when the desired condition is met. * *    In normal operation, the FSM is processed at interrupt context *    either when the driver's tasklet is scheduled, or when an irq is *    raised by the hardware.  The tasklet can be scheduled at any time. *    The request method in particular schedules the tasklet when a new *    request has been indicated by the block layer.  Once started, the *    FSM proceeds as far as it can processing the request until it *    needs on a hardware event.  At this point, it must yield execution. * *    A state has two options when yielding execution: *    1. ace_fsm_yield() *       - Call if need to poll for event. *       - clears the fsm_continue flag to exit the processing loop *       - reschedules the tasklet to run again as soon as possible *    2. ace_fsm_yieldirq() *       - Call if an irq is expected from the HW *       - clears the fsm_continue flag to exit the processing loop *       - does not reschedule the tasklet so the FSM will not be processed *         again until an irq is received. *    After calling a yield function, the state must return control back *    to the FSM main loop. * *    Additionally, the driver maintains a kernel timer which can process *    the FSM.  If the FSM gets stalled, typically due to a missed *    interrupt, then the kernel timer will expire and the driver can *    continue where it left off. * * To Do: *    - Add FPGA configuration control interface. *    - Request major number from lanana */ #undef DEBUG #include <linux/module.h>#include <linux/ctype.h>#include <linux/init.h>#include <linux/interrupt.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/slab.h>#include <linux/blkdev.h>#include <linux/mutex.h>#include <linux/ata.h>#include <linux/hdreg.h>#include <linux/platform_device.h>#if defined(CONFIG_OF)#include <linux/of_address.h>#include <linux/of_device.h>#include <linux/of_platform.h>#endif MODULE_AUTHOR("Grant Likely <grant.likely@secretlab.ca>");MODULE_DESCRIPTION("Xilinx SystemACE device driver");MODULE_LICENSE("GPL"); /* SystemACE register definitions */#define ACE_BUSMODE (0x00) #define ACE_STATUS (0x04)#define ACE_STATUS_CFGLOCK      (0x00000001)#define ACE_STATUS_MPULOCK      (0x00000002)#define ACE_STATUS_CFGERROR     (0x00000004)	/* config controller error */#define ACE_STATUS_CFCERROR     (0x00000008)	/* CF controller error */#define ACE_STATUS_CFDETECT     (0x00000010)#define ACE_STATUS_DATABUFRDY   (0x00000020)#define ACE_STATUS_DATABUFMODE  (0x00000040)#define ACE_STATUS_CFGDONE      (0x00000080)#define ACE_STATUS_RDYFORCFCMD  (0x00000100)#define ACE_STATUS_CFGMODEPIN   (0x00000200)#define ACE_STATUS_CFGADDR_MASK (0x0000e000)#define ACE_STATUS_CFBSY        (0x00020000)#define ACE_STATUS_CFRDY        (0x00040000)#define ACE_STATUS_CFDWF        (0x00080000)#define ACE_STATUS_CFDSC        (0x00100000)#define ACE_STATUS_CFDRQ        (0x00200000)#define ACE_STATUS_CFCORR       (0x00400000)#define ACE_STATUS_CFERR        (0x00800000) #define ACE_ERROR (0x08)#define ACE_CFGLBA (0x0c)#define ACE_MPULBA (0x10) #define ACE_SECCNTCMD (0x14)#define ACE_SECCNTCMD_RESET      (0x0100)#define ACE_SECCNTCMD_IDENTIFY   (0x0200)#define ACE_SECCNTCMD_READ_DATA  (0x0300)#define ACE_SECCNTCMD_WRITE_DATA (0x0400)#define ACE_SECCNTCMD_ABORT      (0x0600) #define ACE_VERSION (0x16)#define ACE_VERSION_REVISION_MASK (0x00FF)#define ACE_VERSION_MINOR_MASK    (0x0F00)#define ACE_VERSION_MAJOR_MASK    (0xF000) #define ACE_CTRL (0x18)#define ACE_CTRL_FORCELOCKREQ   (0x0001)#define ACE_CTRL_LOCKREQ        (0x0002)#define ACE_CTRL_FORCECFGADDR   (0x0004)#define ACE_CTRL_FORCECFGMODE   (0x0008)#define ACE_CTRL_CFGMODE        (0x0010)#define ACE_CTRL_CFGSTART       (0x0020)#define ACE_CTRL_CFGSEL         (0x0040)#define ACE_CTRL_CFGRESET       (0x0080)#define ACE_CTRL_DATABUFRDYIRQ  (0x0100)#define ACE_CTRL_ERRORIRQ       (0x0200)#define ACE_CTRL_CFGDONEIRQ     (0x0400)#define ACE_CTRL_RESETIRQ       (0x0800)#define ACE_CTRL_CFGPROG        (0x1000)#define ACE_CTRL_CFGADDR_MASK   (0xe000) #define ACE_FATSTAT (0x1c) #define ACE_NUM_MINORS 16#define ACE_SECTOR_SIZE (512)#define ACE_FIFO_SIZE (32)#define ACE_BUF_PER_SECTOR (ACE_SECTOR_SIZE / ACE_FIFO_SIZE) #define ACE_BUS_WIDTH_8  0#define ACE_BUS_WIDTH_16 1 struct ace_reg_ops; struct ace_device {	/* driver state data */	int id;	int media_change;	int users;	struct list_head list; 	/* finite state machine data */	struct tasklet_struct fsm_tasklet;	uint fsm_task;		/* Current activity (ACE_TASK_*) */	uint fsm_state;		/* Current state (ACE_FSM_STATE_*) */	uint fsm_continue_flag;	/* cleared to exit FSM mainloop */	uint fsm_iter_num;	struct timer_list stall_timer; 	/* Transfer state/result, use for both id and block request */	struct request *req;	/* request being processed */	void *data_ptr;		/* pointer to I/O buffer */	int data_count;		/* number of buffers remaining */	int data_result;	/* Result of transfer; 0 := success */ 	int id_req_count;	/* count of id requests */	int id_result;	struct completion id_completion;	/* used when id req finishes */	int in_irq; 	/* Details of hardware device */	resource_size_t physaddr;	void __iomem *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 */	spinlock_t lock;	struct device *dev;	struct request_queue *queue;	struct gendisk *gd; 	/* Inserted CF card parameters */	u16 cf_id[ATA_ID_WORDS];}; static DEFINE_MUTEX(xsysace_mutex);static int ace_major; /* --------------------------------------------------------------------- * Low level register access */ struct ace_reg_ops {	u16(*in) (struct ace_device * ace, int reg);	void (*out) (struct ace_device * ace, int reg, u16 val);	void (*datain) (struct ace_device * ace);	void (*dataout) (struct ace_device * ace);}; /* 8 Bit bus width */static u16 ace_in_8(struct ace_device *ace, int reg){	void __iomem *r = ace->baseaddr + reg;	return in_8(r) | (in_8(r + 1) << 8);} static void ace_out_8(struct ace_device *ace, int reg, u16 val){	void __iomem *r = ace->baseaddr + reg;	out_8(r, val);	out_8(r + 1, val >> 8);} static void ace_datain_8(struct ace_device *ace){	void __iomem *r = ace->baseaddr + 0x40;	u8 *dst = ace->data_ptr;	int i = ACE_FIFO_SIZE;	while (i--)		*dst++ = in_8(r++);	ace->data_ptr = dst;} static void ace_dataout_8(struct ace_device *ace){	void __iomem *r = ace->baseaddr + 0x40;	u8 *src = ace->data_ptr;	int i = ACE_FIFO_SIZE;	while (i--)		out_8(r++, *src++);	ace->data_ptr = src;} static struct ace_reg_ops ace_reg_8_ops = {	.in = ace_in_8,	.out = ace_out_8,	.datain = ace_datain_8,	.dataout = ace_dataout_8,}; /* 16 bit big endian bus attachment */static u16 ace_in_be16(struct ace_device *ace, int reg){	return in_be16(ace->baseaddr + reg);} static void ace_out_be16(struct ace_device *ace, int reg, u16 val){	out_be16(ace->baseaddr + reg, val);} static void ace_datain_be16(struct ace_device *ace){	int i = ACE_FIFO_SIZE / 2;	u16 *dst = ace->data_ptr;	while (i--)		*dst++ = in_le16(ace->baseaddr + 0x40);	ace->data_ptr = dst;} static void ace_dataout_be16(struct ace_device *ace){	int i = ACE_FIFO_SIZE / 2;	u16 *src = ace->data_ptr;	while (i--)		out_le16(ace->baseaddr + 0x40, *src++);	ace->data_ptr = src;} /* 16 bit little endian bus attachment */static u16 ace_in_le16(struct ace_device *ace, int reg){	return in_le16(ace->baseaddr + reg);} static void ace_out_le16(struct ace_device *ace, int reg, u16 val){	out_le16(ace->baseaddr + reg, val);} static void ace_datain_le16(struct ace_device *ace){	int i = ACE_FIFO_SIZE / 2;	u16 *dst = ace->data_ptr;	while (i--)		*dst++ = in_be16(ace->baseaddr + 0x40);	ace->data_ptr = dst;} static void ace_dataout_le16(struct ace_device *ace){	int i = ACE_FIFO_SIZE / 2;	u16 *src = ace->data_ptr;	while (i--)		out_be16(ace->baseaddr + 0x40, *src++);	ace->data_ptr = src;} static struct ace_reg_ops ace_reg_be16_ops = {	.in = ace_in_be16,	.out = ace_out_be16,	.datain = ace_datain_be16,	.dataout = ace_dataout_be16,}; static struct ace_reg_ops ace_reg_le16_ops = {	.in = ace_in_le16,	.out = ace_out_le16,	.datain = ace_datain_le16,	.dataout = ace_dataout_le16,}; static inline u16 ace_in(struct ace_device *ace, int reg){	return ace->reg_ops->in(ace, reg);} static inline u32 ace_in32(struct ace_device *ace, int reg){	return ace_in(ace, reg) | (ace_in(ace, reg + 2) << 16);} static inline void ace_out(struct ace_device *ace, int reg, u16 val){	ace->reg_ops->out(ace, reg, val);} static inline void ace_out32(struct ace_device *ace, int reg, u32 val){	ace_out(ace, reg, val);	ace_out(ace, reg + 2, val >> 16);} /* --------------------------------------------------------------------- * Debug support functions */ #if defined(DEBUG)static void ace_dump_mem(void *base, int len){	const char *ptr = base;	int i, j; 	for (i = 0; i < len; i += 16) {		printk(KERN_INFO "%.8x:", i);		for (j = 0; j < 16; j++) {			if (!(j % 4))				printk(" ");			printk("%.2x", ptr[i + j]);		}		printk(" ");		for (j = 0; j < 16; j++)			printk("%c", isprint(ptr[i + j]) ? ptr[i + j] : '.');		printk("\n");	}}#elsestatic inline void ace_dump_mem(void *base, int len){}#endif static void ace_dump_regs(struct ace_device *ace){	dev_info(ace->dev,		 "    ctrl:  %.8x  seccnt/cmd: %.4x      ver:%.4x\n"		 "    status:%.8x  mpu_lba:%.8x  busmode:%4x\n"		 "    error: %.8x  cfg_lba:%.8x  fatstat:%.4x\n",		 ace_in32(ace, ACE_CTRL),		 ace_in(ace, ACE_SECCNTCMD),		 ace_in(ace, ACE_VERSION),		 ace_in32(ace, ACE_STATUS),		 ace_in32(ace, ACE_MPULBA),		 ace_in(ace, ACE_BUSMODE),		 ace_in32(ace, ACE_ERROR),		 ace_in32(ace, ACE_CFGLBA), ace_in(ace, ACE_FATSTAT));} static void ace_fix_driveid(u16 *id){#if defined(__BIG_ENDIAN)	int i; 	/* All half words have wrong byte order; swap the bytes */	for (i = 0; i < ATA_ID_WORDS; i++, id++)		*id = le16_to_cpu(*id);#endif} /* --------------------------------------------------------------------- * Finite State Machine (FSM) implementation */ /* FSM tasks; used to direct state transitions */#define ACE_TASK_IDLE      0#define ACE_TASK_IDENTIFY  1#define ACE_TASK_READ      2#define ACE_TASK_WRITE     3#define ACE_FSM_NUM_TASKS  4 /* FSM state definitions */#define ACE_FSM_STATE_IDLE               0#define ACE_FSM_STATE_REQ_LOCK           1#define ACE_FSM_STATE_WAIT_LOCK          2#define ACE_FSM_STATE_WAIT_CFREADY       3#define ACE_FSM_STATE_IDENTIFY_PREPARE   4#define ACE_FSM_STATE_IDENTIFY_TRANSFER  5#define ACE_FSM_STATE_IDENTIFY_COMPLETE  6#define ACE_FSM_STATE_REQ_PREPARE        7#define ACE_FSM_STATE_REQ_TRANSFER       8#define ACE_FSM_STATE_REQ_COMPLETE       9#define ACE_FSM_STATE_ERROR             10#define ACE_FSM_NUM_STATES              11 /* Set flag to exit FSM loop and reschedule tasklet */static inline void ace_fsm_yield(struct ace_device *ace){	dev_dbg(ace->dev, "ace_fsm_yield()\n");	tasklet_schedule(&ace->fsm_tasklet);	ace->fsm_continue_flag = 0;} /* Set flag to exit FSM loop and wait for IRQ to reschedule tasklet */static inline void ace_fsm_yieldirq(struct ace_device *ace){	dev_dbg(ace->dev, "ace_fsm_yieldirq()\n"); 	if (!ace->irq)		/* No IRQ assigned, so need to poll */		tasklet_schedule(&ace->fsm_tasklet);	ace->fsm_continue_flag = 0;} /* Get the next read/write request; ending requests that we don't handle */static struct request *ace_get_next_request(struct request_queue *q){	struct request *req; 	while ((req = blk_peek_request(q)) != NULL) {		if (req->cmd_type == REQ_TYPE_FS)			break;		blk_start_request(req);		__blk_end_request_all(req, -EIO);	}	return req;} static void ace_fsm_dostate(struct ace_device *ace){	struct request *req;	u32 status;	u16 val;	int count; #if defined(DEBUG)	dev_dbg(ace->dev, "fsm_state=%i, id_req_count=%i\n",		ace->fsm_state, ace->id_req_count);#endif 	/* Verify that there is actually a CF in the slot. If not, then	 * bail out back to the idle state and wake up all the waiters */	status = ace_in32(ace, ACE_STATUS);	if ((status & ACE_STATUS_CFDETECT) == 0) {		ace->fsm_state = ACE_FSM_STATE_IDLE;		ace->media_change = 1;		set_capacity(ace->gd, 0);		dev_info(ace->dev, "No CF in slot\n"); 		/* Drop all in-flight and pending requests */

Lines 1–500 of 1246.