/*
* 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>
#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_*) */