Skip to content

Commit

Permalink
[PATCH] spi: add spi_driver to SPI framework
Browse files Browse the repository at this point in the history
This is a refresh of the "Simple SPI Framework" found in 2.6.15-rc3-mm1
which makes the following changes:

  * There's now a "struct spi_driver".  This increase the footprint
    of the core a bit, since it now includes code to do what the driver
    core was previously handling directly.  Documentation and comments
    were updated to match.

  * spi_alloc_master() now does class_device_initialize(), so it can
    at least be refcounted before spi_register_master().  To match,
    spi_register_master() switched over to class_device_add().

  * States explicitly that after transfer errors, spi_devices will be
    deselected.  We want fault recovery procedures to work the same
    for all controller drivers.

  * Minor tweaks:  controller_data no longer points to readonly data;
    prevent some potential cast-from-null bugs with container_of calls;
    clarifies some existing kerneldoc,

And a few small cleanups.

Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
  • Loading branch information
David Brownell authored and Greg Kroah-Hartman committed Jan 14, 2006
1 parent 1d6432f commit b885244
Show file tree
Hide file tree
Showing 3 changed files with 170 additions and 75 deletions.
52 changes: 32 additions & 20 deletions Documentation/spi/spi-summary
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
Overview of Linux kernel SPI support
====================================

22-Nov-2005
02-Dec-2005

What is SPI?
------------
The "Serial Peripheral Interface" (SPI) is a four-wire point-to-point
serial link used to connect microcontrollers to sensors and memory.
The "Serial Peripheral Interface" (SPI) is a synchronous four wire serial
link used to connect microcontrollers to sensors, memory, and peripherals.

The three signal wires hold a clock (SCLK, often on the order of 10 MHz),
and parallel data lines with "Master Out, Slave In" (MOSI) or "Master In,
Slave Out" (MISO) signals. (Other names are also used.) There are four
clocking modes through which data is exchanged; mode-0 and mode-3 are most
commonly used.
commonly used. Each clock cycle shifts data out and data in; the clock
doesn't cycle except when there is data to shift.

SPI masters may use a "chip select" line to activate a given SPI slave
device, so those three signal wires may be connected to several chips
Expand Down Expand Up @@ -79,11 +80,18 @@ The <linux/spi/spi.h> header file includes kerneldoc, as does the
main source code, and you should certainly read that. This is just
an overview, so you get the big picture before the details.

SPI requests always go into I/O queues. Requests for a given SPI device
are always executed in FIFO order, and complete asynchronously through
completion callbacks. There are also some simple synchronous wrappers
for those calls, including ones for common transaction types like writing
a command and then reading its response.

There are two types of SPI driver, here called:

Controller drivers ... these are often built in to System-On-Chip
processors, and often support both Master and Slave roles.
These drivers touch hardware registers and may use DMA.
Or they can be PIO bitbangers, needing just GPIO pins.

Protocol drivers ... these pass messages through the controller
driver to communicate with a Slave or Master device on the
Expand Down Expand Up @@ -116,11 +124,6 @@ shows up in sysfs in several locations:
managing bus "B". All the spiB.* devices share the same
physical SPI bus segment, with SCLK, MOSI, and MISO.

The basic I/O primitive submits an asynchronous message to an I/O queue
maintained by the controller driver. A completion callback is issued
asynchronously when the data transfer(s) in that message completes.
There are also some simple synchronous wrappers for those calls.


How does board-specific init code declare SPI devices?
------------------------------------------------------
Expand Down Expand Up @@ -263,33 +266,40 @@ would just be another kernel driver, probably offering some lowlevel
access through aio_read(), aio_write(), and ioctl() calls and using the
standard userspace sysfs mechanisms to bind to a given SPI device.

SPI protocol drivers are normal device drivers, with no more wrapper
than needed by platform devices:
SPI protocol drivers somewhat resemble platform device drivers:

