SPI

From: dmitry pervushin
Date: Mon Sep 26 2005 - 06:18:29 EST


Hello guys,

I am attaching the next incarnation of SPI core; feel free to comment it.

Documentation/spi.txt | 351 +++++++++++++++++++++++++++++++++++++
arch/arm/Kconfig | 2
drivers/Kconfig | 2
drivers/Makefile | 1
drivers/spi/Kconfig | 33 +++
drivers/spi/Makefile | 11 +
drivers/spi/spi-core.c | 456 +++++++++++++++++++++++++++++++++++++++++++++++++
drivers/spi/spi-dev.c | 236 +++++++++++++++++++++++++
include/linux/spi.h | 214 ++++++++++++++++++++++
9 files changed, 1306 insertions(+)

Index: linux-2.6.10/arch/arm/Kconfig
===================================================================
--- linux-2.6.10.orig/arch/arm/Kconfig
+++ linux-2.6.10/arch/arm/Kconfig
@@ -834,6 +834,8 @@ source "drivers/ssi/Kconfig"

source "drivers/mmc/Kconfig"

+source "drivers/spi/Kconfig"
+
endmenu

source "ktools/Kconfig"
Index: linux-2.6.10/drivers/Kconfig
===================================================================
--- linux-2.6.10.orig/drivers/Kconfig
+++ linux-2.6.10/drivers/Kconfig
@@ -42,6 +42,8 @@ source "drivers/char/Kconfig"

source "drivers/i2c/Kconfig"

+source "drivers/spi/Kconfig"
+
source "drivers/w1/Kconfig"

