Monday, July 28, 2008

blackfin spi device driver



/********************** ADAM **************************************/

The SPI master is registered as a platform devices, while SPI slave devices are registered
as devices in the SPI bus.

SPI Bus
~~~~~~~
spi.c: spi_init()
status = bus_register(&spi_bus_type);
status = class_register(&spi_master_class);

root:/sys/bus/spi> ls -lR
.:
drwxr-xr-x 2 root root 0 Jan 1 01:47 devices
drwxr-xr-x 3 root root 0 Jan 1 2007 drivers
-rw-r--r-- 1 root root 4096 Jan 1 01:47 drivers_autoprobe
--w------- 1 root root 4096 Jan 1 01:47 drivers_probe

./devices:
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../devices/platform/bfin-spi.0/spi0.1

./drivers:
drwxr-xr-x 2 root root 0 Jan 1 01:47 spidev

./drivers/spidev:
--w------- 1 root root 4096 Jan 1 01:47 bind
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../../devices/platform/bfin-spi.0/spi0.1
--w------- 1 root root 4096 Jan 1 01:47 unbind



Setup the master (SPI controller)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The SPI master device is registered as a platform device:
// stamp.c:

struct platform_device {
const char *name;
u32 id;
struct device dev;
u32 num_resources;
struct resource *resource;
};

//---------------------------------------------------------------
stamp.c:

static struct resource bfin_spi0_resource[] = {
[0] = {
.start = SPI0_REGBASE,
.end = SPI0_REGBASE + 0xFF,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = CH_SPI,
.end = CH_SPI,
.flags = IORESOURCE_IRQ,
}
};

/* SPI controller data */
static struct bfin5xx_spi_master bfin_spi0_info = {
.num_chipselect = 8,
.enable_dma = 1, /* master has the ability to do dma transfer */
.pin_req = {P_SPI0_SCK, P_SPI0_MISO, P_SPI0_MOSI, 0},
};

static struct platform_device bfin_spi0_device = {
.name = "bfin-spi",
.id = 0, /* Bus number */
.num_resources = ARRAY_SIZE(bfin_spi0_resource),
.resource = bfin_spi0_resource,
.dev = {
.platform_data = &bfin_spi0_info, /* Passed to driver */
},
};

static int __init stamp_init(void) {
platform_add_devices(stamp_devices, ARRAY_SIZE(stamp_devices));

}
//-------------------------------------------------------------------------

The driver for SPI master:

static struct platform_driver bfin5xx_spi_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.suspend = bfin5xx_spi_suspend,
.resume = bfin5xx_spi_resume,
.remove = __devexit_p(bfin5xx_spi_remove),
}

/**
* platform_driver_probe - register driver for non-hotpluggable device
* @drv: platform driver structure
* @probe: the driver probe routine, probably from an __init section
*
* Use this instead of platform_driver_register() when you know the device
* is not hotpluggable and has already been registered, and you want to
* remove its run-once probe() infrastructure from memory after the driver
* has bound to the device.
*
* One typical use for this would be with drivers for controllers integrated
* into system-on-chip processors, where the controller devices have been
* configured as part of board setup.
*
* Returns zero if the driver registered and bound to a device, else returns
* a negative error code and with the driver not registered.
*/
static int __init bfin5xx_spi_init(void)
{
return platform_driver_probe(&bfin5xx_spi_driver, bfin5xx_spi_probe);
}

------------------------------------------------------------------------------------------
Bind spi slave devices and spi driver.

platform_probe()
--> bfin_spi_probe() // spi_bf5xx.c
--> spi_register_master() // spi.c
--> scan_board_info() // for all the devices in the table defined in board.c (stamp.c)
--> spi_new_device()
--> master->setup() // set up the new device HW
--> device_register()

-----------------------------------------------------------------
This driver defines bellow routine for spi master device
master->cleanup = cleanup;
master->setup = setup;
master->transfer = transfer;
------------------------------------------------------------------
Set up queues for transfer:
// spi_bf5xx.c
bfin_spi_probe()
---> init_queue()
---> tasklet_init(&drv_data->pump_transfers,
pump_transfers, (unsigned long)drv_data);
---> INIT_WORK(&drv_data->pump_messages, pump_messages);

root:/sys/devices/platform/bfin-spi.0> ls -lR
.:
lrwxrwxrwx 1 root root 0 Jan 1 01:48 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 01:48 driver -> ../../../bus/platform/drivers/bfin-spi
-r--r--r-- 1 root root 4096 Jan 1 01:48 modalias
drwxr-xr-x 2 root root 0 Jan 1 2007 power
drwxr-xr-x 3 root root 0 Jan 1 2007 spi0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:48 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx 1 root root 0 Jan 1 01:48 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 01:48 uevent

./power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

./spi0.1:
lrwxrwxrwx 1 root root 0 Jan 1 01:49 bus -> ../../../../bus/spi
lrwxrwxrwx 1 root root 0 Jan 1 01:49 driver -> ../../../../bus/spi/drivers/spidev
-r--r--r-- 1 root root 4096 Jan 1 01:49 modalias
drwxr-xr-x 2 root root 0 Jan 1 2007 power
lrwxrwxrwx 1 root root 0 Jan 1 01:49 spidev:spidev0.1 -> ../../../../class/spidev/spidev0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:49 subsystem -> ../../../../bus/spi
-rw-r--r-- 1 root root 4096 Jan 1 01:49 uevent

./spi0.1/power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup



For a SPI slave driver:
~~~~~~~~~~~~~~~~~

