All of lore.kernel.org
 help / color / mirror / Atom feed
From: Vitaly Wool <vwool@ru.mvista.com>
To: linux-kernel@vger.kernel.org
Cc: david-b@pacbell.net, dpervushin@gmail.com, akpm@osdl.org,
	greg@kroah.com, basicmark@yahoo.com, komal_shah802003@yahoo.com,
	stephen@streetfiresound.com,
	spi-devel-general@lists.sourceforge.net, Joachim_Jaeger@digi.com
Subject: [PATCH 2.6-git 1/4] SPI core refresh: SPI core patch
Date: Mon, 12 Dec 2005 18:22:49 +0300	[thread overview]
Message-ID: <20051212182249.018daa1b.vwool@ru.mvista.com> (raw)
In-Reply-To: <20051212182026.4e393d5a.vwool@ru.mvista.com>

Signed-off-by: Dmitry Pervushin <dpervushinl@gmail.com>
Signed-off-by: Vitaly Wool <vwool@ru.mvista.com>

 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   |  659 +++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-core.h   |   43 +++
 drivers/spi/spi-dev.c    |  209 ++++++++++++++
 drivers/spi/spi-thread.c |  187 +++++++++++++
 drivers/spi/spi-thread.h |   42 ++
 include/linux/spi.h      |  250 +++++++++++++++++
 14 files changed, 1749 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:anonymous@cvs.sourceforge.net:/cvsroot/spi-devel, i. e.