source "drivers/misc/Kconfig"
Index: linux-2.6.10/drivers/Makefile
===================================================================
--- linux-2.6.10.orig/drivers/Makefile
+++ linux-2.6.10/drivers/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_DPM) += dpm/
obj-$(CONFIG_MMC) += mmc/
obj-y += firmware/
obj-$(CONFIG_EVENT_BROKER) += evb/
+obj-$(CONFIG_SPI) += spi/
Index: linux-2.6.10/drivers/spi/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/Kconfig
@@ -0,0 +1,33 @@
+#
+# SPI device configuration
+#
+menu "SPI support"
+
+config SPI
+ default Y
+ tristate "SPI support"
+ default false
+ help
+ Say Y if you need to enable SPI support on your kernel
+
+config SPI_DEBUG
+ bool "SPI debug output"
+ depends on SPI
+ default false
+ help
+ Say Y there if you'd like to see debug output from SPI drivers.
+ If unsure, say N
+
+config SPI_CHARDEV
+ default Y
+ tristate "SPI device interface"
+ depends on SPI
+ help
+ Say Y here to use spi-* device files, usually found in the /dev
+ directory on your system. They make it possible to have user-space
+ programs use the SPI bus.
+ This support is also available as a module. If so, the module
+ will be called spi-dev.
+
+endmenu
+
Index: linux-2.6.10/drivers/spi/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the kernel spi bus driver.
+#
+
+obj-$(CONFIG_SPI) += spi-core.o
+obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o
+
+ifeq ($(CONFIG_SPI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
Index: linux-2.6.10/drivers/spi/spi-core.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spi-core.c
@@ -0,0 +1,456 @@
+/*
+ * drivers/spi/spi-core.c
+ *
+ * Copyright (C) 2005 MontaVista Software, Inc <sources@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/proc_fs.h>
+#include <linux/kmod.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/kthread.h>
+#include <linux/spi.h>
+#include <asm/atomic.h>
+
+static int spi_thread(void *context);
+
+/*
+ * spi_bus_match_name
+ *
+ * Drivers and devices on SPI bus are matched by name, just like the
+ * platform devices, with exception of SPI_DEV_CHAR. Driver with this name
+ * will be matched against any device
+ */
+static int spi_bus_match_name(struct device *dev, struct device_driver *drv)
+{
+ return !strcmp (drv->name, SPI_DEV_CHAR) ||
+ !strcmp(TO_SPI_DEV(dev)->name, drv->name);
+}
+
+struct bus_type spi_bus = {
+ .name = "spi",
+ .match = spi_bus_match_name,
+};
+
+/*
+ * spi_bus_driver_init
+ *
+ * This function initializes the spi_bus_data structure for the
+ * bus. Functions has to be called when bus driver gets probed
+ *
+ * Parameters:
+ * spi_bus_driver* pointer to bus driver structure
+ * device* platform device to be attached to
+ * Return value:
+ * 0 on success, error code otherwise
+ */
+int spi_bus_driver_init(struct spi_bus_driver *bus, struct device *dev)
+{
+ struct spi_bus_data *pd =
+ kmalloc(sizeof(struct spi_bus_data), GFP_KERNEL);
+ int err = 0;
+
+ if (!pd) {
+ err = -ENOMEM;
+ goto init_failed_1;
+ }
+ atomic_set(&pd->exiting, 0);
+ pd->bus = bus;
+ init_MUTEX(&pd->lock);
+ INIT_LIST_HEAD(&pd->msgs);
+ init_waitqueue_head(&pd->queue);
+ pd->thread = kthread_run(spi_thread, pd, "%s-work", dev->bus_id);
+ if (IS_ERR(pd->thread)) {
+ err = PTR_ERR(pd->thread);
+ goto init_failed_2;
+ }
+ dev->platform_data = pd;
+ return 0;
+
+init_failed_2:
+ kfree(pd);
+init_failed_1:
+ return err;
+}
+
+/*
+ * __spi_bus_free
+ *
+ * This function called as part of unregistering bus device driver. It
+ * calls spi_device_del for each child (SPI) device on the bus
+ *
+ * Parameters:
+ * struct device* dev the 'bus' device
+ * void* context not used. Will be NULL
+ */
+int __spi_bus_free(struct device *dev, void *context)
+{
+ struct spi_bus_data *pd = dev->platform_data;
+
+ atomic_inc(&pd->exiting);
+ kthread_stop(pd->thread);
+ kfree(pd);
+
+ dev_dbg( dev, "unregistering children\n" );
+ /*
+ * NOTE: the loop below might needs redesign. Currently
+ * we delete devices from the head of children list
+ * until the list is empty; that's because the function
+ * device_for_each_child will hold the semaphore needed
+ * for deletion of device
+ */
+ while( !list_empty( &dev->children ) ) {
+ struct device* child = list_entry ( dev->children.next, struct device, node );
+ spi_device_del (TO_SPI_DEV (child) );
+ }
+ return 0;
+}
+
+/*
+ * spi_bus_driver_unregister
+ *
+ * unregisters the SPI bus from the system. Before unregistering, it deletes
+ * each SPI device on the bus using call to __spi_device_free
+ *
+ * Parameters:
+ * struct spi_bus_driver* bus_driver the bus driver
+ * Return value:
+ * void
+ */
+void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver)
+{
+ if (bus_driver) {
+ driver_for_each_dev( &bus_driver->driver, NULL, __spi_bus_free);
+ driver_unregister(&bus_driver->driver);
+ }
+}
+
+/*
+ * spi_device_release
+ *
+ * Pointer to this function will be put to dev->release place
+ * This function gets called as a part of device removing
+ *
+ * Parameters:
+ * struct device* dev
+ * Return value:
+ * none
+ */
+void spi_device_release( struct device* dev )
+{
+/* just a placeholder */
+}
+
+/*
+ * spi_device_add
+ *
+ * Add the new (discovered) SPI device to the bus. Mostly used by bus drivers
+ *
+ * Parameters:
+ * struct device* parent the 'bus' device
+ * struct spi_device* dev new device to be added
+ * char* name name of device. Should not be NULL
+ * Return value:
+ * error code, or 0 on success
+ */
+int spi_device_add(struct device *parent, struct spi_device *dev, char *name)
+{
+ if (!name || !dev)
+ return -EINVAL;
+
+ memset(&dev->dev, 0, sizeof(dev->dev));
+ dev->dev.parent = parent;
+ dev->dev.bus = &spi_bus;
+ strncpy( dev->name, name, sizeof(dev->name));
+ strncpy( dev->dev.bus_id, name, sizeof( dev->dev.bus_id ) );
+ dev->dev.release = spi_device_release;
+
+ return device_register(&dev->dev);
+}
+
+/*
+ * spi_queue
+ *
+ * Queue the message to be processed asynchronously
+ *
+ * Parameters:
+ * struct spi_msg* msg message to be sent
+ * Return value:
+ * 0 on no errors, negative error code otherwise
+ */
+int spi_queue( struct spi_msg *msg)
+{
+ struct device* dev = &msg->device->dev;
+ struct spi_bus_data *pd = dev->parent->platform_data;
+
+ down(&pd->lock);
+ list_add_tail(&msg->link, &pd->msgs);
+ dev_dbg(dev->parent, "message has been queued\n" );
+ up(&pd->lock);
+ wake_up_interruptible(&pd->queue);
+ return 0;
+}
+
+/*
+ * __spi_transfer_callback
+ *
+ * callback for synchronously processed message. If spi_transfer determines
+ * that there is no callback provided neither by msg->status nor callback
+ * parameter, the __spi_transfer_callback will be used, and spi_transfer
+ * does not return until transfer is finished
+ *
+ * Parameters:
+ * struct spimsg* msg message that is being processed now
+ * int code status of processing
+ */
+static void __spi_transfer_callback( struct spi_msg* msg, int code )
+{
+ if( code & (SPIMSG_OK|SPIMSG_FAILED) )
+ complete( (struct completion*)msg->context );
+}
+
+/*
+ * spi_transfer
+ *
+ * Process the SPI message, by queuing it to the driver and either
+ * immediately return or waiting till the end-of-processing
+ *
+ * Parameters:
+ * struct spi_msg* msg message to process
+ * callback user-supplied callback. If both msg->status and
+ * callback are set, the error code of -EINVAL
+ * will be returned
+ * Return value:
+ * 0 on success, error code otherwise. This code does not reflect
+ * status of message, just status of queueing
+ */
+int spi_transfer( struct spi_msg* msg, void (*callback)( struct spi_msg*, int ) )
+{
+ struct completion msg_done;
+ int err = -EINVAL;
+
+ if( callback && !msg->status ) {
+ msg->status = callback;
+ callback = NULL;
+ }
+
+ if( !callback ) {
+ if( !msg->status ) {
+ init_completion( &msg_done );
+ msg->context = &msg_done;
+ msg->status = __spi_transfer_callback;
+ spi_queue( msg );
+ wait_for_completion( &msg_done );
+ err = 0;
+ } else {
+ err = spi_queue( msg );
+ }
+ }
+
+ return err;
+}
+/*
+ * spi_thread
+ *
+ * This function is started as separate thread to perform actual
+ * transfers on SPI bus
+ *
+ * Parameters:
+ * void* context pointer to struct spi_bus_data
+ */
+static int spi_thread_awake(struct spi_bus_data *bd)
+{
+ int ret;
+
+ if (atomic_read(&bd->exiting)) {
+ return 1;
+ }
+ down(&bd->lock);
+ ret = !list_empty(&bd->msgs);
+ up(&bd->lock);
+ return ret;
+}
+
+static int spi_thread(void *context)
+{
+ struct spi_bus_data *bd = context;
+ struct spi_msg *msg;
+ int xfer_status;
+ int found;
+
+ while (!kthread_should_stop()) {
+
+ wait_event_interruptible(bd->queue, spi_thread_awake(bd));
+
+ if (atomic_read(&bd->exiting))
+ goto thr_exit;
+
+ down(&bd->lock);
+ while (!list_empty(&bd->msgs)) {
+ /*
+ * this part is locked by bus_data->lock,
+ * to protect spi_msg extraction
+ */
+ found = 0;
+ list_for_each_entry(msg, &bd->msgs, link) {
+ if (!bd->selected_device) {
+ bd->selected_device = msg->device;
+ if (bd->bus->select)
+ bd->bus->select(bd->
+ selected_device);
+ found = 1;
+ break;
+ }
+ if (msg->device == bd->selected_device) {
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /*
+ * all messages for current selected_device
+ * are processed.
+ * let's switch to another device
+ */
+ msg =
+ list_entry(bd->msgs.next, struct spi_msg,
+ link);
+ if (bd->bus->deselect)
+ bd->bus->deselect(bd->selected_device);
+ bd->selected_device = msg->device;
+ if (bd->bus->select)
+ bd->bus->select(bd->selected_device);
+ }
+ list_del(&msg->link);
+ up(&bd->lock);
+
+ /*
+ * and this part is locked by device's lock;
+ * spi_queue will be able to queue new
+ * messages
+ */
+ spi_device_lock(&msg->device);
+ if (msg->status)
+ msg->status(msg, SPIMSG_STARTED);
+ if( bd->bus->set_clock && msg->clock )
+ bd->bus->set_clock(
+ msg->device->dev.parent, msg->clock );
+ xfer_status = bd->bus->xfer( msg );
+ if (msg->status) {
+ msg->status(msg, SPIMSG_DONE);
+ msg->status(msg,
+ xfer_status ? SPIMSG_OK :
+ SPIMSG_FAILED);
+ }
+ spi_device_unlock(&msg->device);
+
+ /* lock the bus_data again... */
+ down(&bd->lock);
+ }
+ if (bd->bus->deselect)
+ bd->bus->deselect(bd->selected_device);
+ bd->selected_device = NULL;
+ /* device has been just deselected, unlocking the bus */
+ up(&bd->lock);
+ }
+thr_exit:
+ return 0;
+}
+
+/*
+ * spi_write
+ * send data to a device on an SPI bus
+ * Parameters:
+ * spi_device* dev the target device
+ * char* buf buffer to be sent
+ * int len buffer length
+ * Return:
+ * the number of bytes transferred, or negative error code.
+ */
+int spi_write(struct spi_device *dev, const char *buf, int len)
+{
+ struct spi_msg *msg = spimsg_alloc(dev, SPI_M_WR, len, NULL);
+ int ret;
+
+ memcpy(spimsg_buffer_wr(msg), buf, len);
+ ret = spi_transfer( msg, NULL );
+ return ret == 1 ? len : ret;
+}
+
+/*
+ * spi_write
+ * receive data from a device on an SPI bus
+ * Parameters:
+ * spi_device* dev the target device
+ * char* buf buffer to be sent
+ * int len number of bytes to receive
+ * Return:
+ * the number of bytes transferred, or negative error code.
+ */
+int spi_read(struct spi_device *dev, char *buf, int len)
+{
+ int ret;
+ struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL);
+
+ ret = spi_transfer( msg, NULL );
+ memcpy(buf, spimsg_buffer_rd(msg), len);
+ return ret == 1 ? len : ret;
+}
+
+int spi_bus_populate(struct device *parent,
+ char *devices,
+ void (*callback) (struct device * bus,
+ struct spi_device * new_dev))
+{
+ struct spi_device *new_device;
+ int count = 0;
+
+ while (devices[0]) {
+ dev_dbg(parent, "discovered new SPI device, name '%s'\n",
+ devices);
+ new_device = kmalloc(sizeof(struct spi_device), GFP_KERNEL);
+ if (!new_device) {
+ break;
+ }
+ if (spi_device_add(parent, new_device, devices)) {
+ break;
+ }
+ if (callback) {
+ callback(parent, new_device);
+ }
+ devices += (strlen(devices) + 1);
+ count++;
+ }
+ return count;
+}
+
+int __init spi_core_init( void )
+{
+ return bus_register(&spi_bus);
+}
+
+subsys_initcall(spi_core_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <dpervushin@xxxxxxxxxxxxx>");
+
+EXPORT_SYMBOL_GPL(spi_queue);
+EXPORT_SYMBOL_GPL(spi_device_add);
+EXPORT_SYMBOL_GPL(spi_bus_driver_unregister);
+EXPORT_SYMBOL_GPL(spi_bus_populate);
+EXPORT_SYMBOL_GPL(spi_transfer);
+EXPORT_SYMBOL_GPL(spi_write);
+EXPORT_SYMBOL_GPL(spi_read);
+EXPORT_SYMBOL_GPL(spi_bus);
+EXPORT_SYMBOL_GPL(spi_bus_driver_init);
Index: linux-2.6.10/drivers/spi/spi-dev.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spi-dev.c
@@ -0,0 +1,236 @@
+/*
+ spi-dev.c - spi-bus driver, char device interface
+
+ Copyright (C) 1995-97 Simon G. Vogl
+ Copyright (C) 1998-99 Frodo Looijaard <frodol@xxxxxx>
+ Copyright (C) 2002 Compaq Computer Corporation
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Adapted from i2c-dev module by Jamey Hicks <jamey.hicks@xxxxxxxxxx> */
+
+/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
+ But I have used so much of his original code and ideas that it seems
+ only fair to recognize him as co-author -- Frodo */
+
+/* The devfs code is contributed by Philipp Matthias Hahn
+ <pmhahn@xxxxxxxxxxxxx> */
+
+/* Modifications to allow work with current spi-core by
+ Andrey Ivolgin <aivolgin@xxxxxxxxxxxxx>, Sep 2004
+ */
+
+/* devfs code corrected to support automatic device addition/deletion
+ by Vitaly Wool <vwool@xxxxxxxxxxxxx> (C) 2004 MontaVista Software, Inc.
+ */
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/spi.h>
+
+#define SPI_TRANSFER_MAX 65535L
+
+struct spidev_driver_data
+{
+ int minor;
+};
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+ loff_t * offset);
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+ loff_t * offset);
+
+static int spidev_open(struct inode *inode, struct file *file);
+static int spidev_release(struct inode *inode, struct file *file);
+static int __init spidev_init(void);
+
+static void spidev_cleanup(void);
+
+static int spidev_probe(struct device *dev);
+static int spidev_remove(struct device *dev);
+
+static struct file_operations spidev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = spidev_read,
+ .write = spidev_write,
+ .open = spidev_open,
+ .release = spidev_release,
+};
+
+static struct class_simple *spidev_class;
+
+static struct spi_driver spidev_driver = {
+ .driver = {
+ .name = SPI_DEV_CHAR,
+ .probe = spidev_probe,
+ .remove = spidev_remove,
+ },
+};
+
+static int spidev_minor;
+
+static int spidev_probe(struct device *dev)
+{
+ struct spidev_driver_data *drvdata;
+
+ drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL);
+ if ( !drvdata) {
+ dev_dbg( dev, "allocating drvdata failed\n" );
+ return -ENOMEM;
+ }
+
+ drvdata->minor = spidev_minor++;
+ dev_dbg( dev, "setting device's(%p) minor to %d\n",
+ dev, drvdata->minor);
+ dev_set_drvdata(dev, drvdata);
+
+ class_simple_device_add(spidev_class,
+ MKDEV(SPI_MAJOR, drvdata->minor),
+ NULL, "spi%d", drvdata->minor);
+ dev_dbg( dev, " added\n" );
+ return 0;
+}
+
+static int spidev_remove(struct device *dev)
+{
+ struct spidev_driver_data *drvdata;
+
+ drvdata = (struct spidev_driver_data *)dev_get_drvdata(dev);
+ class_simple_device_remove(MKDEV(SPI_MAJOR, drvdata->minor));
+ kfree(drvdata);
+ dev_dbg( dev, " removed\n" );
+ return 0;
+}
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+ loff_t * offset)
+{
+ struct spi_device *dev = (struct spi_device *)file->private_data;
+ if( count > SPI_TRANSFER_MAX ) count = SPI_TRANSFER_MAX;
+ return spi_read(dev, buf, count );
+}
+
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+ loff_t * offset)
+{
+ struct spi_device *dev = (struct spi_device *)file->private_data;
+ if( count > SPI_TRANSFER_MAX ) count = SPI_TRANSFER_MAX;
+ return spi_write( dev, buf, count );
+}
+
+struct spidev_openclose {
+ unsigned int minor;
+ struct file *file;
+};
+
+static int spidev_do_open(struct device *the_dev, void *context)
+{
+ struct spidev_openclose *o = (struct spidev_openclose *)context;
+ struct spi_device *dev = TO_SPI_DEV(the_dev);
+ struct spidev_driver_data *drvdata;
+
+ drvdata = (struct spidev_driver_data *)dev_get_drvdata(the_dev);
+ if (NULL == drvdata) {
+ pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__);
+ return 0;
+ }
+
+ pr_debug("drvdata->minor = %d vs %d\n", drvdata->minor, o->minor);
+ if (drvdata->minor == o->minor) {
+ get_device(&dev->dev);
+ o->file->private_data = dev;
+ return 1;
+ }
+ return 0;
+}
+
+int spidev_open(struct inode *inode, struct file *file)
+{
+ struct spidev_openclose o;
+ int status;
+
+ o.minor = iminor(inode);
+ o.file = file;
+ status = driver_for_each_dev(&spidev_driver.driver, &o, spidev_do_open);
+ if (status == 0) {
+ status = -ENODEV;
+ }
+ return status < 0 ? status : 0;
+}
+
+static int spidev_release(struct inode *inode, struct file *file)
+{
+ struct spi_device *dev = file->private_data;
+
+ if (dev) {
+ put_device(&dev->dev);
+ }
+ file->private_data = NULL;
+
+ return 0;
+}
+
+static int __init spidev_init(void)
+{
+ int res;
+
+ if (0 != (res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops))) {
+ goto out;
+ }
+
+ spidev_class = class_simple_create(THIS_MODULE, "spi");
+ if (IS_ERR(spidev_class)) {
+ printk(KERN_ERR "%s: error creating class\n", __FUNCTION__);
+ res = -EINVAL;
+ goto out_unreg;
+ }
+
+ if (0 != (res = spi_driver_add(&spidev_driver))) {
+ goto out_unreg;
+ }
+ printk("SPI /dev entries driver.\n");
+ return 0;
+
+out_unreg:
+ unregister_chrdev(SPI_MAJOR, "spi");
+out:
+ printk(KERN_ERR "%s: Driver initialization failed\n", __FILE__);
+ return res;
+}
+
+static void spidev_cleanup(void)
+{
+ spi_driver_del(&spidev_driver);
+ class_simple_destroy(spidev_class);
+ unregister_chrdev(SPI_MAJOR, "spi");
+}
+
+MODULE_AUTHOR("dmitry pervushin <dpervushin@xxxxxxxxxxxxx>");
+MODULE_DESCRIPTION("SPI /dev entries driver");
+MODULE_LICENSE("GPL");
+
+module_init(spidev_init);
+module_exit(spidev_cleanup);
Index: linux-2.6.10/include/linux/spi.h
===================================================================
--- /dev/null
+++ linux-2.6.10/include/linux/spi.h
@@ -0,0 +1,214 @@
+/*
+ * linux/include/linux/spi/spi.h
+ *
+ * Copyright (C) 2005 MontaVista Software, Inc <sources@xxxxxxxxxx>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * Derived from l3.h by Jamey Hicks
+ */
+#ifndef SPI_H
+#define SPI_H
+
+#include <linux/types.h>
+
+struct spi_device;
+struct spi_driver;
+struct spi_msg;
+struct spi_bus_driver;
+
+extern struct bus_type spi_bus;
+
+struct spi_bus_data
+{
+ struct semaphore lock;
+ struct list_head msgs;
+ atomic_t exiting;
+ struct task_struct* thread;
+ wait_queue_head_t queue;
+ struct spi_device* selected_device;
+ struct spi_bus_driver* bus;
+};
+
+#define TO_SPI_BUS_DRIVER(drv) container_of( drv, struct spi_bus_driver, driver )
+struct spi_bus_driver
+{
+ int (*xfer)( struct spi_msg* msg );
+ void (*select)( struct spi_device* dev );
+ void (*deselect)( struct spi_device* dev );
+ void (*set_clock)( struct device* bus_device, u32 clock_hz );
+ struct device_driver driver;
+};
+
+#define TO_SPI_DEV(device) container_of( device, struct spi_device, dev )
+struct spi_device
+{
+ char name[ BUS_ID_SIZE ];
+ struct device dev;
+};
+
+#define TO_SPI_DRIVER(drv) container_of( drv, struct spi_driver, driver )
+struct spi_driver {
+ void* (*alloc)( size_t, int );
+ void (*free)( const void* );
+ unsigned char* (*get_buffer)( struct spi_device*, void* );
+ void (*release_buffer)( struct spi_device*, unsigned char*);
+ void (*control)( struct spi_device*, int mode, u32 ctl );
+ struct device_driver driver;
+};
+
+#define SPI_DEV_DRV( device ) TO_SPI_DRIVER( (device)->dev.driver )
+
+#define spi_device_lock( dev ) /* down( dev->dev.sem ) */
+#define spi_device_unlock( dev ) /* up( dev->dev.sem ) */
+
+/*
+ * struct spi_msg
+ *
+ * This structure represent the SPI message internally. You should never use fields of this structure directly
+ * Please use corresponding functions to create/destroy/access fields
+ *
+ */
+struct spi_msg {
+ unsigned char flags;
+#define SPI_M_RD 0x01
+#define SPI_M_WR 0x02 /**< Write mode flag */
+#define SPI_M_CSREL 0x04 /**< CS release level at end of the frame */
+#define SPI_M_CS 0x08 /**< CS active level at begining of frame ( default low ) */
+#define SPI_M_CPOL 0x10 /**< Clock polarity */
+#define SPI_M_CPHA 0x20 /**< Clock Phase */
+ unsigned short len; /* msg length */
+ unsigned long clock;
+ struct spi_device* device;
+ void *context;
+ struct list_head link;
+ void (*status)( struct spi_msg* msg, int code );
+ void *devbuf_rd, *devbuf_wr;
+ u8 *databuf_rd, *databuf_wr;
+};
+
+static inline struct spi_msg* spimsg_alloc( struct spi_device* device,
+ unsigned flags,
+ unsigned short len,
+ void (*status)( struct spi_msg*, int code ) )
+{
+ struct spi_msg* msg;
+ struct spi_driver* drv = SPI_DEV_DRV( device );
+
+ msg = kmalloc( sizeof( struct spi_msg ), GFP_KERNEL );
+ if( !msg )
+ return NULL;
+ memset( msg, 0, sizeof( struct spi_msg ) );
+ msg->len = len;
+ msg->status = status;
+ msg->device = device;
+ msg->flags = flags;
+ INIT_LIST_HEAD( &msg->link );
+ if( flags & SPI_M_RD ) {
+ msg->devbuf_rd = drv->alloc ?
+ drv->alloc( len, GFP_KERNEL ):kmalloc( len, GFP_KERNEL);
+ msg->databuf_rd = drv->get_buffer ?
+ drv->get_buffer( device, msg->devbuf_rd ) : msg->devbuf_rd;
+ }
+ if( flags & SPI_M_WR ) {
+ msg->devbuf_wr = drv->alloc ?
+ drv->alloc( len, GFP_KERNEL ):kmalloc( len, GFP_KERNEL);
+ msg->databuf_wr = drv->get_buffer ?
+ drv->get_buffer( device, msg->devbuf_wr ) : msg->devbuf_wr;
+ }
+ pr_debug( "%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n",
+ __FUNCTION__,
+ msg,
+ msg->devbuf_rd, msg->databuf_rd,
+ msg->devbuf_wr, msg->databuf_wr,
+ msg->flags & SPI_M_RD ? "RD" : "~rd",
+ msg->flags & SPI_M_WR ? "WR" : "~wr" );
+ return msg;
+}
+
+static inline void spimsg_free( struct spi_msg * msg )
+{
+ void (*do_free)( const void* ) = kfree;
+ struct spi_driver* drv = SPI_DEV_DRV( msg->device );
+
+ if( msg ) {
+ if( drv->free )
+ do_free = drv->free;
+ if( drv->release_buffer ) {
+ if( msg->databuf_rd)
+ drv->release_buffer( msg->device, msg->databuf_rd );
+ if( msg->databuf_wr)
+ drv->release_buffer( msg->device, msg->databuf_wr );
+ }
+ if( msg->devbuf_rd )
+ do_free( msg->devbuf_rd );
+ if( msg->devbuf_wr)
+ do_free( msg->devbuf_wr );
+ kfree( msg );
+ }
+}
+
+static inline u8* spimsg_buffer_rd( struct spi_msg* msg )
+{
+ return msg ? msg->databuf_rd : NULL;
+}
+
+static inline u8* spimsg_buffer_wr( struct spi_msg* msg )
+{
+ return msg ? msg->databuf_wr : NULL;
+}
+
+static inline u8* spimsg_buffer( struct spi_msg* msg )
+{
+ if( !msg ) return NULL;
+ if( ( msg->flags & (SPI_M_RD|SPI_M_WR) ) == (SPI_M_RD|SPI_M_WR) ) {
+ printk( KERN_ERR"%s: what buffer do you really want ?\n", __FUNCTION__ );
+ return NULL;
+ }
+ if( msg->flags & SPI_M_RD) return msg->databuf_rd;
+ if( msg->flags & SPI_M_WR) return msg->databuf_wr;
+}
+
+#define SPIMSG_OK 0x01
+#define SPIMSG_FAILED 0x80
+#define SPIMSG_STARTED 0x02
+#define SPIMSG_DONE 0x04
+
+#define SPI_MAJOR 98
+
+struct spi_driver;
+struct spi_device;
+
+static inline int spi_bus_driver_register( struct spi_bus_driver* bus_driver )
+{
+ return driver_register( &bus_driver->driver );
+}
+
+void spi_bus_driver_unregister( struct spi_bus_driver* );
+int spi_bus_driver_init( struct spi_bus_driver* driver, struct device* dev );
+int spi_device_add( struct device* parent, struct spi_device*, char* name );
+static inline void spi_device_del( struct spi_device* dev )
+{
+ device_unregister( &dev->dev );
+}
+static inline int spi_driver_add( struct spi_driver* drv )
+{
+ return driver_register( &drv->driver );
+}
+static inline void spi_driver_del( struct spi_driver* drv )
+{
+ driver_unregister( &drv->driver );
+}
+
+#define SPI_DEV_CHAR "spi-char"
+
+extern int spi_write(struct spi_device *dev, const char *buf, int len);
+extern int spi_read(struct spi_device *dev, char *buf, int len);
+
+extern int spi_queue( struct spi_msg* message );
+extern int spi_transfer( struct spi_msg* message, void (*status)( struct spi_msg*, int ) );
+extern int spi_bus_populate( struct device* parent, char* device, void (*assign)( struct device* parent, struct spi_device* ) );
+
+#endif /* SPI_H */
Index: linux-2.6.10/Documentation/spi.txt
===================================================================
--- /dev/null
+++ linux-2.6.10/Documentation/spi.txt
@@ -0,0 +1,351 @@
+Documentation/spi.txt
+========================================================
+Table of contents
+1. Introduction -- what is SPI ?
+2. Purposes of this code
+3. SPI devices stack
+3.1 SPI outline
+3.2 How the SPI devices gets discovered and probed ?
+3.3 DMA and SPI messages
+4. SPI functions and structures reference
+5. How to contact authors
+========================================================
+
+1. What is SPI ?
+----------------
+SPI stands for "Serial Peripheral Interface", a full-duplex synchronous
+serial interface for connecting low-/medium-bandwidth external devices
+using four wires. SPI devices communicate using a master/slave relation-
+ship over two data lines and two control lines:
+- Master Out Slave In (MOSI): supplies the output data from the master
+ to the inputs of the slaves;
+- Master In Slave Out (MISO): supplies the output data from a slave to
+ the input of the master. It is important to note that there can be no
+ more than one slave that is transmitting data during any particular
+ transfer;
+- Serial Clock (SCLK): a control line driven by the master, regulating
+ the flow of data bits;
+- Slave Select (SS): a control line that allows slaves to be turned on
+ and off with hardware control.
+More information is also available at http://en.wikipedia.org/wiki/Serial_Peripheral_Interface/ .
+
+2. Purposes of this code
+------------------------
+The supplied patch is starting point for implementing drivers for various
+SPI busses as well as devices connected to these busses. Currently, the
+SPI core supports only for MASTER mode for system running Linux.
+
+3. SPI devices stack
+--------------------
+
+3.1 The SPI outline
+
+The SPI infrastructure deals with several levels of abstraction. They are
+"SPI bus", "SPI bus driver", "SPI device" and "SPI device driver". The
+"SPI bus" is hardware device, which usually called "SPI adapter", and has
+"SPI devices" connected. From the Linux' point of view, the "SPI bus" is
+structure of type platform_device, and "SPI device" is structure of type
+spi_device. The "SPI bus driver" is the driver which controls the whole
+SPI bus (and, particularly, creates and destroys "SPI devices" on the bus),
+and "SPI device driver" is driver that controls the only device on the SPI
+bus, controlled by "SPI bus driver". "SPI device driver" can indirectly
+call "SPI bus driver" to send/receive messages using API provided by SPI
+core, and provide its own interface to the kernel and/or userland.
+So, the device stack looks as follows:
+
+ +--------------+ +---------+
+ | platform_bus | | spi_bus |
+ +--------------+ +---------+
+ |..| |
+ |..|--------+ +---------------+
+ +------------+| is parent to | SPI devices |
+ | SPI busses |+-------------> | |
+ +------------+ +---------------+
+ | |
+ +----------------+ +----------------------+
+ | SPI bus driver | | SPI device driver |
+ +----------------+ +----------------------+
+
+3.2 How do the SPI devices gets discovered and probed ?
+
+In general, the SPI bus driver cannot effective discover devices
+on its bus. Fortunately, the devices on SPI bus usually implemented
+onboard, so the following method has been chosen: the SPI bus driver
+calls the function named spi_bus_populate and passed the `topology
+string' to it. The function will parse the string and call the callback
+for each device, just before registering it. This allows bus driver
+to determine parameters like CS# for each device, retrieve them from
+string and store somewhere like spi_device->platform_data. An example:
+ err = spi_bus_populate( the_spi_bus,
+ "Dev1 0 1 2\0" "Dev2 2 1 0\0",
+ extract_name )
+In this example, function like extract_name would put the '\0' on the
+1st space of device's name, so names will become just "Dev1", "Dev2",
+and the rest of string will become parameters of device.
+
+3.3. DMA and SPI messages
+-------------------------
+
+To handle DMA transfers on SPI bus, any device driver might provide special
+callbacks to allocate/free/get access to buffer. These callbacks are defined
+in subsection iii of section 4.
+To send data using DMA, the buffers should be allocated using
+dma_alloc_coherent function. Usually buffers are allocated statically or
+using kmalloc function.
+To allow drivers to allocate buffers in non-standard
+When one allocates the structure for spi message, it needs to provide target
+device. If its driver wants to allocate buffer in driver-specific way, it may
+provide its own allocation/free methods: alloc and free. If driver does not
+provide these methods, kmalloc and kfree will be used.
+After allocation, the buffer must be accessed to copy the buffer to be send
+or retrieve buffer that has been just received from device. If buffer was
+allocated using driver's alloc method, it(buffer) will be accessed using
+get_buffer. Driver should provide accessible buffer that corresponds buffer
+allocated by driver's alloc method. If there is no get_buffer method,
+the result of alloc will be used.
+After reading/writing from/to buffer, it will be released by call to driver's
+release_buffer method.
+
+
+4. SPI functions are structures reference
+-----------------------------------------
+This section describes structures and functions that listed
+in include/linux/spi.h
+
+i. struct spi_msg
+~~~~~~~~~~~~~~~~~
+
+struct spi_msg {
+ unsigned char flags;
+ unsigned short len;
+ unsigned long clock;
+ struct spi_device* device;
+ void *context;
+ struct list_head link;
+ void (*status)( struct spi_msg* msg, int code );
+ void *devbuf_rd, *devbuf_wr;
+ u8 *databuf_rd, *databuf_wr;
+};
+This structure represents the message that SPI device driver sends to the
+SPI bus driver to handle.
+Fields:
+ flags combination of message flags
+ SPI_M_RD "read" operation (from device to host)
+ SPI_M_WR "write" operation (from host to device)
+ SPI_M_CS assert the CS signal before sending the message
+ SPI_M_CSREL clear the CS signal after sending the message
+ SPI_M_CSPOL set clock polarity to high
+ SPI_M_CPHA set clock phase to high
+ len length, in bytes, of allocated buffer
+ clock reserved, set to zero
+ device the target device of the message
+ context user-defined field; to associate any user data with the message
+ link used by bus driver to queue messages
+ status user-provided callback function to inform about message flow
+ devbuf_rd, devbuf_wr
+ so-called "device buffers". These buffers allocated by the
+ device driver, if device driver provides approproate callback.
+ Otherwise, the kmalloc API will be used.
+ databuf_rd, databuf_wr
+ pointers to access content of device buffers. They are acquired
+ using get_buffer callback, if device driver provides one.
+ Otherwise, they are just pointers to corresponding
+ device buffers
+
+struct spi_msg* spimsg_alloc( struct spi_device* device,
+ unsigned flags,
+ unsigned short len,
+ void (*status)( struct spi_msg*, int code ) )
+This functions is called to allocate the spi_msg structure and set the
+corresponding fields in structure. If device->platform_data provides callbacks
+to handle buffers, alloc/get_buffer are to be used. Returns NULL on errors.
+
+struct void spimsg_free( struct spi_msg* msg )
+Deallocate spi_msg as well as internal buffers. If msg->device->platform_data
+provides callbacks to handle buffers, release_buffer and free are to be used.
+
+u8* spimsg_buffer_rd( struct spi_msg* msg )
+u8* spimsg_buffer_wr( struct spi_msg* msg )
+u8* spimsg_buffer( struct spi_msg* )
+Return the corresponding data buffer, which can be directly modified by driver.
+spimsg_buffer checks flags and return either databuf_rd or databuf_wr basing on
+value of `flags' in spi_msg structure.
+
+ii. struct spi_device
+~~~~~~~~~~~~~~~~~~~~~
+
+struct spi_device
+{
+ char name[ BUS_ID_SIZE ];
+ struct device dev;
+};
+This structure represents the physical device on SPI bus. The SPI bus driver
+will create and register this structure for you.
+ name the name of the device. It should match to the SPI device
+ driver name
+ dev field used to be registered with core
+
+int spi_device_add( struct device* parent,
+ struct spi_device* dev,
+ char* name )
+This function registers the device `dev' on the spi bus, and set its parent
+to `parent', which represents the SPI bus. The device name will be set to name,
+that should be non-empty, non-NULL string. Returns 0 on no error, error code
+otherwise.
+
+void spi_device_del( struct spi_device* dev )
+Unregister the SPI device. Return value is ignored
+
+iii. struct spi_driver
+~~~~~~~~~~~~~~~~~~~~~~
+
+struct spi_driver {
+ void* (*alloc)( size_t, int );
+ void (*free)( const void* );
+ unsigned char* (*get_buffer)( struct spi_device*, void* );
+ void (*release_buffer)( struct spi_device*, unsigned char*);
+ void (*control)( struct spi_device*, int mode, u32 ctl );
+ struct device_driver driver;
+};
+This structure represents the SPI device driver object. Before registering,
+all fields of driver sub-structure should be properly filled, e.g., the
+`bus_type' should be set to spi_bus. Otherwise, the driver will be incorrectly
+registered and its callbacks might never been called. An example of will-
+formed spi_driver structure:
+ extern struct bus_type spi_bus;
+ static struct spi_driver pnx4008_eeprom_driver = {
+ .driver = {
+ .bus = &spi_bus,
+ .name = "pnx4008-eeprom",
+ .probe = pnx4008_spiee_probe,
+ .remove = pnx4008_spiee_remove,
+ .suspend = pnx4008_spiee_suspend,
+ .resume = pnx4008_spiee_resume,
+ },
+};
+The method control gets called during the processing of SPI message.
+For detailed description of malloc/free/get_buffer/release_buffer, please
+look to section 3.3, "DMA and SPI messages"
+
+
+int spi_driver_add( struct spi_driver* driver )
+Register the SPI device driver with core; returns 0 on no errors, error code
+otherwise.
+
+void spi_driver_del( struct spi_driver* driver )
+Unregisters the SPI device driver; return value ignored.
+
+iv. struct spi_bus_driver
+~~~~~~~~~~~~~~~~~~~~~~~~~
+To handle transactions over the SPI bus, the spi_bus_driver structure must
+be prepared and registered with core. Any transactions, either synchronous
+or asynchronous, go through spi_bus_driver->xfer function.
+
+struct spi_bus_driver
+{
+ int (*xfer)( struct spi_msg* msgs );
+ void (*select) ( struct spi_device* arg );
+ void (*deselect)( struct spi_device* arg );
+
+ struct device_driver driver;
+};
+
+Fields:
+ xfer pointer to function to execute actual transaction on SPI bus
+ msg message to handle
+ select pointer to function that gets called when bus needs to
+ select another device to be target of transfers
+ deselect
+ pointer to function that gets called before another device
+ is selected to be the target of transfers
+
+
+spi_bus_driver_register( struct spi_bus_driver* )
+
+Register the SPI bus driver with the system. The driver sub-structure should
+be properly filled before using this function, otherwise you may get unpredi-
+ctable results when trying to exchange data. An example of correctly prepared
+spi_bus_driver structure:
+ static struct spi_bus_driver spipnx_driver = {
+ .driver = {
+ .bus = &platform_bus_type,
+ .name = "spipnx",
+ .probe = spipnx_probe,
+ .remove = spipnx_remove,
+ .suspend = spipnx_suspend,
+ .resume = spipnx_resume,
+ },
+ .xfer = spipnx_xfer,
+};
+The driver and corresponding platform device are matched by name, so, in
+order the example abive to work, the platform_device named "spipnx" should
+be registered somewhere.
+
+void spi_bus_driver_unregister( struct spi_bus_driver* )
+
+Unregister the SPI bus driver registered by call to spi_buys_driver_register
+function; returns void.
+
+void spi_bus_populate( struct device* parent,
+ char* devices,
+ void (*callback)( struct device* parent, struct spi_device* new_one ) )
+This function usually called by SPI bus drivers in order to populate the SPI
+bus (see also section 3.2, "How the SPI devices gets discovered and probed ?").
+After creating the spi_device, the spi_bus_populate calls the `callback'
+function to allow to modify spi_device's fields before registering it with core.
+ parent pointer to SPI bus
+ devices string representing the current topology of SPI bus. It should
+ be formed like
+ "dev-1_and_its_info\0dev-2_and_its_info\0another_device\0\0"
+ the spi_bus_populate delimits this string by '\0' characters,
+ creates spi_device and after calling the callback registers the
+ spi_device
+ callback
+ pointer to function which could modify spi_device fields just
+ before registering them with core
+
+v. spi_transfer and spi_queue
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The driver that uses SPI core can initiate transfers either by calling
+spi_transfer function (that will wait till the transfer is funished) or
+queueing the message using spi_queue function (you need to provide function
+that will be called during message is processed). In any way, you need to
+prepare the spimsg structure and know the target device to your message to
+be sent.
+
+int spi_transfer( struct spi_msg msgs,
+ void (*callback)( struct spi_msg* msg, int ) )
+If callback is zero, start synchronous transfer. Otherwise, queue
+the message.
+ msg message to be handled
+ callback the callback function to be called during
+ message processing. If NULL, the function
+ will wait until end of processing.
+
+int spi_queue( struct spi_msg* msg )
+
+Queue the only message to the device. Returns status of queueing. To obtain
+status of message processing, you have to provide `status' callback in message
+and examine its parameters
+ msg message to be queued
+
+vi. the spi_bus variable
+~~~~~~~~~~~~~~~~~~~~~~~~
+This variable is created during initialization of spi core, and has to be
+specified as `bus' on any SPI device driver (look to section iii, "struct
+spi_driver" ). If you do not specify spi_bus, your driver will be never
+matched to spi_device and never be probed with hardware. Note that
+spi_bus.match points to function that matches drivers and devices by name,
+so SPI devices and their drivers should have the same name.
+
+5. How to contact authors
+-------------------------
+Do you have any comments ? Enhancements ? Device driver ? Feel free
+to contact me:
+ dpervushin@xxxxxxxxx
+ dimka@xxxxxxxxxxxxxxxx
+Visit our project page:
+ http://spi-devel.sourceforge.net
+Subscribe to mailing list:
+ spi-devel-general@xxxxxxxxxxxxxxxxxxxxx
+


-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/