* This represents the kind of device driver that uses SPI messages to
* interact with the hardware at the other end of a SPI link. It's called
* a "protocol" driver because it works through messages rather than talking
* directly to SPI hardware (which is what the underlying SPI controller
* driver does to pass those messages). These protocols are defined in the
* specification for the device(s) supported by the driver.
*
* As a rule, those device protocols represent the lowest level interface
* supported by a driver, and it will support upper level interfaces too.
* Examples of such upper levels include frameworks like MTD, networking,
* MMC, RTC, filesystem character device nodes, and hardware monitoring.

struct spi_driver {
int (*probe)(struct spi_device *spi);
int (*remove)(struct spi_device *spi);
void (*shutdown)(struct spi_device *spi);
int (*suspend)(struct spi_device *spi, pm_message_t mesg);
int (*resume)(struct spi_device *spi);
struct device_driver driver;
};

static struct spi_driver bfin_spi_adc_driver = {
.driver = {
.name = "bfin_spi_adc",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = bfin_spi_adc_probe,
.remove = __devexit_p(bfin_spi_adc_remove),
};


spi_register_driver(&bfin_spi_adc_driver);


struct bfin_spi_adc {
int opened;
unsigned short *buffer;
int hz;
int cont;
struct spi_device *spidev;
dma_addr_t dma_handle;
};

struct bfin_spi_adc spi_adc;


struct spi_device {
struct device dev;
struct spi_master *master;
u32 max_speed_hz;
u8 chip_select;
u8 mode;
#define SPI_CPHA 0x01 /* clock phase */
#define SPI_CPOL 0x02 /* clock polarity */
#define SPI_MODE_0 (0|0) /* (original MicroWire) */
#define SPI_MODE_1 (0|SPI_CPHA)
#define SPI_MODE_2 (SPI_CPOL|0)
#define SPI_MODE_3 (SPI_CPOL|SPI_CPHA)
#define SPI_CS_HIGH 0x04 /* chipselect active high? */
#define SPI_LSB_FIRST 0x08 /* per-word bits-on-wire */
#define SPI_3WIRE 0x10 /* SI/SO signals shared */
#define SPI_LOOP 0x20 /* loopback mode */
u8 bits_per_word;
int irq;
void *controller_state;
void *controller_data;
const char *modalias;
};


root:/sys/bus/spi/drivers/spidev> ls -lR
.:
--w------- 1 root root 4096 Jan 1 01:47 bind
lrwxrwxrwx 1 root root 0 Jan 1 01:47 spi0.1 -> ../../../../devices/platform/bfin-spi.0/spi0.1
--w------- 1 root root 4096 Jan 1 01:47 unbind

root:/sys/devices/platform/bfin-spi.0> ls -lR
.:
lrwxrwxrwx 1 root root 0 Jan 1 01:48 bus -> ../../../bus/platform
lrwxrwxrwx 1 root root 0 Jan 1 01:48 driver -> ../../../bus/platform/drivers/bfin-spi
-r--r--r-- 1 root root 4096 Jan 1 01:48 modalias
drwxr-xr-x 2 root root 0 Jan 1 01:49 power
drwxr-xr-x 3 root root 0 Jan 1 01:49 spi0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:48 spi_master:spi0 -> ../../../class/spi_master/spi0
lrwxrwxrwx 1 root root 0 Jan 1 01:48 subsystem -> ../../../bus/platform
-rw-r--r-- 1 root root 4096 Jan 1 01:48 uevent

./power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

./spi0.1:
lrwxrwxrwx 1 root root 0 Jan 1 01:49 bus -> ../../../../bus/spi
lrwxrwxrwx 1 root root 0 Jan 1 01:49 driver -> ../../../../bus/spi/drivers/spidev
-r--r--r-- 1 root root 4096 Jan 1 01:49 modalias
drwxr-xr-x 2 root root 0 Jan 1 01:49 power
lrwxrwxrwx 1 root root 0 Jan 1 01:49 spidev:spidev0.1 -> ../../../../class/spidev/spidev0.1
lrwxrwxrwx 1 root root 0 Jan 1 01:49 subsystem -> ../../../../bus/spi
-rw-r--r-- 1 root root 4096 Jan 1 01:49 uevent

./spi0.1/power:
-rw-r--r-- 1 root root 4096 Jan 1 01:49 wakeup

-------------------------------------------------
Bind the SPI protocol driver to SPI device:

1. In driver init: calls spi_register_driver() --> driver_register()

2. when device_register() is called, the "Driver Core" will search the list of drivers,
and check whether the device "match" with a driver.

3. when driver_register() get called, the "Driver Core" will also scan existing devices



To send a message:
~~~~~~~~~~~~~~~~~

spi_sync() // spi.c
--> spi_async();
--> master->transfer(); // spi_bf5xx.c
--> queue_work(drv_data->workqueue, &drv_data->pump_messages);
--> pump_messages()
--> tasklet_schedule(&drv_data->pump_transfers) //activate the tasklet
--> pump_taskfer()
--> wait_for_completion(&done)


miscellaneous device
~~~~~~~~~~~~~~~~~~~~~
Misc (or miscellaneous) drivers are simple char drivers that share certain common characteristics. The kernel
abstracts these commonalities into an API (implemented in drivers/char/misc.c), and this simplifies the way
these drivers are initialized. All misc devices are assigned a major number of 10, but each can choose a single
minor number. So, if a char driver needs to drive multiple devices as in the CMOS example discussed earlier, it's
probably not a candidate for being a misc driver.



Platform devices
~~~~~~~~~~~~~~~~
Platform devices are devices that typically appear as autonomous
entities in the system. This includes legacy port-based devices and
host bridges to peripheral buses, and most controllers integrated
into system-on-chip platforms. What they usually have in common
is direct addressing from a CPU bus. Rarely, a platform_device will
be connected through a segment of some other kind of bus; but its
registers will still be directly addressable.

Platform devices are given a name, used in driver binding, and a
list of resources such as addresses and IRQs.


Blog Archive