[PATCH 2.6-git 1/3] SPI core refresh: the core

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Dmitry Pervushin <[email protected]>
Signed-off-by: Vitaly Wool <[email protected]>

 Documentation/spi.txt    |  134 +++++++++
 arch/arm/Kconfig         |    2
 drivers/Kconfig          |    2
 drivers/Makefile         |    1
 drivers/spi/Kconfig      |   46 +++
 drivers/spi/Makefile     |   19 +
 drivers/spi/spi-alloc.c  |  101 +++++++
 drivers/spi/spi-alloc.h  |   54 +++
 drivers/spi/spi-core.c   |  658 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-core.h   |   32 ++
 drivers/spi/spi-dev.c    |  209 ++++++++++++++
 drivers/spi/spi-thread.c |  187 +++++++++++++
 drivers/spi/spi-thread.h |   42 +++
 include/linux/spi.h      |  252 ++++++++++++++++++
 14 files changed, 1739 insertions(+)

Index: linux-2.6.orig/Documentation/spi.txt
===================================================================
--- /dev/null
+++ linux-2.6.orig/Documentation/spi.txt
@@ -0,0 +1,134 @@
+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 slave device" and "SPI device driver". The
+"SPI bus" is hardware device, which usually called "SPI adapter", and has
+"SPI slave devices" connected. From the Linux' point of view, the "SPI bus" is
+structure of type platform_device, and "SPI slave 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 slave 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:
+
+  +--------------+                    +---------+
+  | some_bus     |                    | spi_bus |
+  +--------------+                    +---------+
+       |..|                                |
+       |..|--------+               +---------------+
+     +------------+| is parent to  |  SPI devices  |
+     | SPI busses |+-------------> |               |
+     +------------+                +---------------+
+           |                               |
+     +----------------+          +----------------------+
+     | SPI bus driver |          |    SPI device driver |
+     +----------------+          +----------------------+
+
+3.2 How do the SPI devices get discovered and probed ?
+
+In general, the SPI bus driver cannot effectively discover devices
+on its bus. Fortunately, the devices on SPI bus usually implemented
+onboard, so you need to create array of structures spi_device_desc and
+pass this array to function spi_bus_populate, like this:
+  struct spi_device_desc spi_slaves[] = {
+    [0] = {
+	.name = "device1",
+        .param = device1_params,
+    },
+    [1] = {
+        .name = "device2",
+        .param = NULL,
+    }
+    [2] = {
+	NULL, NULL
+    };
+  err = spi_bus_populate( the_spi_bus, spi_slaves, callback );
+
+3.3. DMA and SPI messages
+-------------------------
+
+The core provides additional robustness when the buffer suppiled is not
+DMA-safe. If it is, the core will allocate DMA-safe buffer and copy user-
+supplied buffer to it (before operation in WRITE case, and after in READ case).
+This two situations are distinguished by specific flag SPI_M_DMAUNSAFE.
+Bus driver should use spimsg_get_buffer and spimsg_put_buffer to access buffer.
+The buffer received from spimsg_get_buffer will be always DMA-safe and suitable for
+DMA mapping.
+
+4. SPI functions are structures reference
+-----------------------------------------
+Please refer to kerneldocs for the information. To create it, use command
+	$ scripts/kernel-doc -html drivers/spi/* > spi.html
+
+5. Getting the latest sources
+-----------------------------
+The stable snapshots are available at
+	http://spi-devel.sourceforge.net/downloads
+The most recent sources can be obtained via the CVS.
+Use the following CVS setup to grab them:
+* set CVSROOT environment variable to 
+	:pserver:[email protected]:/cvsroot/spi-devel, i. e.
+$ export CVSROOT=:pserver:[email protected]:/cvsroot/spi-devel
+* login into the anonymous CVS using 'cvs login'. When prompted for password,
+  just type Enter, i. e.
+$ cvs login
+Logging in to :pserver:[email protected]:2401/cvsroot/spi-devel
+CVS password:
+$
+* checkout the 'spi-core' CVS repo:
+$ cvs co cpi-core
+You'll get a set of patches to be applied to the most recent 2.6-git kernel.
+
+6. How to contact authors
+-------------------------
+Do you have any comments ? Enhancements ? Device driver ? Feel free
+to contact me:
+	[email protected]
+	[email protected]
+Visit our project page:
+	http://spi-devel.sourceforge.net
+Subscribe to mailing list:
+	[email protected]
+
Index: linux-2.6.orig/arch/arm/Kconfig
===================================================================
--- linux-2.6.orig.orig/arch/arm/Kconfig
+++ linux-2.6.orig/arch/arm/Kconfig
@@ -753,6 +753,8 @@ source "drivers/usb/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 endmenu
 
 source "fs/Kconfig"
Index: linux-2.6.orig/drivers/Kconfig
===================================================================
--- linux-2.6.orig.orig/drivers/Kconfig
+++ linux-2.6.orig/drivers/Kconfig
@@ -44,6 +44,8 @@ source "drivers/char/Kconfig"
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/hwmon/Kconfig"
Index: linux-2.6.orig/drivers/Makefile
===================================================================
--- linux-2.6.orig.orig/drivers/Makefile
+++ linux-2.6.orig/drivers/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_SGI_IOC4)		+= sn/
 obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
 obj-$(CONFIG_SUPERH)		+= sh/
+obj-$(CONFIG_SPI)		+= spi/
Index: linux-2.6.orig/drivers/spi/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/Kconfig
@@ -0,0 +1,46 @@
+#
+# SPI device configuration
+#
+menu "SPI support"
+
+config SPI
+	tristate "SPI (Serial Peripheral Interface) bus support"
+        default false
+	help
+	  Say Y if you need to enable SPI support on your kernel.
+ 	  Say M if you want to create the spi loadable module.
+
+config SPI_THREAD
+	bool "Threaded handling of SPI asynchronous messages"
+	default true
+	help
+	  Say Y here to compile thread-based asynchronous message
+	  handling for SPI. This will be a default SPI async message
+	  handling method, which can be overridden by bus driver.
+	  If unsure, say Y.
+
+config SPI_CUSTOM_ALLOC
+	bool "Custom (faster) SPI message allocation"
+	default false
+	help
+	  Say Y here to use faster SPI message allocation.
+	  If unsure, say N.
+
+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
+	bool "SPI device interface"
+	depends on SPI
+	help
+	  Say Y here to use /dev/spiNN device files. They make it possible to have user-space
+	  programs use the SPI bus.
+
+endmenu
+
Index: linux-2.6.orig/drivers/spi/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/Makefile
@@ -0,0 +1,19 @@
+#
+# Makefile for the kernel spi bus driver.
+#
+
+spi-y += spi-core.o
+spi-$(CONFIG_SPI_CHARDEV) += spi-dev.o
+spi-$(CONFIG_SPI_THREAD) += spi-thread.o
+spi-$(CONFIG_SPI_CUSTOM_ALLOC) += spi-alloc.o
+# bus drivers
+obj-$(CONFIG_SPI_PNX) += spipnx.o
+#  ...functional drivers
+obj-$(CONFIG_SPI_PNX4008_EEPROM) += pnx4008-eeprom.o
+# ...and the common spi-dev driver
+obj-$(CONFIG_SPI) += spi.o
+
+ifeq ($(CONFIG_SPI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
Index: linux-2.6.orig/drivers/spi/spi-core.c
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-core.c
@@ -0,0 +1,658 @@
+/*
+ *  drivers/spi/spi-core.c
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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>
+
+#include "spi-thread.h"
+#include "spi-alloc.h"
+#include "spi-core.h"
+
+static int spi_device_del(struct device *dev, void *data);
+
+/**
+ * spimsg_set_clock - set message's clock
+ * @message: SPI message
+ * @clock: clock settings
+ */
+void spimsg_set_clock (struct spi_msg* message, u32 clock)
+{
+	message->clock = clock;
+}
+
+/**
+ * spimsg_get_clock - get message's clock
+ * @message: SPI message
+ */
+u32 spimsg_get_clock (struct spi_msg* message)
+{
+	return message->clock;
+}
+
+/**
+ * spimsg_get_flags - get message's flags
+ * @message: SPI message
+ */
+u32 spimsg_get_flags (struct spi_msg* message)
+{
+	return message->flags;
+}
+
+/**
+ * spimsg_get_buffer - get the buffer for I/O ops
+ * @message: SPI message
+ * @buffer: ptr to the buffer to be filled in
+ */
+u32 spimsg_get_buffer (struct spi_msg *message, void **buffer)
+{
+	struct spi_bus_driver *bdrv = SPI_DEV_DRV(message->device);
+	
+	if (!buffer)
+		return 0;
+
+	*buffer = message->buf_ptr;
+
+	if (message->flags & SPI_M_DMAUNSAFE && bdrv->dma_safe_alloc) {
+		*buffer = bdrv->dma_safe_alloc (message->buf_ptr, message->len, message->flags & (SPI_M_RD|SPI_M_WR));
+		if (!*buffer)
+			return 0;
+	}
+	return message->len;
+}
+
+/**
+ * spimsg_put_buffer - put the buffer used by I/O ops
+ * @message: SPI message
+ * @buffer: buffer used for I/O
+ */
+void spimsg_put_buffer (struct spi_msg *message, void *buffer)
+{
+	struct spi_bus_driver *bdrv = SPI_DEV_DRV(message->device);
+
+	if (message->flags & SPI_M_DMAUNSAFE && bdrv->dma_safe_free)
+		bdrv->dma_safe_free (message->buf_ptr, buffer, message->len, message->flags & (SPI_M_RD|SPI_M_WR));
+}
+
+/**
+ * spimsg_get_spidev - obtain the SPI device from the message
+ * @message: SPI message
+ */
+struct spi_device *spimsg_get_spidev (struct spi_msg* message)
+{
+	return message->device;
+}
+
+void spimsg_set_ctx(struct spi_msg *message, void *ctx)
+{
+	message->context = ctx;
+}
+
+void *spimsg_get_ctx(struct spi_msg *message)
+{
+	return message->context;
+}
+
+int spimsg_complete(struct spi_msg *message, int code)
+{
+	if (message->status)
+		message->status(message, code);
+	return code;
+}
+
+static inline struct spi_msg *__spimsg_alloc(struct spi_device *device,
+					   struct spi_msg *link,
+					   u32 flags,
+					   void *buf,
+					   unsigned short len,
+					   void (*status) (struct spi_msg *,
+							   int code))
+{
+	struct spi_msg *msg;
+
+	if ((flags & (SPI_M_RD|SPI_M_WR)) == (SPI_M_RD|SPI_M_WR))
+		return NULL;
+	msg = spimsg_kzalloc();
+	if (!msg)
+		return NULL;
+	msg->len = len;
+	msg->status = status;
+	msg->device = device;
+	msg->flags = flags;
+	INIT_LIST_HEAD(&msg->link);
+
+	msg->buf_ptr = buf;
+
+	if (link)
+		link->next = msg;
+
+	return msg;
+}
+
+/**
+ * spimsg_alloc - allocate the spi message
+ * @device: target device
+ * @flags: SPI message flags
+ * @buf: user-supplied buffer
+ * @len: buffer's length
+ * @status: user-supplied callback function
+ */
+struct spi_msg *spimsg_alloc(struct spi_device *device,
+					   u32 flags,
+					   void *buf,
+					   unsigned short len,
+					   void (*status) (struct spi_msg *,
+							   int code))
+{
+	return __spimsg_alloc(device, NULL, flags, buf, len, status);
+}
+
+/**
+ * spimsg_chain - allocate the spi message and link it to the current
+ * @msg: current message
+ * @flags: SPI message flags
+ * @buf: user-supplied buffer
+ * @len: buffer's length
+ * @status: user-supplied callback function
+ */
+struct spi_msg *spimsg_chain(struct spi_msg *msg,
+					   u32 flags,
+					   void *buf,
+					   unsigned short len,
+					   void (*status) (struct spi_msg *,
+							   int code))
+{
+	return msg ?
+		__spimsg_alloc(msg->device, msg, flags, buf, len, status) :
+		NULL;
+}
+
+/**
+ * spimsg_free - free the message(s) allocated by spimsg_alloc
+ * @msg: head message to free
+ */
+void spimsg_free(struct spi_msg *msg)
+{
+	if (msg->next)
+		spimsg_free(msg->next);
+	spimsg_kfree(msg);
+}
+
+/**
+ * spimsg_getnext - get next message in chain
+ * @message: this message
+ */
+struct spi_msg *spimsg_getnext(struct spi_msg *message)
+{
+	return message ? message->next : ERR_PTR(-EINVAL);
+}
+
+/**
+ * spi_bus_match_name - verify that driver matches device on spi bus
+ * @dev: device that hasn't yet being assigned to any driver
+ * @drv: driver for spi device
+ * Description:
+ * 	match the device to driver.Drivers and devices on SPI bus
+ * 	are matched by name, just like the platform devices
+ */
+static int spi_bus_match_name(struct device *dev, struct device_driver *drv)
+{
+	return !strcmp(TO_SPI_DEV(dev)->name, drv->name);
+}
+
+/**
+ * spi_bus_suspend - suspend all devices on the spi bus
+ * @dev: spi device to be suspended
+ * @message: PM message
+ * Description:
+ * 	this function set device on SPI bus to suspended state, just
+ * 	like platform_bus does
+ */
+static int spi_bus_suspend(struct device * dev, pm_message_t message)
+{
+	int ret = 0;
+
+	if (dev->driver && TO_SPI_DRIVER(dev->driver)->suspend ) {
+		ret = TO_SPI_DRIVER(dev->driver)->suspend( TO_SPI_DEV(dev), message);
+		if (ret == 0 )
+			dev->power.power_state = message;
+	}
+	return ret;
+}
+
+/**
+ * spi_bus_resume - resume functioning of all devices on spi bus
+ * @dev: device to resume
+ * Description:
+ * 	This function resumes device on SPI bus, just like platform_bus does
+ */
+static int spi_bus_resume(struct device * dev)
+{
+	int ret = 0;
+
+	if (dev->driver && TO_SPI_DRIVER(dev->driver)->suspend ) {
+		ret = TO_SPI_DRIVER(dev->driver)->resume(TO_SPI_DEV(dev));
+		if (ret == 0)
+			dev->power.power_state = PMSG_ON;
+	}
+	return ret;
+}
+
+struct bus_type spi_bus = {
+	.name = "spi",
+	.match = spi_bus_match_name,
+	.suspend = spi_bus_suspend,
+	.resume = spi_bus_resume,
+};
+
+/**
+ * spi_bus_driver_init - init internal bus driver structures
+ * @bus: registered spi_bus_driver structure
+ * @dev: device that represents spi controller
+ * Description:
+ * 	Once registered by spi_bus_register, the bus driver needs
+ * 	initialization, that includes starting thread, initializing
+ * 	internal structures.. The best place where the spi_bus_driver_init
+ * 	is in the `probe' function, when we already sure that passed
+ * 	device object is SPI master controller.
+ */
+int spi_bus_driver_init(struct spi_bus_driver *bus, struct device *dev)
+{
+	struct spi_bus_data *pd =
+	    kmalloc(sizeof(struct spi_bus_data), SLAB_KERNEL);
+	int err = 0;
+
+	if (!pd) {
+		err = -ENOMEM;
+		goto init_failed;
+	}
+
+	pd->bus = bus;
+	pd->lock = SPIN_LOCK_UNLOCKED;
+	INIT_LIST_HEAD(&pd->msgs);
+	pd->id = dev->bus_id;
+
+	if (!bus->start_async && !bus->stop_async) {
+		bus->start_async = spi_start_async;
+		bus->stop_async = spi_stop_async;
+		if (!bus->queue)
+			bus->queue = spi_queue;
+	}
+
+	dev->platform_data = pd;
+
+	pd->async_data = bus->start_async ? bus->start_async(dev) : NULL;
+
+	return 0;
+
+init_failed:
+	return err;
+}
+
+/**
+ * spi_bus_driver_cleanup -  cleanup internal driver structures
+ *
+ * @bus: pointer to spi_bus_driver structure to deinitialize
+ * @dev: bus device object
+ *
+ * Description: in order to roll back initialization of bus driver,
+ * which is made by spi_bus_driver_init, this function should be used
+ **/
+int spi_bus_driver_cleanup (struct spi_bus_driver *bus_drv, struct device *dev)
+{
+	struct spi_bus_data *pd = dev->platform_data;
+
+	if (!pd) {
+		if(bus_drv->stop_async)
+			bus_drv->stop_async(dev, pd->async_data);
+		kfree (pd);
+		dev->platform_data = NULL;
+		device_for_each_child(dev, NULL, spi_device_del);
+	}
+	return 0;
+}
+/**
+ * __spi_bus_free -- unregister all children of the spi bus
+ * @dev: the spi bus `device' object
+ * @context: not used, will be NULL
+ * Description:
+ * 	This is an internal function that is called when unregistering
+ * 	bus driver. Responsibility of this function is freeing the
+ * 	resources that were requested by spi_bus_driver_init
+ */
+static int __spi_bus_free(struct device *dev, void *context)
+{
+	return spi_bus_driver_cleanup(TO_SPI_BUS_DRIVER(dev->driver), dev);
+}
+
+/**
+ * spi_bus_driver_unregister - unregister SPI bus controller from the system
+ * @bus_driver: driver registered by call to spi_bus_driver_register
+ * Description:
+ * 	This routine unregisters the SPI bus from the system. Before
+ * 	unregistering, it deletes each SPI device on the bus using call
+ * 	to __spi_device_free
+ */
+void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver)
+{
+	if (bus_driver) {
+		driver_for_each_device(&bus_driver->driver, NULL, NULL, __spi_bus_free);
+		driver_unregister(&bus_driver->driver);
+	}
+}
+
+/**
+ * spi_device_release - release the spi device structure
+ * @dev: spi_device to be released
+ * Description:
+ * 	Pointer to this function will be put to dev->release place
+ * 	This fus called as a part of device removing
+ */
+void spi_device_release(struct device *dev)
+{
+	struct spi_device* sdev = TO_SPI_DEV(dev);
+
+	kfree(sdev);
+}
+
+/**
+ * spi_device_add - add the new (discovered) SPI device to the bus. Mostly used by bus drivers
+ * @parent: the bus device object
+ * @name: name of device (non-null!)
+ * @bus_data: bus data to be assigned to device
+ * Description:
+ * 	SPI device usually cannot be discovered by SPI bus driver, so it
+ * 	needs to take the configuration somewhere from hardcoded structures,
+ * 	and prepare bus_data for its devices
+ */
+struct spi_device* spi_device_add(struct device *parent, char *name, void *bus_data)
+{
+	struct spi_device* dev;
+	static int minor = 0;
+
+	if (!name)
+		goto dev_add_out;
+
+	dev = kzalloc(sizeof(struct spi_device), SLAB_KERNEL);
+	if(!dev)
+		goto dev_add_out;
+
+	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;
+	dev->dev.platform_data = bus_data;
+
+	if (device_register(&dev->dev)<0) {
+		dev_dbg(parent, "device '%s' cannot be added\n", name);
+		goto dev_add_out_2;
+	}
+	dev->cdev = spi_class_device_create(minor, &dev->dev);
+	dev->minor = minor++;
+	return dev;
+
+dev_add_out_2:
+	kfree(dev);
+dev_add_out:
+	return NULL;
+}
+
+/**
+ * spi_device_del - delete the SPI device
+ * @dev: device to delete
+ * @data: data associated with the device
+ */
+static int spi_device_del(struct device *dev, void *data)
+{
+	struct spi_device *spidev = TO_SPI_DEV(dev);
+	if (spidev->cdev) {
+		spi_class_device_destroy(spidev->cdev);
+		spidev->cdev = NULL;
+	}
+	device_unregister(&spidev->dev);
+	return 0;
+}
+/**
+ * __spi_transfer_callback - callback to process synchronous messages
+ * @msg: message that is about to complete
+ * @code: message status
+ * Description:
+ * 	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
+ */
+static void __spi_transfer_callback(struct spi_msg *msg, int code)
+{
+	complete(&msg->sync);
+}
+
+/*
+ * spi_transfer - transfer the message either in sync or async way
+ * @msg: message to process
+ * @callback: user-supplied callback
+ * @return:
+ * 	if both msg->status and callback are set, the error code of -EINVAL
+ * 	will be returned
+ */
+int spi_transfer(struct spi_msg *msg, void (*callback) (struct spi_msg *, int))
+{
+	int err = -EINVAL;
+	struct device *bus = msg->device->dev.parent;
+
+	if (msg && TO_SPI_BUS_DRIVER(bus->driver)->queue)
+	{
+		if (callback && !msg->status) {
+			msg->status = callback;
+			callback = NULL;
+		}
+
+		if (!callback) {
+			if (!msg->status) {
+				init_completion (&msg->sync);
+				msg->status = __spi_transfer_callback;
+				err = TO_SPI_BUS_DRIVER(bus->driver)->queue(msg);
+				wait_for_completion(&msg->sync);
+				err = 0;
+			} else {
+				err = TO_SPI_BUS_DRIVER(bus->driver)->queue(msg);
+			}
+		}
+	}
+
+	return err;
+}
+
+/**
+ * spi_write - send data to a device on an SPI bus
+ * @dev: the target device
+ * @flags: additional flags for message
+ * @buf: buffer to be sent
+ * @len: buffer's length
+ * @return: Returns the number of bytes transferred, or negative error code.
+ */
+int spi_write(struct spi_device *dev, u32 flags, char *buf, size_t len)
+{
+	struct spi_msg *msg = spimsg_alloc(dev, SPI_M_WR | SPI_M_DMAUNSAFE | flags, buf, len, NULL);
+	int ret;
+
+	ret = spi_transfer(msg, NULL);
+	return ret == 1 ? len : ret;
+}
+
+/**
+ * spi_read - receive data from a device on an SPI bus
+ * @dev: the target device
+ * @flags: additional flags for message
+ * @buf: buffer to be sent
+ * @len: buffer's length
+ * @return: Returns the number of bytes transferred, or negative error code.
+ */
+int spi_read(struct spi_device *dev, u32 flags, char *buf, size_t len)
+{
+	int ret;
+	struct spi_msg *msg = spimsg_alloc(dev, SPI_M_RD | SPI_M_DMAUNSAFE | flags, buf, len, NULL);
+
+	ret = spi_transfer(msg, NULL);
+	return ret == 1 ? len : ret;
+}
+
+/**
+ * spi_bus_populate - populate the bus
+ * @parent: the SPI bus device object
+ * @devices_s: array of structures that represents bus population
+ * @callback: optional pointer to function that called on each device's add
+ * Description:
+ * 	This function is intended to populate the SPI bus corresponding to
+ * 	device passed as 1st parameter. If some device cannot be added
+ * 	because of spi_device_add fail, the function will not try to parse
+ * 	the rest of list
+ */
+int spi_bus_populate(struct device *parent,
+			struct spi_device_desc* devices_s,
+			void (*callback) (struct device* bus,
+					  struct spi_device *new_dev,
+					  void* params))
+{
+	struct spi_device *new_device;
+	int count = 0;
+
+	while (devices_s->name) {
+		dev_dbg(parent, " discovered new SPI device, name '%s'\n",
+				devices_s->name);
+		if ((new_device = spi_device_add(parent, devices_s->name, devices_s->params)) == NULL)
+			break;
+		if (callback)
+			callback(parent, new_device, devices_s->params);
+		devices_s++;
+		count++;
+	}
+	return count;
+}
+
+/**
+ * spi_bus_reset - reset the spi bus
+ * @bus: device object that represents the SPI bus
+ * @context: u32 value to be passed to reset method of bus
+ * Description:
+ * 	This is simple wrapper for bus' `reset' method
+ */
+void spi_bus_reset (struct device *bus, u32 context)
+{
+	if (bus && bus->driver && TO_SPI_BUS_DRIVER(bus->driver)->reset)
+		TO_SPI_BUS_DRIVER(bus->driver)->reset(bus, context);
+}
+
+/*
+ * The functions below are wrappers for corresponding device_driver's methods
+ */
+static int spi_driver_probe (struct device *dev)
+{
+	struct spi_driver *sdrv = TO_SPI_DRIVER(dev->driver);
+	struct spi_device *sdev = TO_SPI_DEV(dev);
+
+	return sdrv->probe ? sdrv->probe(sdev) : -EFAULT;
+}
+
+static int spi_driver_remove (struct device *dev)
+{
+	struct spi_driver *sdrv = TO_SPI_DRIVER(dev->driver);
+	struct spi_device *sdev = TO_SPI_DEV(dev);
+
+	return  sdrv->remove  ? sdrv->remove(sdev) : -EFAULT;
+}
+
+static void spi_driver_shutdown (struct device *dev)
+{
+	struct spi_driver *sdrv = TO_SPI_DRIVER(dev->driver);
+	struct spi_device *sdev = TO_SPI_DEV(dev);
+
+	if (dev->driver && sdrv->shutdown)
+		sdrv->shutdown(sdev);
+}
+
+static int __init spi_core_init(void)
+{
+	int ret = spidev_init();
+
+	if (ret < 0)
+		goto out;
+
+	ret = spimsg_init();
+	if (ret < 0)
+		goto out;
+
+	ret = bus_register(&spi_bus);
+
+out:
+	return ret;
+}
+
+int spi_driver_add(struct spi_driver *drv)
+{
+	drv->driver.bus = &spi_bus;
+	drv->driver.probe = spi_driver_probe;
+	drv->driver.remove = spi_driver_remove;
+	drv->driver.shutdown = spi_driver_shutdown;
+	return driver_register(&drv->driver);
+}
+
+static void __exit spi_core_exit(void)
+{
+	bus_unregister(&spi_bus);
+	spimsg_exit();
+	spidev_cleanup();
+}
+
+subsys_initcall(spi_core_init);
+module_exit(spi_core_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <[email protected]>");
+
+EXPORT_SYMBOL_GPL(spi_bus_reset);
+EXPORT_SYMBOL_GPL(spi_device_add);
+EXPORT_SYMBOL_GPL(spi_driver_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);
+EXPORT_SYMBOL_GPL(spi_bus_driver_cleanup);
+
+EXPORT_SYMBOL_GPL(spimsg_alloc);
+EXPORT_SYMBOL_GPL(spimsg_chain);
+EXPORT_SYMBOL_GPL(spimsg_free);
+EXPORT_SYMBOL_GPL(spimsg_put_buffer);
+EXPORT_SYMBOL_GPL(spimsg_get_flags);
+EXPORT_SYMBOL_GPL(spimsg_get_buffer);
+EXPORT_SYMBOL_GPL(spimsg_get_clock);
+EXPORT_SYMBOL_GPL(spimsg_set_clock);
+EXPORT_SYMBOL_GPL(spimsg_getnext);
+EXPORT_SYMBOL_GPL(spimsg_get_spidev);
+EXPORT_SYMBOL_GPL(spimsg_set_ctx);
+EXPORT_SYMBOL_GPL(spimsg_get_ctx);
+EXPORT_SYMBOL_GPL(spimsg_complete);
+
Index: linux-2.6.orig/drivers/spi/spi-core.h
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-core.h
@@ -0,0 +1,32 @@
+/*
+ *  linux/drivers/spi/spi-core.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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.
+ *
+ */
+#ifndef __SPI_CORE_H
+#define __SPI_CORE_H
+
+struct spi_msg {
+	u32  flags;
+	u16 len;	/* msg length           */
+	u32 clock;
+	struct spi_device *device;
+	void *context;
+
+	struct completion sync;
+
+	struct spi_msg *next;
+
+	struct list_head link;
+
+	void (*status) (struct spi_msg * msg, int code);
+
+	void *buf_ptr;
+};
+
+#endif /* __SPI_CORE_H */
Index: linux-2.6.orig/drivers/spi/spi-dev.c
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-dev.c
@@ -0,0 +1,209 @@
+/*
+ *  drivers/spi/spi-dev.c
+ *
+ *  Character device interface for SPI
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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/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
+
+static struct class *spidev_class;
+
+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);
+
+/**
+ * spi_class_device_create - wrapper for class_device_create
+ * @minor: sequental minor number of SPI slave device
+ * @device: pointer to struct device embedded to spi_device
+ */
+struct class_device *spi_class_device_create(int minor, struct device *device)
+{
+	return class_device_create(spidev_class, NULL, MKDEV(SPI_MAJOR, minor), device, "spi%d", minor);
+}
+
+/**
+ * spi_class_device_destroy - wrapper for class_device_destroy
+ * @cdev: class device created by spi_class_device_create
+ */
+void spi_class_device_destroy(struct class_device *cdev)
+{
+	class_device_destroy(spidev_class, cdev->devt);
+}
+
+static struct file_operations spidev_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = spidev_read,
+	.write = spidev_write,
+	.open = spidev_open,
+	.release = spidev_release,
+};
+
+/**
+ * spidev_read - read from the SPI device
+ * @file: device file
+ * @buf: buffer to read into
+ * @count: number of bytes to read
+ * @offset: offset to read from
+ */
+static ssize_t spidev_read(struct file *file, char __user *buf, size_t count,
+			   loff_t * offset)
+{
+	int rc = 0;
+	char *kbuf = kmalloc(count, GFP_DMA);
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+
+	if (!kbuf)
+		rc = -ENOMEM;
+	else {
+		rc = spi_read(dev, SPI_M_DMAUNSAFE, kbuf, count);
+		if (rc < 0 || copy_to_user(buf, kbuf, count))
+			rc = -EFAULT;
+		kfree(kbuf);
+	}
+	return rc;
+}
+
+/**
+ * spidev_write - write to the SPI device
+ * @file: device file
+ * @buf: buffer to write the data from
+ * @count: number of bytes to write
+ * @offset: start offset to write to
+ */
+static ssize_t spidev_write(struct file *file, const char __user *buf, size_t count,
+			    loff_t * offset)
+{
+	int rc = 0;
+	char *kbuf = kmalloc(count, GFP_DMA);
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+
+	if (!kbuf)
+		rc = -ENOMEM;
+	else {
+		if (!copy_from_user(kbuf, buf, count))
+			rc = spi_write(dev, SPI_M_DMAUNSAFE, kbuf, count);
+		else
+			rc = -EFAULT;
+		kfree(kbuf);
+	}
+	return rc;
+}
+
+struct spidev_openclose {
+	unsigned int minor;
+	struct file *file;
+};
+
+/**
+ * spidev_do_open - open the SPI device
+ * @the_device: device structure
+ * @context: context of SPI device
+ */
+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);
+
+	pr_debug("device->minor = %d vs %d\n", dev->minor, o->minor);
+	if (dev->minor == o->minor) {
+		get_device(&dev->dev);
+		o->file->private_data = dev;
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * spidev_open - open the SPI bus device
+ * @inode: device inode
+ * @file: device file
+ */
+int spidev_open(struct inode *inode, struct file *file)
+{
+	struct spidev_openclose o;
+	int status;
+
+	o.minor = iminor(inode);
+	o.file = file;
+	status = bus_for_each_dev(&spi_bus, NULL, &o, spidev_do_open);
+	if (status == 0) {
+		status = -ENODEV;
+	}
+	return status < 0 ? status : 0;
+}
+
+/**
+ * spidev_release - release the SPI bus device
+ * @inode: device inode
+ * @file: device file
+ */
+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;
+}
+
+int __init spidev_init(void)
+{
+	int res;
+
+	if ((res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) != 0) {
+		goto out;
+	}
+
+	spidev_class = class_create(THIS_MODULE, "spi");
+	if (IS_ERR(spidev_class)) {
+		printk(KERN_ERR "%s: error creating class\n", __FUNCTION__);
+		res = -EINVAL;
+		goto out_unreg;
+	}
+
+	return 0;
+
+out_unreg:
+	unregister_chrdev(SPI_MAJOR, "spi");
+out:
+	return res;
+}
+
+void __exit spidev_cleanup(void)
+{
+	class_destroy(spidev_class);
+	unregister_chrdev(SPI_MAJOR, "spi");
+}
+
+MODULE_AUTHOR("dmitry pervushin <[email protected]>");
+MODULE_DESCRIPTION("SPI class device driver");
+MODULE_LICENSE("GPL");
Index: linux-2.6.orig/drivers/spi/spi-thread.c
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-thread.c
@@ -0,0 +1,187 @@
+/*
+ *  drivers/spi/spi-thread.c
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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>
+#include "spi-core.h"
+
+static int spi_thread(void *context);
+
+struct threaded_async_data {
+	atomic_t exiting;
+	struct device *dev;
+	struct task_struct *thread;
+	wait_queue_head_t wq;
+};
+
+/**
+ * __spi_start_async - start the thread
+ * @dev: device which the thread is related to
+ * @return: abstract pointer to the thread context
+ */
+void *__spi_start_async (struct device *dev)
+{
+	struct threaded_async_data *td = kmalloc (sizeof (struct threaded_async_data), GFP_KERNEL);
+
+	if (!td)
+		return NULL;
+
+	td->dev = dev;
+	atomic_set(&td->exiting, 0);
+	td->thread = kthread_run(spi_thread, td, "%s-work", dev->bus_id);
+	init_waitqueue_head(&td->wq);
+	return td;
+}
+
+/**
+ * __spi_stop_async - stop the thread
+ * @dev: device which the thread is related to
+ * @ctx: abstract pointer to the thread context
+ */
+void __spi_stop_async (struct device *dev, void *ctx)
+{
+	struct threaded_async_data *td = ctx;
+
+	if (ctx) {
+		atomic_inc (&td->exiting);
+		kthread_stop(td->thread);
+		kfree(td);
+	}
+}
+
+/**
+ * spi_thread_awake - function that called to determine if thread needs to process any messages
+ * @td: pointer to struct threaded_async_data
+ * Description:
+ * 	Thread wakes up if there is signal to exit (bd->exiting is set)
+ * 	or there are any messages in bus' queue.
+ */
+static int spi_thread_awake(struct threaded_async_data *td)
+{
+	int ret = -EINVAL;
+	struct spi_bus_data *bd = td->dev->platform_data;
+
+	if (atomic_read(&td->exiting)) {
+		return 1;
+	}
+
+	if (bd) {
+		spin_lock_irq(&bd->lock);
+		ret = !list_empty(&bd->msgs);
+		spin_unlock_irq(&bd->lock);
+	}
+	return ret;
+}
+
+/**
+ * spi_bus_next_msg - retrieve the next message
+ * @this: spi_bus_driver that needs to retrieve next message from queue
+ * @data: pointer to spi_bus_data structure associated with spi_bus_driver
+ */
+static struct spi_msg *spi_bus_next_msg(struct spi_bus_driver *this, struct spi_bus_data *data)
+{
+	return list_entry(data->msgs.next, struct spi_msg, link);
+}
+
+/**
+ * spi_thread - the thread that calls bus functions to perform actual transfers
+ * @context: pointer to struct spi_bus_data with bus-specific data
+ * Description:
+ * 	This function is started as separate thread to perform actual
+ * 	transfers on SPI bus
+ **/
+static int spi_thread(void *context)
+{
+	struct threaded_async_data *td = context;
+	struct spi_bus_data *bd = td->dev->platform_data;
+	struct spi_msg *cmsg = NULL;
+	int xfer_status;
+
+	while (!kthread_should_stop()) {
+
+		wait_event_interruptible(td->wq, spi_thread_awake(td));
+
+		if (atomic_read(&td->exiting))
+			goto thr_exit;
+
+		spin_lock_irq(&bd->lock);
+		while (!list_empty(&bd->msgs)) {
+			/*
+			 * this part is locked by bus_data->lock,
+			 * to protect spi_msg extraction
+			 */
+			cmsg = spi_bus_next_msg(bd->bus, bd);
+			list_del (&cmsg->link);
+
+			spin_unlock_irq(&bd->lock);
+
+			/*
+			 * and this part is locked by device's lock;
+			 * spi_queue will be able to queue new
+			 * messages
+			 *
+			 * note that bd->selected_device is locked,
+			 * not msg->device
+			 * they are the same, but msg can be freed in
+			 * msg->status function
+			 */
+			bd->selected_device = spimsg_get_spidev(cmsg);
+			spi_device_lock(bd->selected_device);
+			if (bd->bus->set_clock && cmsg->clock)
+				bd->bus->set_clock(cmsg->device->dev.parent,
+						cmsg->clock);
+			xfer_status = bd->bus->xfer(cmsg);
+			if (cmsg->status)
+				cmsg->status(cmsg, xfer_status);
+
+			spi_device_unlock(bd->selected_device);
+
+			/* lock the bus_data again... */
+			spin_lock_irq(&bd->lock);
+		}
+		spin_unlock_irq(&bd->lock);
+	}
+
+thr_exit:
+	return 0;
+}
+
+/**
+ * __spi_queue - (internal) queue the message to be processed asynchronously
+ * @msg: message to be sent
+ * Description:
+ * 	This function queues the message to spi bus driver's queue.
+ */
+int __spi_queue(struct spi_msg *msg)
+{
+	struct device *dev = &msg->device->dev;
+	struct spi_bus_data *pd = dev->parent->platform_data;
+	struct threaded_async_data *td = pd->async_data;
+
+	spin_lock_irq(&pd->lock);
+	list_add_tail(&msg->link, &pd->msgs);
+	dev_dbg(dev->parent, "message has been queued\n");
+	spin_unlock_irq(&pd->lock);
+	wake_up_interruptible(&td->wq);
+	return 0;
+}
+
+
Index: linux-2.6.orig/drivers/spi/spi-thread.h
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-thread.h
@@ -0,0 +1,42 @@
+/*
+ *  linux/drivers/spi/spi-thread.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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.
+ *
+ */
+#ifndef __SPI_THREAD_H
+#define __SPI_THREAD_H
+
+
+static inline void *spi_start_async (struct device *dev)
+{
+#ifdef CONFIG_SPI_THREAD
+	extern void *__spi_start_async (struct device *dev);
+	return  __spi_start_async(dev);
+#else
+	return 0;
+#endif
+}
+
+static inline void spi_stop_async (struct device *dev, void *t)
+{
+#ifdef CONFIG_SPI_THREAD
+	extern void __spi_stop_async (struct device *dev, void *t);
+	__spi_stop_async (dev, t);
+#endif
+}
+
+static inline int spi_queue (struct spi_msg *msg)
+{
+#ifdef CONFIG_SPI_THREAD
+	extern int __spi_queue (struct spi_msg *msg);
+	return __spi_queue(msg);
+#else
+	return -EINVAL;
+#endif
+}
+#endif /* __SPI_THREAD_H */
Index: linux-2.6.orig/include/linux/spi.h
===================================================================
--- /dev/null
+++ linux-2.6.orig/include/linux/spi.h
@@ -0,0 +1,252 @@
+/*
+ *  linux/include/linux/spi/spi.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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>
+#include <linux/device.h>
+
+struct spi_device;
+struct spi_driver;
+struct spi_msg;
+struct spi_bus_driver;
+
+extern struct bus_type spi_bus;
+
+struct spi_bus_data {
+	spinlock_t lock;
+	struct list_head msgs;
+	void *async_data;
+	wait_queue_head_t queue;
+	struct spi_device *selected_device;
+	struct spi_bus_driver *bus;
+	char *id;
+};
+
+#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 	(*set_clock) (struct device * bus_device, u32 clock_hz);
+	void 	(*reset) (struct device *bus_device, u32 context);
+
+	int	(*queue) (struct spi_msg *msg);
+	void   *(*start_async)( struct device *bus);
+	void 	(*stop_async)( struct device *bus, void *async);
+	void   *(*dma_safe_alloc)(void *buf, size_t len, int dir);
+	void    (*dma_safe_free)(void *buf, void *safe, size_t len, int dir);
+
+	struct device_driver driver;
+};
+
+#define TO_SPI_DEV(device) container_of(device, struct spi_device, dev)
+struct spi_device {
+	char name[BUS_ID_SIZE];
+	int minor;
+	struct class_device *cdev;
+	struct device dev;
+};
+
+static inline void *spi_device_get_busdata(struct spi_device *dev)
+{
+	return dev->dev.platform_data;
+}
+
+#define TO_SPI_DRIVER(drv) container_of(drv, struct spi_driver, driver)
+struct spi_driver {
+
+	int     (*probe)        (struct spi_device * dev);
+	int     (*remove)       (struct spi_device * dev);
+	void    (*shutdown)     (struct spi_device * dev);
+	int	(*suspend)	(struct spi_device * dev, pm_message_t pm);
+	int 	(*resume)	(struct spi_device * dev);
+
+	struct device_driver driver;
+};
+
+#define SPI_DEV_DRV(device)  TO_SPI_BUS_DRIVER((device)->dev.parent->driver)
+
+#define spi_device_lock(spi_dev)	 down(&(spi_dev)->dev.sem)
+#define spi_device_unlock(spi_dev)	 up(&(spi_dev)->dev.sem)
+
+#define SPI_M_RD	0x00000001
+#define SPI_M_WR	0x00000002	/**< Write mode flag */
+#define SPI_M_CSREL	0x00000004	/**< CS release level at end of the frame  */
+#define SPI_M_CS	0x00000008	/**< CS active level at begining of frame  */
+#define SPI_M_CSKEEP	0x00000010	/**< Don't change CS */
+#define SPI_M_CPOL	0x00000100	/**< Clock polarity */
+#define SPI_M_CPHA	0x00000200	/**< Clock Phase */
+#define SPI_M_DMAUNSAFE 0x10000000	/** buffer is dma-unsafe */
+
+void spimsg_set_ctx (struct spi_msg *message, void *ctx);
+void *spimsg_get_ctx (struct spi_msg *message);
+int spimsg_complete (struct spi_msg *message, int code);
+void spimsg_set_clock (struct spi_msg* message, u32 clock);
+u32 spimsg_get_clock (struct spi_msg* message);
+struct spi_device *spimsg_get_spidev (struct spi_msg* message);
+u32 spimsg_get_flags (struct spi_msg* message);
+u32 spimsg_get_buffer (struct spi_msg *message, void **buffer);
+void spimsg_put_buffer (struct spi_msg *message, void *buffer);
+struct spi_msg *spimsg_alloc(struct spi_device *device,
+			   u32 flags,
+			   void *buf,
+			   unsigned short len,
+			   void (*status) (struct spi_msg *,
+					   int code));
+struct spi_msg *spimsg_chain(struct spi_msg *msg,
+			   u32 flags,
+			   void *buf,
+			   unsigned short len,
+			   void (*status) (struct spi_msg *,
+					   int code));
+
+void spimsg_free (struct spi_msg *message);
+struct spi_msg *spimsg_getnext(struct spi_msg *message);
+
+#if defined (CONFIG_SPI_CHARDEV)
+extern struct class_device *spi_class_device_create(int minor, struct device *device);
+extern void spi_class_device_destroy(struct class_device *cdev);
+#else
+static inline struct class_device *spi_class_device_create(int minor, struct device *device)
+{
+	return NULL;
+}
+static inline void  spi_class_device_destroy(struct class_device *cdev)
+{
+}
+#endif
+
+enum {
+	SPIMSG_OK = 0,
+	SPIMSG_FAILED = -1,
+};
+
+#define SPI_MAJOR	153
+
+struct spi_driver;
+struct spi_device;
+
+#if defined (CONFIG_SPI_CHARDEV)
+extern int __init spidev_init(void);
+extern void __exit spidev_cleanup(void);
+#else
+static inline int spidev_init(void)
+{
+	return 0;
+}
+static inline void spidev_cleanup(void)
+{
+}
+#endif
+
+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_bus_driver_cleanup(struct spi_bus_driver *driver, struct device *dev);
+struct spi_device* spi_device_add(struct device *parent, char *name, void *private);
+
+extern int spi_driver_add(struct spi_driver *drv);
+
+static inline void spi_driver_del(struct spi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+
+extern void spi_bus_reset(struct device* bus, u32 context);
+extern int spi_write(struct spi_device *dev, u32 flags, char *buf, size_t len);
+extern int spi_read(struct spi_device *dev, u32 flags, char *buf, size_t len);
+
+extern int spi_transfer(struct spi_msg *message,
+			void (*status) (struct spi_msg *, int));
+struct spi_device_desc {
+	char* name;
+	void* params;
+};
+extern int spi_bus_populate(struct device *parent,
+			     struct spi_device_desc *devices,
+			     void (*assign) (struct device *parent,
+				             struct spi_device *,
+					     void *));
+
+static inline int spi_w8r8 (struct spi_device *dev, u8 wr)
+{
+	u8 byte;
+	int rc = -ENOMEM;
+	struct spi_msg *msg;
+
+	msg = spimsg_alloc(dev,
+			SPI_M_WR | SPI_M_CS | SPI_M_DMAUNSAFE,
+			&wr,
+			1,
+			NULL);
+	if (!msg)
+		goto out;
+	msg = spimsg_chain(msg,
+			SPI_M_RD | SPI_M_CSREL | SPI_M_DMAUNSAFE,
+			&byte,
+			1,
+			NULL);
+
+	if (!msg)
+		goto out;
+
+	rc = spi_transfer(msg, NULL);
+	spimsg_free(msg);
+
+out:
+	return rc < 0 ? rc : byte;
+}
+
+static inline int spi_w8r16 (struct spi_device *dev, u8 wr)
+{
+	u16 word;
+	int rc = -ENOMEM;
+	struct spi_msg *msg;
+
+	msg = spimsg_alloc(dev,
+			SPI_M_WR | SPI_M_CS | SPI_M_DMAUNSAFE,
+			&wr,
+			1,
+			NULL);
+	if (!msg)
+		goto out;
+	msg = spimsg_chain(msg,
+			SPI_M_RD | SPI_M_CSREL | SPI_M_DMAUNSAFE,
+			&word,
+			2,
+			NULL);
+
+	if (!msg)
+		goto out;
+
+	rc = spi_transfer(msg, NULL);
+	spimsg_free(msg);
+
+out:
+	return rc < 0 ? rc : word;
+}
+
+static inline int spi_sync(struct spi_msg *message)
+{
+	return spi_transfer(message, NULL);
+}
+
+static inline int spi_async(struct spi_msg *message, void (*status) (struct spi_msg *, int))
+{
+	return spi_transfer(message, status);
+}
+
+#endif				/* SPI_H */
Index: linux-2.6.orig/drivers/spi/spi-alloc.c
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-alloc.c
@@ -0,0 +1,101 @@
+/*
+ *  linux/drivers/spi/spi-alloc.c
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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/kmod.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/spi.h>
+#include <asm/atomic.h>
+#include "spi-core.h"
+
+#define SPIMSG_POOL_SIZE	0x10000
+
+static struct spi_msg_pool {
+	void *vaddr;
+	void *cur;
+	int count;
+} spimsg_pool;
+
+static spinlock_t spimsg_lock = SPIN_LOCK_UNLOCKED;
+
+struct spi_msg *__spimsg_kzalloc(void)
+{
+	struct spi_msg *msg = NULL;
+	unsigned long flags;
+
+	spin_lock_irqsave(&spimsg_lock, flags);
+	if (spimsg_pool.count > 4) {
+		msg = *(struct spi_msg **) spimsg_pool.cur;
+		*(void **)spimsg_pool.cur = **(void ***)spimsg_pool.cur;
+		memset(msg, 0, sizeof(*msg));
+		spimsg_pool.count--;
+	}
+	spin_unlock_irqrestore(&spimsg_lock, flags);
+
+	return msg;
+}
+
+void __spimsg_kfree(struct spi_msg *msg)
+{
+	unsigned long flags;
+
+	if (msg) {
+		if ((unsigned long)((long)msg - (long)spimsg_pool.vaddr) >
+				SPIMSG_POOL_SIZE) {
+			printk(KERN_ERR "Trying to free entry not from the SPI pool\n");
+			BUG();
+		}
+
+		spin_lock_irqsave(&spimsg_lock, flags);
+		*(long *)msg = *(long *)spimsg_pool.cur;
+		*(long *)spimsg_pool.cur = (long)msg;
+		spimsg_pool.count++;
+		spin_unlock_irqrestore(&spimsg_lock, flags);
+	}
+}
+
+int __spimsg_init(void)
+{
+	int ret = 0, i;
+
+	int size = ((sizeof(struct spi_msg) + 3) >> 2) << 2;
+
+	spimsg_pool.cur = spimsg_pool.vaddr =
+		kzalloc(SPIMSG_POOL_SIZE, SLAB_KERNEL);
+
+	if (!spimsg_pool.cur) {
+		printk(KERN_ERR "Couldn't allocate large buffer for SPI\n");
+		ret = -ENOMEM;
+		goto out;
+	}
+
+	spimsg_pool.count = SPIMSG_POOL_SIZE / size;
+	for (i = 0; i < spimsg_pool.count - 1; i++) {
+		void **addr = spimsg_pool.vaddr + i*size;
+		*addr = (void *)addr + size;
+	}
+	*(long *)(spimsg_pool.vaddr + (spimsg_pool.count - 1) * size) =
+	    (long)spimsg_pool.vaddr;
+
+out:
+	return ret;
+}
+
+void __spimsg_exit(void)
+{
+	if (spimsg_pool.vaddr)
+		kfree(spimsg_pool.vaddr);
+}
Index: linux-2.6.orig/drivers/spi/spi-alloc.h
===================================================================
--- /dev/null
+++ linux-2.6.orig/drivers/spi/spi-alloc.h
@@ -0,0 +1,54 @@
+/*
+ *  linux/linux/drivers/spi/spi-alloc.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <[email protected]>
+ *
+ * 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.
+ *
+ */
+#ifndef __SPI_ALLOC_H
+#define __SPI_ALLOC_H
+
+#include <linux/slab.h>
+
+static inline struct spi_msg *spimsg_kzalloc(void)
+{
+#ifdef CONFIG_SPI_CUSTOM_ALLOC
+	extern struct spi_msg *__spimsg_kzalloc(void);
+	return  __spimsg_kzalloc();
+#else
+	return kzalloc(sizeof(struct spi_msg), SLAB_KERNEL);
+#endif
+}
+
+static inline void spimsg_kfree(struct spi_msg *msg)
+{
+#ifdef CONFIG_SPI_CUSTOM_ALLOC
+	extern void __spimsg_kfree (struct spi_msg *msg);
+	__spimsg_kfree(msg);
+#else
+	kfree(msg);
+#endif
+}
+
+static inline int spimsg_init(void)
+{
+#ifdef CONFIG_SPI_CUSTOM_ALLOC
+	extern int __spimsg_init(void);
+	return __spimsg_init();
+#else
+	return 0;
+#endif
+}
+
+static inline void spimsg_exit(void)
+{
+#ifdef CONFIG_SPI_CUSTOM_ALLOC
+	extern void __spimsg_exit(void);
+	__spimsg_exit();
+#endif
+}
+
+#endif /* __SPI_ALLOC_H */
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux