xsysace.c

来自「linux 内核源代码」· C语言 代码 · 共 1,288 行 · 第 1/3 页

C
1,288
字号
/* * 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/hdreg.h>#include <linux/platform_device.h>#if defined(CONFIG_OF)#include <linux/of_device.h>#include <linux/of_platform.h>#endifMODULE_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 1struct 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 */	unsigned long 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 */	struct hd_driveid cf_id;};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){}#endifstatic void ace_dump_regs(struct ace_device *ace){	dev_info(ace->dev, "    ctrl:  %.8x  seccnt/cmd: %.4x      ver:%.4x\n"		 KERN_INFO "    status:%.8x  mpu_lba:%.8x  busmode:%4x\n"		 KERN_INFO "    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));}void ace_fix_driveid(struct hd_driveid *id){#if defined(__BIG_ENDIAN)	u16 *buf = (void *)id;	int i;	/* All half words have wrong byte order; swap the bytes */	for (i = 0; i < sizeof(struct hd_driveid); i += 2, buf++)		*buf = le16_to_cpu(*buf);	/* Some of the data values are 32bit; swap the half words  */	id->lba_capacity = ((id->lba_capacity >> 16) & 0x0000FFFF) |	    ((id->lba_capacity << 16) & 0xFFFF0000);	id->spg = ((id->spg >> 16) & 0x0000FFFF) |	    ((id->spg << 16) & 0xFFFF0000);#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

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?