+$ export CVSROOT=:pserver:anonymous@cvs.sourceforge.net:/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:anonymous@cvs.sourceforge.net: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:
+	dpervushin@gmail.com
+	dimka@pervushin.msk.ru
+Visit our project page:
+	http://spi-devel.sourceforge.net
+Subscribe to mailing list:
+	spi-devel-general@lists.sourceforge.net
+
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,659 @@
+/*
+ *  drivers/spi/spi-core.c
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <sources@mvista.com>
+ *
+ * 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)
+{
+	if (!buffer)
+		return 0;
+	*buffer = message->buf_ptr;
+
+	if (message->flags & SPI_M_DMAUNSAFE) {
+
+		*buffer = kmalloc (message->len+sizeof(void*), SLAB_KERNEL);
+		if (!*buffer)
+			return 0;
+		if (message->flags & SPI_M_WR)
+			memcpy( *buffer, message->buf_ptr, message->len );
+	}
+	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)
+{
+	if (message->flags & SPI_M_DMAUNSAFE) {
+		if (message->flags & SPI_M_RD)
+			memcpy (message->buf_ptr, buffer, message->len);
+		kfree(buffer);
+	}
+}
+
+/**
+ * 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 && 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 && 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;
+	init_MUTEX(&pd->lock);
+	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 <dpervushin@ru.mvista.com>");
+
+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,43 @@
+/*
+ *  linux/drivers/spi/spi-core.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <sources@mvista.com>
+ *
+ * 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;
+#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_CPOL	0x00000010	/**< Clock polarity */
+#define SPI_M_CPHA	0x00000020	/**< Clock Phase */
+#define SPI_M_EXTBUF	0x80000000    	/** externally allocated buffers */
+#define SPI_M_ASYNC_CB	0x40000000      /** use workqueue to deliver callbacks */
+#define SPI_M_DNA	0x20000000	/** do not allocate buffers */
+#define SPI_M_DMAUNSAFE 0x10000000	/** buffer is dma-unsafe */
+
+	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 <sources@mvista.com>
+ *
+ * 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_DNA, 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_DNA, 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 <dpervushin@ru.mvista.com>");
+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 <sources@mvista.com>
+ *
+ * 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) {
+		down(&bd->lock);
+		ret = !list_empty(&bd->msgs);
+		up(&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;
+
+		down(&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);
+
+			up(&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... */
+			down(&bd->lock);
+		}
+		up(&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;
+
+	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(&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 <sources@mvista.com>
+ *
+ * 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,250 @@
+/*
+ *  linux/include/linux/spi/spi.h
+ *
+ *  Copyright (C) 2005 MontaVista Software, Inc <sources@mvista.com>
+ *
+ * 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 {
+	struct semaphore 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);
+
+	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 <sources@mvista.com>
+ *
+ * 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 <sources@mvista.com>
+ *
+ * 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 */

  reply	other threads:[~2005-12-12 15:22 UTC|newest]

Thread overview: 63+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-12-12 15:20 [PATCH 2.6-git 0/4] SPI core refresh Vitaly Wool
2005-12-12 15:22 ` Vitaly Wool [this message]
2005-12-12 15:49   ` [PATCH 2.6-git 1/4] SPI core refresh: SPI core patch Russell King
2005-12-12 15:24 ` [PATCH 2.6-git 2/4] SPI core refresh: MTD dataflash driver Vitaly Wool
2005-12-12 15:26 ` [PATCH 2.6-git 3/4] SPI core refresh: SPI/PNX controller Vitaly Wool
2005-12-12 15:27 ` [PATCH 2.6-git 4/4] SPI core refresh: dumb EEPROM driver Vitaly Wool
2005-12-12 18:01 ` [PATCH 2.6-git 0/4] SPI core refresh Rui Sousa
2005-12-13 12:09   ` [spi-devel-general] " dmitry pervushin
2005-12-13 15:11     ` Rui Sousa
2005-12-13 17:06       ` dmitry pervushin
2005-12-14  6:57       ` Vitaly Wool
2005-12-14 14:28         ` Rui Sousa
2005-12-13 16:35     ` David Brownell
2005-12-13 18:02       ` Rui Sousa
2005-12-13 14:06 ` [PATCH/RFC] SPI: add async message handing library to David Brownell's core Vitaly Wool
2005-12-13 16:53   ` [PATCH/RFC] SPI: add DMAUNSAFE analog " Vitaly Wool
2005-12-13 19:01     ` David Brownell
2005-12-13 19:15       ` Greg KH
2005-12-14 13:50         ` Vitaly Wool
2005-12-14 17:18           ` Greg KH
2005-12-14 17:53             ` Vitaly Wool
2005-12-14 18:50               ` [PATCH/RFC] SPI: add DMAUNSAFE analog David Brownell
2005-12-14 19:29                 ` Vitaly Wool
2005-12-14 19:02               ` [PATCH/RFC] SPI: add DMAUNSAFE analog to David Brownell's core David Brownell
2005-12-14 19:19                 ` Vitaly Wool
2005-12-14 19:33                   ` [spi-devel-general] Re: [PATCH/RFC] SPI: add DMAUNSAFE analog David Brownell
2005-12-14 19:34                 ` [PATCH/RFC] SPI: add DMAUNSAFE analog to David Brownell's core Vitaly Wool
2005-12-15  6:47                 ` Vitaly Wool
2005-12-15 16:44                   ` Greg KH
2005-12-15 22:23                     ` Vitaly Wool
2005-12-15 23:02                       ` Greg KH
2005-12-16  8:37                         ` Vitaly Wool
2005-12-16 17:34                           ` Greg KH
2005-12-16 18:32                             ` [spi-devel-general] Re: [PATCH/RFC] SPI: add DMAUNSAFE analog David Brownell
2005-12-15 20:06                   ` David Brownell
2005-12-15 22:17                     ` Vitaly Wool
2005-12-15 22:33                       ` Greg KH
2005-12-16  3:34                         ` Andy Isaacson
2005-12-16  5:17                           ` Greg KH
2005-12-14 19:16               ` [PATCH/RFC] SPI: add DMAUNSAFE analog to David Brownell's core Greg KH
2005-12-14 19:30                 ` Vitaly Wool
2005-12-15 10:00               ` [spi-devel-general] " dmitry pervushin
2005-12-14 17:22           ` David Brownell
2005-12-14 17:50             ` Vitaly Wool
2005-12-14 19:17               ` [PATCH/RFC] SPI: add DMAUNSAFE analog David Brownell
2005-12-14 20:11                 ` Vitaly Wool
2005-12-13 21:47       ` [PATCH/RFC] SPI: add DMAUNSAFE analog to David Brownell's core Vitaly Wool
2005-12-13 22:15       ` Vitaly Wool
2005-12-14 16:55         ` David Brownell
2005-12-14 17:23           ` Vitaly Wool
2005-12-14 18:48   ` [PATCH/RFC] SPI: add async message handing library " Stephen Street
2005-12-14 19:41     ` Vitaly Wool
2005-12-14 21:19       ` Stephen Street
2005-12-14 19:31   ` [PATCH/RFC] SPI: add async message handing library David Brownell
2005-12-15 12:19   ` [PATCH/RFC] SPI: async message handing library update Vitaly Wool
2005-12-18 18:59     ` David Brownell
2005-12-19 15:40       ` [spi-devel-general] " dmitry pervushin
2005-12-20  7:23         ` David Brownell
2005-12-20 18:02       ` Vitaly Wool
2005-12-22 17:28         ` David Brownell
2005-12-22 22:10           ` Vitaly Wool
2005-12-22 23:55             ` David Brownell
2005-12-21 13:17       ` Vitaly Wool

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20051212182249.018daa1b.vwool@ru.mvista.com \
    --to=vwool@ru.mvista.com \
    --cc=Joachim_Jaeger@digi.com \
    --cc=akpm@osdl.org \
    --cc=basicmark@yahoo.com \
    --cc=david-b@pacbell.net \
    --cc=dpervushin@gmail.com \
    --cc=greg@kroah.com \
    --cc=komal_shah802003@yahoo.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=spi-devel-general@lists.sourceforge.net \
    --cc=stephen@streetfiresound.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.