static struct spi_driver CHIP_driver = {
.driver = {
.name = "CHIP",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},

static struct device_driver CHIP_driver = {
.name = "CHIP",
.bus = &spi_bus_type,
.probe = CHIP_probe,
.remove = __exit_p(CHIP_remove),
.remove = __devexit_p(CHIP_remove),
.suspend = CHIP_suspend,
.resume = CHIP_resume,
};

The SPI core will autmatically attempt to bind this driver to any SPI
The driver core will autmatically attempt to bind this driver to any SPI
device whose board_info gave a modalias of "CHIP". Your probe() code
might look like this unless you're creating a class_device:

static int __init CHIP_probe(struct device *dev)
static int __devinit CHIP_probe(struct spi_device *spi)
{
struct spi_device *spi = to_spi_device(dev);
struct CHIP *chip;
struct CHIP_platform_data *pdata = dev->platform_data;
struct CHIP_platform_data *pdata;

/* assuming the driver requires board-specific data: */
pdata = &spi->dev.platform_data;
if (!pdata)
return -ENODEV;

/* get memory for driver's per-chip state */
chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;
dev_set_drvdata(dev, chip);
dev_set_drvdata(&spi->dev, chip);

... etc
return 0;
Expand Down Expand Up @@ -328,6 +338,8 @@ the driver guarantees that it won't submit any more such messages.
- The basic I/O primitive is spi_async(). Async requests may be
issued in any context (irq handler, task, etc) and completion
is reported using a callback provided with the message.
After any detected error, the chip is deselected and processing
of that spi_message is aborted.

- There are also synchronous wrappers like spi_sync(), and wrappers
like spi_read(), spi_write(), and spi_write_then_read(). These
Expand Down
118 changes: 86 additions & 32 deletions drivers/spi/spi.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,9 @@
#include <linux/spi/spi.h>


/* SPI bustype and spi_master class are registered during early boot,
* usually before board init code provides the SPI device tables, and
* are available later when driver init code needs them.
*
* Drivers for SPI devices started out like those for platform bus
* devices. But both have changed in 2.6.15; maybe this should get
* an "spi_driver" structure at some point (not currently needed)
/* SPI bustype and spi_master class are registered after board init code
* provides the SPI device tables, ensuring that both are present by the
* time controller driver registration causes spi_devices to "enumerate".
*/
static void spidev_release(struct device *dev)
{
Expand Down Expand Up @@ -83,38 +79,37 @@ static int spi_uevent(struct device *dev, char **envp, int num_envp,

#ifdef CONFIG_PM

/* Suspend/resume in "struct device_driver" don't really need that
* strange third parameter, so we just make it a constant and expect
* SPI drivers to ignore it just like most platform drivers do.
*
/*
* NOTE: the suspend() method for an spi_master controller driver
* should verify that all its child devices are marked as suspended;
* suspend requests delivered through sysfs power/state files don't
* enforce such constraints.
*/
static int spi_suspend(struct device *dev, pm_message_t message)
{
int value;
int value;
struct spi_driver *drv = to_spi_driver(dev->driver);

if (!dev->driver || !dev->driver->suspend)
if (!drv || !drv->suspend)
return 0;

/* suspend will stop irqs and dma; no more i/o */
value = dev->driver->suspend(dev, message);
value = drv->suspend(to_spi_device(dev), message);
if (value == 0)
dev->power.power_state = message;
return value;
}

static int spi_resume(struct device *dev)
{
int value;
int value;
struct spi_driver *drv = to_spi_driver(dev->driver);

if (!dev->driver || !dev->driver->resume)
if (!drv || !drv->resume)
return 0;

/* resume may restart the i/o queue */
value = dev->driver->resume(dev);
value = drv->resume(to_spi_device(dev));
if (value == 0)
dev->power.power_state = PMSG_ON;
return value;
Expand All @@ -135,6 +130,41 @@ struct bus_type spi_bus_type = {
};
EXPORT_SYMBOL_GPL(spi_bus_type);


static int spi_drv_probe(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);

return sdrv->probe(to_spi_device(dev));
}

static int spi_drv_remove(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);

return sdrv->remove(to_spi_device(dev));
}

static void spi_drv_shutdown(struct device *dev)
{
const struct spi_driver *sdrv = to_spi_driver(dev->driver);

sdrv->shutdown(to_spi_device(dev));
}

int spi_register_driver(struct spi_driver *sdrv)
{
sdrv->driver.bus = &spi_bus_type;
if (sdrv->probe)
sdrv->driver.probe = spi_drv_probe;
if (sdrv->remove)
sdrv->driver.remove = spi_drv_remove;
if (sdrv->shutdown)
sdrv->driver.shutdown = spi_drv_shutdown;
return driver_register(&sdrv->driver);
}
EXPORT_SYMBOL_GPL(spi_register_driver);

/*-------------------------------------------------------------------------*/

/* SPI devices should normally not be created by SPI device drivers; that
Expand Down Expand Up @@ -208,13 +238,15 @@ spi_new_device(struct spi_master *master, struct spi_board_info *chip)
if (status < 0) {
dev_dbg(dev, "can't %s %s, status %d\n",
"add", proxy->dev.bus_id, status);
fail:
class_device_put(&master->cdev);
kfree(proxy);
return NULL;
goto fail;
}
dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
return proxy;

fail:
class_device_put(&master->cdev);
kfree(proxy);
return NULL;
}
EXPORT_SYMBOL_GPL(spi_new_device);

Expand All @@ -237,11 +269,11 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
{
struct boardinfo *bi;

bi = kmalloc (sizeof (*bi) + n * sizeof (*info), GFP_KERNEL);
bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
if (!bi)
return -ENOMEM;
bi->n_board_info = n;
memcpy(bi->board_info, info, n * sizeof (*info));
memcpy(bi->board_info, info, n * sizeof *info);

down(&board_lock);
list_add_tail(&bi->list, &board_list);
Expand Down Expand Up @@ -330,6 +362,7 @@ spi_alloc_master(struct device *dev, unsigned size)
if (!master)
return NULL;

class_device_initialize(&master->cdev);
master->cdev.class = &spi_master_class;
master->cdev.dev = get_device(dev);
class_set_devdata(&master->cdev, &master[1]);
Expand Down Expand Up @@ -366,19 +399,17 @@ spi_register_master(struct spi_master *master)
/* convention: dynamically assigned bus IDs count down from the max */
if (master->bus_num == 0) {
master->bus_num = atomic_dec_return(&dyn_bus_id);
dynamic = 0;
dynamic = 1;
}

/* register the device, then userspace will see it.
* registration fails if the bus ID is in use.
*/
snprintf(master->cdev.class_id, sizeof master->cdev.class_id,
"spi%u", master->bus_num);
status = class_device_register(&master->cdev);
if (status < 0) {
class_device_put(&master->cdev);
status = class_device_add(&master->cdev);
if (status < 0)
goto done;
}
dev_dbg(dev, "registered master %s%s\n", master->cdev.class_id,
dynamic ? " (dynamic)" : "");

Expand Down Expand Up @@ -491,6 +522,7 @@ static u8 *buf;
* This performs a half duplex MicroWire style transaction with the
* device, sending txbuf and then reading rxbuf. The return value
* is zero for success, else a negative errno status code.
* This call may only be used from a context that may sleep.
*
* Parameters to this routine are always copied using a small buffer,
* large transfers should use use spi_{async,sync}() calls with
Expand Down Expand Up @@ -553,16 +585,38 @@ EXPORT_SYMBOL_GPL(spi_write_then_read);

static int __init spi_init(void)
{
int status;

buf = kmalloc(SPI_BUFSIZ, SLAB_KERNEL);
if (!buf)
return -ENOMEM;
if (!buf) {
status = -ENOMEM;
goto err0;
}

status = bus_register(&spi_bus_type);
if (status < 0)
goto err1;

bus_register(&spi_bus_type);
class_register(&spi_master_class);
status = class_register(&spi_master_class);
if (status < 0)
goto err2;
return 0;

err2:
bus_unregister(&spi_bus_type);
err1:
kfree(buf);
buf = NULL;
err0:
return status;
}

/* board_info is normally registered in arch_initcall(),
* but even essential drivers wait till later
*
* REVISIT only boardinfo really needs static linking. the rest (device and
* driver registration) _could_ be dynamically linked (modular) ... costs
* include needing to have boardinfo data structures be much more public.
*/
subsys_initcall(spi_init);

Loading

0 comments on commit b885244

Please sign in to comment.