linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* SPI
@ 2005-09-26 11:12 dmitry pervushin
  2005-09-26 12:31 ` SPI Eric Piel
                   ` (4 more replies)
  0 siblings, 5 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-09-26 11:12 UTC (permalink / raw)
  To: Linux Kernel Mailing List; +Cc: spi-devel-general

Hello guys,

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

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

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



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: SPI
  2005-09-26 11:12 SPI dmitry pervushin
@ 2005-09-26 12:31 ` Eric Piel
  2005-09-26 12:37   ` [spi-devel-general] SPI dmitry pervushin
  2005-09-26 16:20 ` SPI Grant Likely
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 38+ messages in thread
From: Eric Piel @ 2005-09-26 12:31 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

09/26/2005 01:12 PM, dmitry pervushin wrote/a écrit:
> Hello guys,
> 
> I am attaching the next incarnation of SPI core; feel free to comment it.
Hello,

Very little comments...


> Index: linux-2.6.10/drivers/spi/Kconfig
> ===================================================================
> --- /dev/null
> +++ linux-2.6.10/drivers/spi/Kconfig
> @@ -0,0 +1,33 @@
> +#
> +# SPI device configuration
> +#
> +menu "SPI support"
> +
> +config SPI
> +	default Y
> +	tristate "SPI support"
> +        default false
> +	help
> +	  Say Y if you need to enable SPI support on your kernel
SPI is far from being well know, please put more help. At least define 
SPI as "Serial Peripheral Interface" and suggest the user to have a look 
at Documentation/spi.txt . IMHO, it's also convenient if you give the 
name of the module that will be created (spi?).


> Index: linux-2.6.10/Documentation/spi.txt
> ===================================================================
> --- /dev/null
> +++ linux-2.6.10/Documentation/spi.txt
:
:
> +
> +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/ .
Broken link, it is 
http://en.wikipedia.org/wiki/Serial_Peripheral_Interface (no trailing /)


Cheers,
Eric

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-26 12:31 ` SPI Eric Piel
@ 2005-09-26 12:37   ` dmitry pervushin
  0 siblings, 0 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-09-26 12:37 UTC (permalink / raw)
  To: Eric Piel; +Cc: Linux Kernel Mailing List, spi-devel-general

On Mon, 2005-09-26 at 14:31 +0200, Eric Piel wrote:
> > +	  Say Y if you need to enable SPI support on your kernel
> SPI is far from being well know, please put more help. At least define 
> SPI as "Serial Peripheral Interface" and suggest the user to have a look 
> at Documentation/spi.txt . IMHO, it's also convenient if you give the 
> name of the module that will be created (spi?).
The module name is spi-core. If one who configures the kernel does not
know about the SPI, it seems that it is better to keep the option
unchanged... However, I edit the text in Kconfig.
> Broken link, it is 
> http://en.wikipedia.org/wiki/Serial_Peripheral_Interface (no trailing /)
Fixed.

Thank you for comments,
dmitry 


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: SPI
  2005-09-26 11:12 SPI dmitry pervushin
  2005-09-26 12:31 ` SPI Eric Piel
@ 2005-09-26 16:20 ` Grant Likely
  2005-09-27  7:39   ` [spi-devel-general] SPI dmitry pervushin
  2005-09-26 16:25 ` SPI Valdis.Kletnieks
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 38+ messages in thread
From: Grant Likely @ 2005-09-26 16:20 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

resend; sorry.  Forgot to cc: the list.

On 9/26/05, dmitry pervushin <dpervushin@gmail.com> wrote:
> Hello guys,
>
> I am attaching the next incarnation of SPI core; feel free to comment it.

===================================================================
> --- /dev/null
> +++ linux-2.6.10/Documentation/spi.txt
> @@ -0,0 +1,351 @@
> +Documentation/spi.txt

> +3.1 The SPI outline
> +
> +The SPI infrastructure deals with several levels of abstraction. They are
> +"SPI bus", "SPI bus driver", "SPI device" and "SPI device driver". The
Would "SPI slave" or "SPI slave device" be better terminology than
"SPI device"?  That way the terminology matches how SPI hardware docs
are usually written.  (not a big deal, just thought I'd ask)

> +"SPI bus" is hardware device, which usually called "SPI adapter", and has
> +"SPI devices" connected. From the Linux' point of view, the "SPI bus" is
> +structure of type platform_device, and "SPI device" is structure of type
> +spi_device. The "SPI bus driver" is the driver which controls the whole
> +SPI bus (and, particularly, creates and destroys "SPI devices" on the bus),
> +and "SPI device driver" is driver that controls the only device on the SPI
> +bus, controlled by "SPI bus driver". "SPI device driver" can indirectly
> +call "SPI bus driver" to send/receive messages using API provided by SPI
> +core, and provide its own interface to the kernel and/or userland.
> +So, the device stack looks as follows:
> +
> +  +--------------+                    +---------+
> +  | platform_bus |                    | spi_bus |
> +  +--------------+                    +---------+
> +       |..|                                |
> +       |..|--------+               +---------------+
> +     +------------+| is parent to  |  SPI devices  |
> +     | SPI busses |+-------------> |               |
> +     +------------+                +---------------+
> +           |                               |
> +     +----------------+          +----------------------+
> +     | SPI bus driver |          |    SPI device driver |
> +     +----------------+          +----------------------+
Helpful diagram.  :)

> +
> +3.2 How do the SPI devices gets discovered and probed ?
> +
> +In general, the SPI bus driver cannot effective discover devices
> +on its bus. Fortunately, the devices on SPI bus usually implemented
> +onboard, so the following method has been chosen: the SPI bus driver
> +calls the function named spi_bus_populate and passed the `topology
> +string' to it. The function will parse the string and call the callback
> +for each device, just before registering it. This allows bus driver
> +to determine parameters like CS# for each device, retrieve them from
> +string and store somewhere like spi_device->platform_data. An example:
> +       err = spi_bus_populate( the_spi_bus,
> +                       "Dev1 0 1 2\0" "Dev2 2 1 0\0",
> +                       extract_name )
In my mind, this is not ideal.  For example, the MPC5200 has 4 PSC
ports which can be in SPI mode.  The SPI bus driver should/will not
know what devices are attached to it.  It should be the responsibility
of the board setup code to populate the bus.... on the other hand,
perhaps the bus driver should look to it's platform_device structure
to find a table of attached devices.  Generic platform_device parsing
code could be used by all SPI bus drivers.

Along the same lines, an SPI bus driver may not know the board
specific way SS lines are driven.  If GPIO is used as SS lines then
each SPI bus will need a different SS routine.  However, it looks like
this is not an issue for your infrastructure.  The individual SPI bus
driver can be handed a SS callback by the board setup code for each
SPI bus.

> +In this example, function like extract_name would put the '\0' on the
> +1st space of device's name, so names will become just "Dev1", "Dev2",
> +and the rest of string will become parameters of device.
I don't think it's wise to use '\0' as a delimiter.  Sure it makes
parsing really simple when the string passed in is formed correctly,
but if someone misses the last '\0' you have no way to know where the
string ends.  It also makes it difficult support passing a device
string from the kernel command line.

> +4. SPI functions are structures reference
> +-----------------------------------------
> +This section describes structures and functions that listed
> +in include/linux/spi.h
I would like to see this function and structure reference in the spi.h
file itself rather than here.  Better chance of it being kept up to
date that way.

> +
> +i. struct spi_msg
> +~~~~~~~~~~~~~~~~~
> +
> +struct spi_msg {
> +        unsigned char flags;
> +        unsigned short len;
> +        unsigned long clock;
> +        struct spi_device* device;
> +        void          *context;
> +        struct list_head link;
> +        void (*status)( struct spi_msg* msg, int code );
> +        void *devbuf_rd, *devbuf_wr;
> +        u8 *databuf_rd, *databuf_wr;
> +};
> +This structure represents the message that SPI device driver sends to the
> +SPI bus driver to handle.
Is there any way for the SPI device to constrain the clock rate for a
transfer?  For example, if the devices maximum speed is lower than the
bus maximum speed.

I looked over the code and I didn't notice anything obviously
incorrect.  I greatly appreciate the large block of documentation.

Overall, I like.  It looks like it does what I need it to.  If I get a
chance this week I'll port my SPI drivers to it and try it out on my
MPC5200 board.

Thanks!
g.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: SPI
  2005-09-26 11:12 SPI dmitry pervushin
  2005-09-26 12:31 ` SPI Eric Piel
  2005-09-26 16:20 ` SPI Grant Likely
@ 2005-09-26 16:25 ` Valdis.Kletnieks
  2005-09-26 16:46   ` [spi-devel-general] SPI Vitaly Wool
  2005-09-26 20:25 ` SPI Jesper Juhl
  2005-09-27 12:43 ` SPI Greg KH
  4 siblings, 1 reply; 38+ messages in thread
From: Valdis.Kletnieks @ 2005-09-26 16:25 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

[-- Attachment #1: Type: text/plain, Size: 744 bytes --]

On Mon, 26 Sep 2005 15:12:14 +0400, dmitry pervushin said:
> Hello guys,
> 
> I am attaching the next incarnation of SPI core; feel free to comment it.

> +/* The devfs code is contributed by Philipp Matthias Hahn 
> +   <pmhahn@titan.lahn.de> */

> +/* devfs code corrected to support automatic device addition/deletion
> +   by Vitaly Wool <vwool@ru.mvista.com> (C) 2004 MontaVista Software, Inc. 
> + */

I'd like to thank Vitaly and Philipp for their work, which was probably useful
at the time, but I've always wondered - when cleaning up code, should such comments
be removed too, or left as historical reminders?  The MAINTAINERS file seems
to get cleaned most of the time, the CREDITS doesn't - which way should
in-source comments go?


[-- Attachment #2: Type: application/pgp-signature, Size: 226 bytes --]

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-26 16:25 ` SPI Valdis.Kletnieks
@ 2005-09-26 16:46   ` Vitaly Wool
  0 siblings, 0 replies; 38+ messages in thread
From: Vitaly Wool @ 2005-09-26 16:46 UTC (permalink / raw)
  To: Valdis.Kletnieks
  Cc: dmitry pervushin, Linux Kernel Mailing List, spi-devel-general

My POV is that those lines should go away.

Best regards,
   Vitaly

Valdis.Kletnieks@vt.edu wrote:

>On Mon, 26 Sep 2005 15:12:14 +0400, dmitry pervushin said:
>  
>
>>Hello guys,
>>
>>I am attaching the next incarnation of SPI core; feel free to comment it.
>>    
>>
>
>  
>
>>+/* The devfs code is contributed by Philipp Matthias Hahn 
>>+   <pmhahn@titan.lahn.de> */
>>    
>>
>
>  
>
>>+/* devfs code corrected to support automatic device addition/deletion
>>+   by Vitaly Wool <vwool@ru.mvista.com> (C) 2004 MontaVista Software, Inc. 
>>+ */
>>    
>>
>
>I'd like to thank Vitaly and Philipp for their work, which was probably useful
>at the time, but I've always wondered - when cleaning up code, should such comments
>be removed too, or left as historical reminders?  The MAINTAINERS file seems
>to get cleaned most of the time, the CREDITS doesn't - which way should
>in-source comments go?
>
>  
>


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: SPI
  2005-09-26 11:12 SPI dmitry pervushin
                   ` (2 preceding siblings ...)
  2005-09-26 16:25 ` SPI Valdis.Kletnieks
@ 2005-09-26 20:25 ` Jesper Juhl
  2005-09-27 12:43 ` SPI Greg KH
  4 siblings, 0 replies; 38+ messages in thread
From: Jesper Juhl @ 2005-09-26 20:25 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

On Monday 26 September 2005 13:12, dmitry pervushin wrote:
> Hello guys,
> 
> I am attaching the next incarnation of SPI core; feel free to comment it.
> 
A few small style comments below.

General notes:
	Please use only tabs for indentation.
	Please get rid of all the trailing whitespace. A small sed script 
	 like this will do:  sed -r s/"[ \t]+$"/""/
	Please use only one statement pr line.
	Please get rid of the extra whitespace after opening paren and before
	 closing paren:  not like ( this ), but like (this).
	Please use a single space after if. Like this: if (foo), not if(foo).
	For pointer variables,  "type *name" is usually prefered, 
	 not "type* name" or "type * name".

See the changes I've made below for more details (note: I may have missed some
 bits, if so, please correct what I missed as well) :-)

See Documentation/CodingStyle for yet more details and rationale.



[snip]
> + */
> +static int spi_bus_match_name(struct device *dev, struct device_driver *drv)
> +{
> +	return  !strcmp (drv->name, SPI_DEV_CHAR) || 

	return  !strcmp(drv->name, SPI_DEV_CHAR) || 


[snip]
> + * Parameters:
> + * 	struct device* dev	the 'bus' device
> + * 	void* context		not used. Will be NULL

 * 	struct device *dev	the 'bus' device
 * 	void *context		not used. Will be NULL


[snip]
> +int __spi_bus_free(struct device *dev, void *context)
> +{
> +	struct spi_bus_data *pd = dev->platform_data;
> +
> +	atomic_inc(&pd->exiting);
> +	kthread_stop(pd->thread);
> +	kfree(pd);
> +
> +	dev_dbg( dev, "unregistering children\n" );

	dev_dbg(dev, "unregistering children\n");


> +	/* 
> +	 * NOTE: the loop below might needs redesign. Currently
> +	 *       we delete devices from the head of children list
> +	 *       until the list is empty; that's because the function
> +	 *       device_for_each_child will hold the semaphore needed 
> +	 *       for deletion of device
> +	 */
> +	while( !list_empty( &dev->children ) ) {
> +		struct device* child = list_entry ( dev->children.next, struct device, node );
> +	    	spi_device_del (TO_SPI_DEV (child) );

	while(!list_empty(&dev->children)) {
		struct device *child = list_entry(dev->children.next, struct device, node);
	    	spi_device_del(TO_SPI_DEV(child));


[snip]
> + * spi_bus_driver_unregister
> + *
> + * unregisters the SPI bus from the system. Before unregistering, it deletes
> + * each SPI device on the bus using call to __spi_device_free
> + *
> + * Parameters:
> + *  	struct spi_bus_driver* bus_driver	the bus driver

 *  	struct spi_bus_driver *bus_driver	the bus driver


[snip]
> +void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver)
> +{
> +	if (bus_driver) {
> +		driver_for_each_dev( &bus_driver->driver, NULL, __spi_bus_free);

		driver_for_each_dev(&bus_driver->driver, NULL, __spi_bus_free);


[snip]
> + * 	struct device* dev

 * 	struct device *dev

> + * Return value:
> + * 	none
> + */
> +void spi_device_release( struct device* dev )

void spi_device_release(struct device *dev)


[snip]
> + * 	struct device* parent		the 'bus' device
> + * 	struct spi_device* dev		new device to be added
> + * 	char* name			name of device. Should not be NULL

 * 	struct device *parent		the 'bus' device
 * 	struct spi_device *dev		new device to be added
 * 	char *name			name of device. Should not be NULL

[snip]
> +int spi_device_add(struct device *parent, struct spi_device *dev, char *name)
> +{
> +	if (!name || !dev) 
> +	    return -EINVAL;
> +	    
> +	memset(&dev->dev, 0, sizeof(dev->dev));
> +	dev->dev.parent = parent;
> +	dev->dev.bus = &spi_bus;
> +	strncpy( dev->name, name, sizeof(dev->name));
> +	strncpy( dev->dev.bus_id, name, sizeof( dev->dev.bus_id ) );

	strncpy(dev->dev.bus_id, name, sizeof(dev->dev.bus_id));


[snip]
> + * spi_queue
> + *
> + * Queue the message to be processed asynchronously
> + *
> + * Parameters:
> + *  	struct spi_msg* msg            message to be sent

 *  	struct spi_msg *msg            message to be sent


> + * Return value:
> + *  	0 on no errors, negative error code otherwise
> + */
> +int spi_queue( struct spi_msg *msg)

int spi_queue(struct spi_msg *msg)


> +{
> +	struct device* dev = &msg->device->dev;

	struct device *dev = &msg->device->dev;


> +	struct spi_bus_data *pd = dev->parent->platform_data;
> +
> +	down(&pd->lock);
> +	list_add_tail(&msg->link, &pd->msgs);
> +	dev_dbg(dev->parent, "message has been queued\n" );

	dev_dbg(dev->parent, "message has been queued\n");


[snip]
> + * __spi_transfer_callback
> + *
> + * callback for synchronously processed message. If spi_transfer determines
> + * that there is no callback provided neither by msg->status nor callback
> + * parameter, the __spi_transfer_callback will be used, and spi_transfer
> + * does not return until transfer is finished
> + *
> + * Parameters:
> + * 	struct spimsg* msg	message that is being processed now

 * 	struct spimsg *msg	message that is being processed now


> + * 	int code		status of processing
> + */
> +static void __spi_transfer_callback( struct spi_msg* msg, int code )

static void __spi_transfer_callback(struct spi_msg *msg, int code)



> +{
> +	if( code & (SPIMSG_OK|SPIMSG_FAILED) ) 
> +		complete( (struct completion*)msg->context );

	if (code & (SPIMSG_OK|SPIMSG_FAILED)) 
		complete((struct completion*)msg->context);

[snip]
> + * spi_transfer
> + *
> + * Process the SPI message, by queuing it to the driver and either
> + * immediately return or waiting till the end-of-processing
> + *
> + * Parameters:
> + * 	struct spi_msg* msg	message to process

 * 	struct spi_msg *msg	message to process


[snip]
> +int spi_transfer( struct spi_msg* msg, void (*callback)( struct spi_msg*, int ) )

int spi_transfer(struct spi_msg *msg, void (*callback)(struct spi_msg*, int))


> +{
> +	struct completion msg_done;
> +	int err = -EINVAL;
> +
> +	if( callback && !msg->status ) {

	if (callback && !msg->status) {


[snip]
> +	if( !callback ) {
> +		if( !msg->status ) {
> +			init_completion( &msg_done );
> +			msg->context = &msg_done;
> +			msg->status = __spi_transfer_callback;
> +			spi_queue( msg );
> +			wait_for_completion( &msg_done );
> +			err = 0;
> +		} else {
> +			err = spi_queue( msg );

	if (!callback) {
		if (!msg->status) {
			init_completion(&msg_done);
			msg->context = &msg_done;
			msg->status = __spi_transfer_callback;
			spi_queue(msg);
			wait_for_completion(&msg_done);
			err = 0;
		} else {
			err = spi_queue(msg);


[snip]
> + * spi_thread
> + * 
> + * This function is started as separate thread to perform actual 
> + * transfers on SPI bus
> + *
> + * Parameters:
> + *	void* context 		pointer to struct spi_bus_data 

 *	void *context 		pointer to struct spi_bus_data 


[snip]
> +	while (!kthread_should_stop()) {
> +
^^^^^ superfluous blank line.
> +		wait_event_interruptible(bd->queue, spi_thread_awake(bd));


[snip]
> +			if( bd->bus->set_clock && msg->clock )
> +				bd->bus->set_clock( 
> +					msg->device->dev.parent, msg->clock );
> +			xfer_status = bd->bus->xfer( msg );

			if (bd->bus->set_clock && msg->clock)
				bd->bus->set_clock(	<-- this line has trailing whitespace.
					msg->device->dev.parent, msg->clock);
			xfer_status = bd->bus->xfer(msg);



[snip]
> + * spi_write 
> + * 	send data to a device on an SPI bus
> + * Parameters:
> + * 	spi_device* dev		the target device
> + *	char* buf		buffer to be sent

 * 	spi_device *dev		the target device
 *	char *buf		buffer to be sent


[snip]
> +	ret = spi_transfer( msg, NULL );

	ret = spi_transfer(msg, NULL);


[snip]
> + * spi_write 
> + * 	receive data from a device on an SPI bus
> + * Parameters:
> + * 	spi_device* dev		the target device
> + *	char* buf		buffer to be sent

 * 	spi_device *dev		the target device
 *	char *buf		buffer to be sent


[snip]
> +int spi_read(struct spi_device *dev, char *buf, int len)
> +{
> +	int ret;
> +	struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL);
> +
> +	ret = spi_transfer( msg, NULL );

	ret = spi_transfer(msg, NULL);


[snip]
> +int spi_bus_populate(struct device *parent,
> +		     char *devices,
> +		     void (*callback) (struct device * bus,
> +				       struct spi_device * new_dev))

int spi_bus_populate(struct device *parent,
		char *devices,
		void (*callback)(struct device *bus,
			struct spi_device *new_dev))



[snip]
> +	while (devices[0]) {
> +		dev_dbg(parent, "discovered new SPI device, name '%s'\n",
> +			devices);
> +		new_device = kmalloc(sizeof(struct spi_device), GFP_KERNEL);
> +		if (!new_device) {
> +			break;
> +		}
> +		if (spi_device_add(parent, new_device, devices)) {
> +			break;
> +		}
> +		if (callback) {
> +			callback(parent, new_device);
> +		}

		if (!new_device)
			break;
		if (spi_device_add(parent, new_device, devices))
			break;
		if (callback)
			callback(parent, new_device);

We usually don't use curly braces for if statements when the body of the if
is only a single statement.


[snip]
> +int __init spi_core_init( void )

int __init spi_core_init(void)

[snip]
> +++ linux-2.6.10/drivers/spi/spi-dev.c
[snip]
[snip]
> +#include <linux/init.h>
> +#include <asm/uaccess.h>
> +#include <linux/spi.h>

#include <linux/init.h>
#include <linux/spi.h>
#include <asm/uaccess.h>

conventionally, asm/ includes are placed last.


[snip]
> +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 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);


[snip]
> +static int spidev_probe(struct device *dev)
> +{
> +	struct spidev_driver_data *drvdata;
> +
> +	drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL);
> +	if ( !drvdata) {
> +		dev_dbg( dev, "allocating drvdata failed\n" );

	if (!drvdata) {
		dev_dbg(dev, "allocating drvdata failed\n");


[snip]
> +	dev_dbg( dev, " added\n" );

	dev_dbg(dev, " added\n");


[snip]
> +static int spidev_remove(struct device *dev)
> +{
> +	struct spidev_driver_data *drvdata;
> +
> +	drvdata = (struct spidev_driver_data *)dev_get_drvdata(dev);
> +	class_simple_device_remove(MKDEV(SPI_MAJOR, drvdata->minor));
> +	kfree(drvdata);
> +	dev_dbg( dev, " removed\n" );

	dev_dbg(dev, " removed\n");


[snip]
> +static ssize_t spidev_read(struct file *file, char *buf, size_t count,
> +			   loff_t * offset)

static ssize_t spidev_read(struct file *file, char *buf, size_t count,
		loff_t *offset)


> +{
> +	struct spi_device *dev = (struct spi_device *)file->private_data;
> +	if( count > SPI_TRANSFER_MAX ) count = SPI_TRANSFER_MAX;
> +	return spi_read(dev, buf, count );

	if (count > SPI_TRANSFER_MAX)
		count = SPI_TRANSFER_MAX;
	return spi_read(dev, buf, count);


[snip]
> +static ssize_t spidev_write(struct file *file, const 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)


> +{
> +	struct spi_device *dev = (struct spi_device *)file->private_data;
> +	if( count > SPI_TRANSFER_MAX ) count = SPI_TRANSFER_MAX;
> +	return spi_write( dev, buf, count );

	if (count > SPI_TRANSFER_MAX)
		count = SPI_TRANSFER_MAX;
	return spi_write(dev, buf, count);


[snip]
> +	if (NULL == drvdata) {

	if (drvdata == NULL) {

debatable, but I believe the most common style is what I changed it to.


[snip]
> +++ linux-2.6.10/include/linux/spi.h
[snip]
> +/*
> + *  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

Blank line between end of comment and start of code at the top of file seems
to be most common.


[snip]
> +struct spi_bus_data
> +{
> +	struct semaphore lock;
> +	struct list_head msgs;
> +	atomic_t exiting;
> +	struct task_struct* thread;
> +	wait_queue_head_t queue; 
> +	struct spi_device* selected_device;
> +	struct spi_bus_driver* bus;

	struct task_struct *thread;
	wait_queue_head_t queue; 
	struct spi_device *selected_device;
	struct spi_bus_driver *bus;


[snip]
> +#define TO_SPI_BUS_DRIVER(drv) container_of( drv, struct spi_bus_driver, driver )

#define TO_SPI_BUS_DRIVER(drv) container_of(drv, struct spi_bus_driver, driver)


> +struct spi_bus_driver
> +{
> +	int (*xfer)( struct spi_msg* msg );
> +	void (*select)( struct spi_device* dev );
> +	void (*deselect)( struct spi_device* dev );
> +	void (*set_clock)( struct device* bus_device, u32 clock_hz );
> +	struct device_driver driver;

	int (*xfer)(struct spi_msg *msg);
	void (*select)(struct spi_device *dev);
	void (*deselect)(struct spi_device *dev);
	void (*set_clock)(struct device *bus_device, u32 clock_hz);


[snip]
> +#define TO_SPI_DEV(device) container_of( device, struct spi_device, dev )

#define TO_SPI_DEV(device) container_of(device, struct spi_device, dev)


> +struct spi_device 
> +{
> +	char name[ BUS_ID_SIZE ];

	char name[BUS_ID_SIZE];


[snip]
> +#define TO_SPI_DRIVER(drv) container_of( drv, struct spi_driver, driver )

#define TO_SPI_DRIVER(drv) container_of(drv, struct spi_driver, driver)


> +struct spi_driver {
> +    	void* (*alloc)( size_t, int );
> +    	void  (*free)( const void* );
> +    	unsigned char* (*get_buffer)( struct spi_device*, void* );
> +    	void (*release_buffer)( struct spi_device*, unsigned char*);
> +    	void (*control)( struct spi_device*, int mode, u32 ctl );

struct spi_driver {
    	void *(*alloc)(size_t, int);
    	void (*free)(const void *);
    	unsigned char *(*get_buffer)(struct spi_device *, void *);
    	void (*release_buffer)(struct spi_device *, unsigned char *);
    	void (*control)(struct spi_device *, int mode, u32 ctl);


[snip]
> +#define SPI_DEV_DRV( device )  TO_SPI_DRIVER( (device)->dev.driver )

#define SPI_DEV_DRV(device) TO_SPI_DRIVER((device)->dev.driver)

> +
> +#define spi_device_lock( dev ) /* down( dev->dev.sem ) */
> +#define spi_device_unlock( dev ) /* up( dev->dev.sem ) */

#define spi_device_lock(dev) /* down(dev->dev.sem) */
#define spi_device_unlock(dev) /* up(dev->dev.sem) */



> +/*
> + * struct spi_msg
> + *
> + * This structure represent the SPI message internally. You should never use fields of this structure directly
> + * Please use corresponding functions to create/destroy/access fields

 * This structure represent the SPI message internally.
 * You should never use fields of this structure directly.
 * Please use corresponding functions to create/destroy/access fields


[snip]
> +struct spi_msg {
> +	unsigned char flags;
> +#define SPI_M_RD	0x01
> +#define SPI_M_WR	0x02	/**< Write mode flag */
> +#define SPI_M_CSREL	0x04	/**< CS release level at end of the frame  */
> +#define SPI_M_CS	0x08	/**< CS active level at begining of frame ( default low ) */
> +#define SPI_M_CPOL	0x10	/**< Clock polarity */
> +#define SPI_M_CPHA	0x20	/**< Clock Phase */
> +	unsigned short len;	/* msg length           */
> +	unsigned long clock;
> +	struct spi_device* device;
> +	void	      *context;
> +	struct list_head link; 
> +	void (*status)( struct spi_msg* msg, int code );

#define SPI_M_WR	0x02	/* Write mode flag */
#define SPI_M_CSREL	0x04	/* CS release level at end of the frame */
#define SPI_M_CS	0x08	/* CS active level at begining of frame (default low) */
#define SPI_M_CPOL	0x10	/* Clock polarity */
#define SPI_M_CPHA	0x20	/* Clock Phase */
	unsigned short len;	/* msg length */
	unsigned long clock;
	struct spi_device *device;
	void *context;
	struct list_head link; 
	void (*status)(struct spi_msg *msg, int code);


[snip]
> +static inline struct spi_msg* spimsg_alloc( struct spi_device* device, 
> +					    unsigned flags,
> +					    unsigned short len, 
> +					    void (*status)( struct spi_msg*, int code ) )

static inline struct spi_msg* spimsg_alloc( struct spi_device* device, 
		unsigned flags, unsigned short len,
		void (*status)(struct spi_msg *, int code))

> +{
> +    struct spi_msg* msg;
> +    struct spi_driver* drv = SPI_DEV_DRV( device );
> +    
> +    msg = kmalloc( sizeof( struct spi_msg ), GFP_KERNEL );
> +    if( !msg )
> +	return NULL;
> +    memset( msg, 0, sizeof( struct spi_msg ) );

    struct spi_msg *msg;
    struct spi_driver *drv = SPI_DEV_DRV(device);
    
    msg = kmalloc(sizeof(struct spi_msg), GFP_KERNEL);
    if (!msg)
	return NULL;
    memset(msg, 0, sizeof(struct spi_msg));

In addition to the spacing changes you also seem to be using spaces for 
indentation here instead of tabs. Please use only tabs for indentation - this 
is true for other locations in the file as well, but I'm only mentioning it 
once here.


[snip]
> +    INIT_LIST_HEAD( &msg->link );
> +    if( flags & SPI_M_RD ) {
> +        msg->devbuf_rd =  drv->alloc ? 
> +	    drv->alloc( len, GFP_KERNEL ):kmalloc( len, GFP_KERNEL);
> +	msg->databuf_rd = drv->get_buffer ? 
> +	    drv->get_buffer( device, msg->devbuf_rd ) : msg->devbuf_rd;
> +    }

    INIT_LIST_HEAD(&msg->link);
    if (flags & SPI_M_RD) {
        msg->devbuf_rd =  drv->alloc ? 
	    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
	msg->databuf_rd = drv->get_buffer ? 
	    drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd;
    }


> +    if( flags & SPI_M_WR ) {
> +        msg->devbuf_wr =  drv->alloc ? 
> +	    drv->alloc( len, GFP_KERNEL ):kmalloc( len, GFP_KERNEL);
> +	msg->databuf_wr = drv->get_buffer ? 
> +	    drv->get_buffer( device, msg->devbuf_wr ) : msg->devbuf_wr;
> +    }

	if (flags & SPI_M_WR) {
		msg->devbuf_wr =  drv->alloc ? 
			drv->alloc(len, GFP_KERNEL) :
				kmalloc(len, GFP_KERNEL);
		msg->databuf_wr = drv->get_buffer ? 
			drv->get_buffer(device, msg->devbuf_wr) :
				msg->devbuf_wr;
    }


> +    pr_debug( "%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n",
> +		   __FUNCTION__,
> +		  msg, 
> +		  msg->devbuf_rd, msg->databuf_rd,
> +		  msg->devbuf_wr, msg->databuf_wr, 
> +		  msg->flags & SPI_M_RD ? "RD" : "~rd",
> +		  msg->flags & SPI_M_WR ? "WR" : "~wr" );

pr_debug("%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n",
	__FUNCTION__,
	msg, 
	msg->devbuf_rd, msg->databuf_rd,
	msg->devbuf_wr, msg->databuf_wr, 
	msg->flags & SPI_M_RD ? "RD" : "~rd",
	msg->flags & SPI_M_WR ? "WR" : "~wr");


[snip]
> +static inline void spimsg_free( struct spi_msg * msg )
> +{
> +    void (*do_free)( const void* ) = kfree;
> +    struct spi_driver* drv = SPI_DEV_DRV( msg->device );

static inline void spimsg_free(struct spi_msg *msg)
{
	void (*do_free)(const void *) = kfree;
	struct spi_driver *drv = SPI_DEV_DRV(msg->device);

> +    
> +    if( msg ) {
> +	    if( drv->free ) 
> +		do_free = drv->free;
> +	    if( drv->release_buffer ) {
> +		if( msg->databuf_rd) 
> +		    drv->release_buffer( msg->device, msg->databuf_rd );
> +    		if( msg->databuf_wr) 
> +		    drv->release_buffer( msg->device, msg->databuf_wr );
> +	}
> +	if( msg->devbuf_rd ) 
> +	    do_free( msg->devbuf_rd );
> +	if( msg->devbuf_wr)
> +	    do_free( msg->devbuf_wr );
> +	kfree( msg );
> +    }

	if (msg) {
		if (drv->free) 
			do_free = drv->free;
		if (drv->release_buffer) {
			if (msg->databuf_rd) 
				drv->release_buffer(msg->device, msg->databuf_rd);
			if (msg->databuf_wr) 
				drv->release_buffer( msg->device, msg->databuf_wr);
		}
		if (msg->devbuf_rd) 
			do_free( msg->devbuf_rd);
		if (msg->devbuf_wr)
			do_free( msg->devbuf_wr);
		kfree(msg);
	}




[snip]
> +static inline u8* spimsg_buffer_rd( struct spi_msg* msg ) 

static inline u8 *spimsg_buffer_rd(struct spi_msg *msg) 


[snip]
> +static inline u8* spimsg_buffer_wr( struct spi_msg* msg ) 

static inline u8 *spimsg_buffer_wr(struct spi_msg *msg) 


[snip]
> +static inline u8* spimsg_buffer( struct spi_msg* msg ) 
> +{
> +    if( !msg ) return NULL;
> +    if( ( msg->flags & (SPI_M_RD|SPI_M_WR) ) == (SPI_M_RD|SPI_M_WR) ) {
> +	printk( KERN_ERR"%s: what buffer do you really want ?\n", __FUNCTION__ );
> +	return NULL;
> +    }
> +    if( msg->flags & SPI_M_RD) return msg->databuf_rd;
> +    if( msg->flags & SPI_M_WR) return msg->databuf_wr;
> +}

static inline u8 *spimsg_buffer(struct spi_msg* msg)
{
	if (!msg)
		return NULL;
	if ((msg->flags & (SPI_M_RD|SPI_M_WR)) == (SPI_M_RD|SPI_M_WR)) {
		printk(KERN_ERR "%s: what buffer do you really want ?\n",
			__FUNCTION__ );
		return NULL;
	}
	if (msg->flags & SPI_M_RD)
		return msg->databuf_rd;
	if (msg->flags & SPI_M_WR)
		return msg->databuf_wr;
}


> +
> +#define SPIMSG_OK 	0x01
> +#define SPIMSG_FAILED 	0x80
> +#define SPIMSG_STARTED  0x02 
> +#define SPIMSG_DONE	0x04
> +
> +#define SPI_MAJOR	98

#define SPIMSG_OK	0x01
#define SPIMSG_FAILED	0x80
#define SPIMSG_STARTED	0x02 
#define SPIMSG_DONE	0x04

#define SPI_MAJOR	98

It may not be obvious what change I made here, so I'll tell you. You were
mixing spaces and tabs between the defined named and the value, I've changed
it to only use a single tab (it still lines up nicely).

[snip]
> +static inline int spi_bus_driver_register( struct spi_bus_driver* bus_driver )
> +{
> +	return driver_register( &bus_driver->driver );
> +}

static inline int spi_bus_driver_register(struct spi_bus_driver *bus_driver)
{
	return driver_register(&bus_driver->driver);
}

> +
> +void spi_bus_driver_unregister( struct spi_bus_driver* );
> +int spi_bus_driver_init( struct spi_bus_driver* driver, struct device* dev );
> +int spi_device_add( struct device* parent, struct spi_device*, char* name );

void spi_bus_driver_unregister(struct spi_bus_driver *);
int spi_bus_driver_init(struct spi_bus_driver * driver, struct device *dev);
int spi_device_add(struct device *parent, struct spi_device *, char *name);


> +static inline void spi_device_del( struct spi_device* dev )
> +{
> +	device_unregister( &dev->dev );
> +}

static inline void spi_device_del(struct spi_device *dev)
{
	device_unregister(&dev->dev);
}

> +static inline int spi_driver_add( struct spi_driver* drv ) 
> +{
> +	return driver_register( &drv->driver );
> +}

static inline int spi_driver_add(struct spi_driver *drv) 
{
	return driver_register(&drv->driver);
}

> +static inline void spi_driver_del( struct spi_driver* drv ) 
> +{
> +	driver_unregister( &drv->driver );
> +}

static inline void spi_driver_del(struct spi_driver *drv)
{
	driver_unregister(&drv->driver);
}


[snip]
> +extern int spi_queue( struct spi_msg* message );
> +extern int spi_transfer( struct spi_msg* message, void (*status)( struct spi_msg*, int ) );
> +extern int spi_bus_populate( struct device* parent, char* device, void (*assign)( struct device* parent, struct spi_device* ) );

extern int spi_queue(struct spi_msg* message);
extern int spi_transfer(struct spi_msg *message,
		void (*status)(struct spi_msg *, int));
extern int spi_bus_populate(struct device *parent, char *device,
		void (*assign)(struct device *parent, struct spi_device *));

> +
> +#endif				/* SPI_H */

#endif	/* SPI_H */



-- 
Jesper Juhl <jesper.juhl@gmail.com>




^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-26 16:20 ` SPI Grant Likely
@ 2005-09-27  7:39   ` dmitry pervushin
  0 siblings, 0 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-09-27  7:39 UTC (permalink / raw)
  To: Grant Likely; +Cc: Linux Kernel Mailing List, spi-devel-general

On Mon, 2005-09-26 at 10:20 -0600, Grant Likely wrote:
> Would "SPI slave" or "SPI slave device" be better terminology than
> "SPI device"?  That way the terminology matches how SPI hardware docs
> are usually written.  (not a big deal, just thought I'd ask)
The term "SPI slave device" looks correct.. I am correcting the doc :)
> > +       err = spi_bus_populate( the_spi_bus,
> > +                       "Dev1 0 1 2\0" "Dev2 2 1 0\0",
> > +                       extract_name )
> In my mind, this is not ideal.  For example, the MPC5200 has 4 PSC
> ports which can be in SPI mode.  The SPI bus driver should/will not
> know what devices are attached to it.  It should be the responsibility
> of the board setup code to populate the bus.... on the other hand,
> perhaps the bus driver should look to it's platform_device structure
> to find a table of attached devices.  Generic platform_device parsing
> code could be used by all SPI bus drivers.
The spi_bus_populate is not the only way to populate the bus; the bus
driver can discover SPI devices on his own and directly call
spi_device_add, isn't it ?
> > +In this example, function like extract_name would put the '\0' on the
> > +1st space of device's name, so names will become just "Dev1", "Dev2",
> > +and the rest of string will become parameters of device.
> I don't think it's wise to use '\0' as a delimiter.  Sure it makes
> parsing really simple when the string passed in is formed correctly,
> but if someone misses the last '\0' you have no way to know where the
> string ends.  It also makes it difficult support passing a device
> string from the kernel command line.
You're right. Using spi_populate_bus is the simplest way, that may lead
to errors... From the other hand, if we used another char to delimit
device name and its parameters, there would be person who would want
this character in device name... I think that we can add another
approach to populate the bus ? 
> 
> > +4. SPI functions are structures reference
> > +-----------------------------------------
> > +This section describes structures and functions that listed
> > +in include/linux/spi.h
> I would like to see this function and structure reference in the spi.h
> file itself rather than here.  Better chance of it being kept up to
> date that way.
Yes; but I personally prefer to look to the only place instead of
spi.h/spi-core.c. I'll try to keep the things consistent :)

> > +This structure represents the message that SPI device driver sends to the
> > +SPI bus driver to handle.
> Is there any way for the SPI device to constrain the clock rate for a
> transfer?  For example, if the devices maximum speed is lower than the
> bus maximum speed.
Thank you for this comment; the `clock' field is initially intended to
do this. Device driver might set the field to maximum speed, and bus
driver would analyze the field in its xfer function and send the message
on lower speed. Moreover, there is the `set_clock' callback in
spi_bus_driver. If msg specifies its own clock value, the bus driver's
set_clock will be called just before transferring the message.
> Overall, I like.  It looks like it does what I need it to.  If I get a
> chance this week I'll port my SPI drivers to it and try it out on my
> MPC5200 board.
Thank you! If your drivers are going to open source, could you also sent
them to spi mailing list, to prepare the consolidated patch ? I hope if
there is no significant troubles, the current core will go to the
mainstream kernel :)



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: SPI
  2005-09-26 11:12 SPI dmitry pervushin
                   ` (3 preceding siblings ...)
  2005-09-26 20:25 ` SPI Jesper Juhl
@ 2005-09-27 12:43 ` Greg KH
  2005-09-27 14:27   ` [spi-devel-general] SPI dmitry pervushin
  4 siblings, 1 reply; 38+ messages in thread
From: Greg KH @ 2005-09-27 12:43 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

On Mon, Sep 26, 2005 at 03:12:14PM +0400, dmitry pervushin wrote:
> +/*
> + * spi_device_release
> + * 
> + * Pointer to this function will be put to dev->release place
> + * This function gets called as a part of device removing
> + * 
> + * Parameters:
> + * 	struct device* dev
> + * Return value:
> + * 	none
> + */
> +void spi_device_release( struct device* dev )
> +{
> +/* just a placeholder */
> +}

This is ALWAYS wrong, please fix your code.  See the many times I have
been over this issue in the archives.

Also, please fix your coding style to match the kernel if you wish to
have a chance to get it included. :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-27 12:43 ` SPI Greg KH
@ 2005-09-27 14:27   ` dmitry pervushin
  2005-09-27 14:35     ` Greg KH
  0 siblings, 1 reply; 38+ messages in thread
From: dmitry pervushin @ 2005-09-27 14:27 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Kernel Mailing List, spi-devel-general

On Tue, 2005-09-27 at 05:43 -0700, Greg KH wrote:
> This is ALWAYS wrong, please fix your code.  See the many times I have
> been over this issue in the archives.
Do you mean this comment ? The spi_device_release does nothing, just to
prevent compains from device_release function :)
> 
> Also, please fix your coding style to match the kernel if you wish to
> have a chance to get it included. :)
Hmm... running Lindent... done. Thank you once more, for your valuable
comments :)



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-27 14:27   ` [spi-devel-general] SPI dmitry pervushin
@ 2005-09-27 14:35     ` Greg KH
  2005-09-27 14:49       ` dmitry pervushin
  0 siblings, 1 reply; 38+ messages in thread
From: Greg KH @ 2005-09-27 14:35 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

On Tue, Sep 27, 2005 at 06:27:16PM +0400, dmitry pervushin wrote:
> On Tue, 2005-09-27 at 05:43 -0700, Greg KH wrote:
> > This is ALWAYS wrong, please fix your code.  See the many times I have
> > been over this issue in the archives.
> Do you mean this comment ? The spi_device_release does nothing, just to
> prevent compains from device_release function :)

Think about why the kernel complains about a non-existant release
function.  Just replacing that complaint with a function that does
nothing does NOT solve the issue, it only makes the warning go away.

Please read up on how the lifetime rules work for devices, and what
needs to happen in the release function (hint, take a look at other
busses, like USB and PCI for examples of what needs to be done.)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-27 14:35     ` Greg KH
@ 2005-09-27 14:49       ` dmitry pervushin
  2005-09-27 14:54         ` Greg KH
  0 siblings, 1 reply; 38+ messages in thread
From: dmitry pervushin @ 2005-09-27 14:49 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Kernel Mailing List, spi-devel-general

On Tue, 2005-09-27 at 07:35 -0700, Greg KH wrote:
> Please read up on how the lifetime rules work for devices, and what
> needs to happen in the release function (hint, take a look at other
> busses, like USB and PCI for examples of what needs to be done.)
As far as I can see, pci_release_device deletes the pci_dev using kfree.
But here we have statically allocated spi_device structures --
spi_device_add does not allocate spi_device, but uses caller-allocated
one.
> 
> thanks,
> 
> greg k-h
> 
> 
> -------------------------------------------------------
> SF.Net email is sponsored by:
> Tame your development challenges with Apache's Geronimo App Server. Download
> it for free - -and be entered to win a 42" plasma tv or your very own
> Sony(tm)PSP.  Click here to play: http://sourceforge.net/geronimo.php
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general@lists.sourceforge.net
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general
> 
> 


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-27 14:49       ` dmitry pervushin
@ 2005-09-27 14:54         ` Greg KH
  2005-09-27 15:19           ` dmitry pervushin
  2005-09-28 13:14           ` [PATCH] SPI dmitry pervushin
  0 siblings, 2 replies; 38+ messages in thread
From: Greg KH @ 2005-09-27 14:54 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List, spi-devel-general

On Tue, Sep 27, 2005 at 06:49:57PM +0400, dmitry pervushin wrote:
> On Tue, 2005-09-27 at 07:35 -0700, Greg KH wrote:
> > Please read up on how the lifetime rules work for devices, and what
> > needs to happen in the release function (hint, take a look at other
> > busses, like USB and PCI for examples of what needs to be done.)
> As far as I can see, pci_release_device deletes the pci_dev using kfree.

Yes.

> But here we have statically allocated spi_device structures --
> spi_device_add does not allocate spi_device, but uses caller-allocated
> one.

Not good, reference counted structures almost always should be
dynamically created.  Please change this to also be true for SPI,
otherwise you will have a lot of nasty issues with devices that can be
removed at any point in time.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [spi-devel-general] Re: SPI
  2005-09-27 14:54         ` Greg KH
@ 2005-09-27 15:19           ` dmitry pervushin
  2005-09-28 13:14           ` [PATCH] SPI dmitry pervushin
  1 sibling, 0 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-09-27 15:19 UTC (permalink / raw)
  To: Greg KH; +Cc: Linux Kernel Mailing List, spi-devel-general

On Tue, 2005-09-27 at 07:54 -0700, Greg KH wrote:
> Not good, reference counted structures almost always should be
> dynamically created.  Please change this to also be true for SPI,
> otherwise you will have a lot of nasty issues with devices that can be
> removed at any point in time.
Hmm. I thought a bit about this and it seems reasonable. I'll change
this to dynamic allocation. 

> thanks,
> 
> greg k-h


^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH] SPI
  2005-09-27 14:54         ` Greg KH
  2005-09-27 15:19           ` dmitry pervushin
@ 2005-09-28 13:14           ` dmitry pervushin
  1 sibling, 0 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-09-28 13:14 UTC (permalink / raw)
  To: Greg KH, akpm; +Cc: Linux Kernel Mailing List, spi-devel-general

Hello people,

Here is the revised SPI-core patch. Changes are as follows:
- spi_device_add now allocates and returns struct spi_device* instead of
taking caller-allocated spi_device* as parameter;
- spi_device_release changed to deallocate spi_device* allocated by call
to spi_device_add;
- new function spi_bus_populate2; it populates bus in the same manner as
spi_bus_populate does, but parameter is array of struct spi_device_desc
instead of string delimited by '\0's;
- code style fixes;
- documentation fixes.

-----------------8<- cut here ------------------------------------
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 systems
running Linux.

 Documentation/spi.txt  |  374 ++++++++++++++++++++++++++++++++++++
 arch/arm/Kconfig       |    2
 drivers/Kconfig        |    2
 drivers/Makefile       |    1
 drivers/spi/Kconfig    |   33 +++
 drivers/spi/Makefile   |   14 +
 drivers/spi/spi-core.c |  506 +++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi-dev.c  |  219 +++++++++++++++++++++
 include/linux/spi.h    |  232 ++++++++++++++++++++++

Signed-off-by: dmitry pervushin <dpervushin@ru.mvista.com>

PATCH FOLLOWS
Index: linux-2.6.10/arch/arm/Kconfig
===================================================================
--- linux-2.6.10.orig/arch/arm/Kconfig
+++ linux-2.6.10/arch/arm/Kconfig
@@ -834,6 +834,8 @@ source "drivers/ssi/Kconfig"
 
 source "drivers/mmc/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 endmenu
 
 source "ktools/Kconfig"
Index: linux-2.6.10/drivers/Kconfig
===================================================================
--- linux-2.6.10.orig/drivers/Kconfig
+++ linux-2.6.10/drivers/Kconfig
@@ -42,6 +42,8 @@ source "drivers/char/Kconfig"
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/misc/Kconfig"
Index: linux-2.6.10/drivers/Makefile
===================================================================
--- linux-2.6.10.orig/drivers/Makefile
+++ linux-2.6.10/drivers/Makefile
@@ -67,3 +67,4 @@ obj-$(CONFIG_DPM)		+= dpm/
 obj-$(CONFIG_MMC)		+= mmc/
 obj-y				+= firmware/
 obj-$(CONFIG_EVENT_BROKER)	+= evb/
+obj-$(CONFIG_SPI)		+= spi/
Index: linux-2.6.10/drivers/spi/Kconfig
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/Kconfig
@@ -0,0 +1,33 @@
+#
+# SPI device configuration
+#
+menu "SPI support"
+
+config SPI
+	default Y
+	tristate "SPI (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-core loadable module.
+
+config SPI_DEBUG
+	bool "SPI debug output"
+	depends on SPI
+	default false
+	help
+          Say Y there if you'd like to see debug output from SPI drivers
+	  If unsure, say N
+
+config SPI_CHARDEV
+	default Y
+	tristate "SPI device interface"
+	depends on SPI
+	help
+	  Say Y here to use /dev/spiNN device files. They make it possible to have user-space
+	  programs use the SPI bus.
+	  This support is also available as a module.  If so, the module
+	  will be called spi-dev.
+
+endmenu
+
Index: linux-2.6.10/drivers/spi/Makefile
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/Makefile
@@ -0,0 +1,14 @@
+#
+# Makefile for the kernel spi bus driver.
+#
+
+obj-$(CONFIG_SPI) += spi-core.o
+# bus drivers
+# ...functional drivers
+# ...and the common spi-dev driver
+obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o
+
+ifeq ($(CONFIG_SPI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
Index: linux-2.6.10/drivers/spi/spi-core.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spi-core.c
@@ -0,0 +1,506 @@
+/*
+ *  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>
+
+static int spi_thread(void *context);
+
+/*
+ * spi_bus_match_name
+ *
+ * Drivers and devices on SPI bus are matched by name, just like the
+ * platform devices, with exception of SPI_DEV_CHAR. Driver with this name
+ * will be matched against any device
+ */
+static int spi_bus_match_name(struct device *dev, struct device_driver *drv)
+{
+	return !strcmp(drv->name, SPI_DEV_CHAR) ||
+	    !strcmp(TO_SPI_DEV(dev)->name, drv->name);
+}
+
+struct bus_type spi_bus = {
+	.name = "spi",
+	.match = spi_bus_match_name,
+};
+
+/*
+ * spi_bus_driver_init
+ *
+ * This function initializes the spi_bus_data structure for the
+ * bus. Functions has to be called when bus driver gets probed
+ *
+ * Parameters:
+ * 	spi_bus_driver* 	pointer to bus driver structure
+ * 	device*			platform device to be attached to
+ * Return value:
+ * 	0 on success, error code otherwise
+ */
+int spi_bus_driver_init(struct spi_bus_driver *bus, struct device *dev)
+{
+	struct spi_bus_data *pd =
+	    kmalloc(sizeof(struct spi_bus_data), GFP_KERNEL);
+	int err = 0;
+
+	if (!pd) {
+		err = -ENOMEM;
+		goto init_failed_1;
+	}
+	atomic_set(&pd->exiting, 0);
+	pd->bus = bus;
+	init_MUTEX(&pd->lock);
+	INIT_LIST_HEAD(&pd->msgs);
+	init_waitqueue_head(&pd->queue);
+	pd->thread = kthread_run(spi_thread, pd, "%s-work", dev->bus_id);
+	if (IS_ERR(pd->thread)) {
+		err = PTR_ERR(pd->thread);
+		goto init_failed_2;
+	}
+	dev->platform_data = pd;
+	return 0;
+
+init_failed_2:
+	kfree(pd);
+      init_failed_1:
+	return err;
+}
+
+/*
+ * __spi_bus_free
+ *
+ * This function called as part of unregistering bus device driver. It
+ * calls spi_device_del for each child (SPI) device on the bus
+ *
+ * Parameters:
+ * 	struct device* dev	the 'bus' device
+ * 	void* context		not used. Will be NULL
+ */
+int __spi_bus_free(struct device *dev, void *context)
+{
+	struct spi_bus_data *pd = dev->platform_data;
+
+	atomic_inc(&pd->exiting);
+	kthread_stop(pd->thread);
+	kfree(pd);
+
+	dev_dbg(dev, "unregistering children\n");
+	/*
+	 * NOTE: the loop below might needs redesign. Currently
+	 *       we delete devices from the head of children list
+	 *       until the list is empty; that's because the function
+	 *       device_for_each_child will hold the semaphore needed
+	 *       for deletion of device
+	 */
+	while (!list_empty(&dev->children)) {
+		struct device *child =
+		    list_entry(dev->children.next, struct device, node);
+		spi_device_del(TO_SPI_DEV(child));
+	}
+	return 0;
+}
+
+/*
+ * spi_bus_driver_unregister
+ *
+ * unregisters the SPI bus from the system. Before unregistering, it deletes
+ * each SPI device on the bus using call to __spi_device_free
+ *
+ * Parameters:
+ *  	struct spi_bus_driver* bus_driver	the bus driver
+ * Return value:
+ *  	void
+ */
+void spi_bus_driver_unregister(struct spi_bus_driver *bus_driver)
+{
+	if (bus_driver) {
+		driver_for_each_dev(&bus_driver->driver, NULL, __spi_bus_free);
+		driver_unregister(&bus_driver->driver);
+	}
+}
+
+/*
+ * spi_device_release
+ *
+ * Pointer to this function will be put to dev->release place
+ * This function gets called as a part of device removing
+ *
+ * Parameters:
+ * 	struct device* dev
+ * Return value:
+ * 	none
+ */
+void spi_device_release(struct device *dev)
+{
+	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
+ *
+ * Parameters:
+ * 	struct device* parent		the 'bus' device
+ * 	char* name			name of device. Should not be NULL
+ * Return value:
+ * 	pointer to allocated spi_device structure; NULL on error
+ */
+struct spi_device* spi_device_add(struct device *parent, char *name)
+{
+	struct spi_device* dev;
+
+	if (!name)
+		goto dev_add_out;
+
+	dev = kmalloc(sizeof(struct spi_device), GFP_KERNEL);
+	if( !dev )
+		goto dev_add_out;
+
+	memset(&dev->dev, 0, sizeof(dev->dev));
+	dev->dev.parent = parent;
+	dev->dev.bus = &spi_bus;
+	strncpy(dev->name, name, sizeof(dev->name));
+	strncpy(dev->dev.bus_id, name, sizeof(dev->dev.bus_id));
+	dev->dev.release = spi_device_release;
+
+	if (device_register(&dev->dev)<0) {
+		dev_dbg(parent, " device '%s' cannot be added\n", name);
+		goto dev_add_out_2;
+	}
+	return dev;
+
+dev_add_out_2:
+	kfree(dev);
+dev_add_out:
+	return NULL;
+}
+
+/*
+ * spi_queue
+ *
+ * Queue the message to be processed asynchronously
+ *
+ * Parameters:
+ *  	struct spi_msg* msg            message to be sent
+ * Return value:
+ *  	0 on no errors, negative error code otherwise
+ */
+int spi_queue(struct spi_msg *msg)
+{
+	struct device *dev = &msg->device->dev;
+	struct spi_bus_data *pd = dev->parent->platform_data;
+
+	down(&pd->lock);
+	list_add_tail(&msg->link, &pd->msgs);
+	dev_dbg(dev->parent, "message has been queued\n");
+	up(&pd->lock);
+	wake_up_interruptible(&pd->queue);
+	return 0;
+}
+
+/*
+ * __spi_transfer_callback
+ *
+ * callback for synchronously processed message. If spi_transfer determines
+ * that there is no callback provided neither by msg->status nor callback
+ * parameter, the __spi_transfer_callback will be used, and spi_transfer
+ * does not return until transfer is finished
+ *
+ * Parameters:
+ * 	struct spimsg* msg	message that is being processed now
+ * 	int code		status of processing
+ */
+static void __spi_transfer_callback(struct spi_msg *msg, int code)
+{
+	if (code & (SPIMSG_OK | SPIMSG_FAILED))
+		complete((struct completion *)msg->context);
+}
+
+/*
+ * spi_transfer
+ *
+ * Process the SPI message, by queuing it to the driver and either
+ * immediately return or waiting till the end-of-processing
+ *
+ * Parameters:
+ * 	struct spi_msg* msg	message to process
+ * 	callback		user-supplied callback. If both msg->status and
+ * 				callback are set, the error code of -EINVAL
+ * 				will be returned
+ * Return value:
+ * 	0 on success, error code otherwise. This code does not reflect
+ * 	status of message, just status of queueing
+ */
+int spi_transfer(struct spi_msg *msg, void (*callback) (struct spi_msg *, int))
+{
+	struct completion msg_done;
+	int err = -EINVAL;
+
+	if (callback && !msg->status) {
+		msg->status = callback;
+		callback = NULL;
+	}
+
+	if (!callback) {
+		if (!msg->status) {
+			init_completion(&msg_done);
+			msg->context = &msg_done;
+			msg->status = __spi_transfer_callback;
+			spi_queue(msg);
+			wait_for_completion(&msg_done);
+			err = 0;
+		} else {
+			err = spi_queue(msg);
+		}
+	}
+
+	return err;
+}
+
+/*
+ * spi_thread
+ *
+ * This function is started as separate thread to perform actual
+ * transfers on SPI bus
+ *
+ * Parameters:
+ *	void* context 		pointer to struct spi_bus_data
+ */
+static int spi_thread_awake(struct spi_bus_data *bd)
+{
+	int ret;
+
+	if (atomic_read(&bd->exiting)) {
+		return 1;
+	}
+	down(&bd->lock);
+	ret = !list_empty(&bd->msgs);
+	up(&bd->lock);
+	return ret;
+}
+
+static int spi_thread(void *context)
+{
+	struct spi_bus_data *bd = context;
+	struct spi_msg *msg;
+	int xfer_status;
+	int found;
+
+	while (!kthread_should_stop()) {
+
+		wait_event_interruptible(bd->queue, spi_thread_awake(bd));
+
+		if (atomic_read(&bd->exiting))
+			goto thr_exit;
+
+		down(&bd->lock);
+		while (!list_empty(&bd->msgs)) {
+			/*
+			 * this part is locked by bus_data->lock,
+			 * to protect spi_msg extraction
+			 */
+			found = 0;
+			list_for_each_entry(msg, &bd->msgs, link) {
+				if (!bd->selected_device) {
+					bd->selected_device = msg->device;
+					if (bd->bus->select)
+						bd->bus->select(bd->
+								selected_device);
+					found = 1;
+					break;
+				}
+				if (msg->device == bd->selected_device) {
+					found = 1;
+					break;
+				}
+			}
+			if (!found) {
+				/*
+				 * all messages for current selected_device
+				 * are processed.
+				 * let's switch to another device
+				 */
+				msg =
+				    list_entry(bd->msgs.next, struct spi_msg,
+					       link);
+				if (bd->bus->deselect)
+					bd->bus->deselect(bd->selected_device);
+				bd->selected_device = msg->device;
+				if (bd->bus->select)
+					bd->bus->select(bd->selected_device);
+			}
+			list_del(&msg->link);
+			up(&bd->lock);
+
+			/*
+			 * and this part is locked by device's lock;
+			 * spi_queue will be able to queue new
+			 * messages
+			 */
+			spi_device_lock(&msg->device);
+			if (msg->status)
+				msg->status(msg, SPIMSG_STARTED);
+			if (bd->bus->set_clock && msg->clock)
+				bd->bus->set_clock(msg->device->dev.parent,
+						   msg->clock);
+			xfer_status = bd->bus->xfer(msg);
+			if (msg->status) {
+				msg->status(msg, SPIMSG_DONE);
+				msg->status(msg,
+					    xfer_status ? SPIMSG_OK :
+					    SPIMSG_FAILED);
+			}
+			spi_device_unlock(&msg->device);
+
+			/* lock the bus_data again... */
+			down(&bd->lock);
+		}
+		if (bd->bus->deselect)
+			bd->bus->deselect(bd->selected_device);
+		bd->selected_device = NULL;
+		/* device has been just deselected, unlocking the bus */
+		up(&bd->lock);
+	}
+thr_exit:
+	return 0;
+}
+
+/*
+ * spi_write
+ * 	send data to a device on an SPI bus
+ * Parameters:
+ * 	spi_device* dev		the target device
+ *	char* buf		buffer to be sent
+ *	int len			buffer length
+ * Return:
+ * 	the number of bytes transferred, or negative error code.
+ */
+int spi_write(struct spi_device *dev, const char *buf, int len)
+{
+	struct spi_msg *msg = spimsg_alloc(dev, SPI_M_WR, len, NULL);
+	int ret;
+
+	memcpy(spimsg_buffer_wr(msg), buf, len);
+	ret = spi_transfer(msg, NULL);
+	return ret == 1 ? len : ret;
+}
+
+/*
+ * spi_write
+ * 	receive data from a device on an SPI bus
+ * Parameters:
+ * 	spi_device* dev		the target device
+ *	char* buf		buffer to be sent
+ *	int len			number of bytes to receive
+ * Return:
+ * 	 the number of bytes transferred, or negative error code.
+ */
+int spi_read(struct spi_device *dev, char *buf, int len)
+{
+	int ret;
+	struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL);
+
+	ret = spi_transfer(msg, NULL);
+	memcpy(buf, spimsg_buffer_rd(msg), len);
+	return ret == 1 ? len : ret;
+}
+
+/*
+ * spi_bus_populate and spi_bus_populate2
+ *
+ * These two functions intended to populate the SPI bus corresponding to
+ * device passed as 1st parameter. The difference is in the way to describe
+ * new SPI slave devices: the spi_bus_populate takes the ASCII string delimited
+ * by '\0', where each section matches one SPI device name _and_ its parameters,
+ * and the spi_bus_populate2 takes the array of structures spi_device_desc.
+ *
+ * If some device cannot be added because of spi_device_add fail, the function will
+ * not try to parse the rest of list
+ *
+ * Return:
+ * 	the number of devices that were successfully added
+ */
+int spi_bus_populate(struct device *parent,
+		     char *devices,
+		     void (*callback) (struct device * bus,
+				       struct spi_device * new_dev))
+{
+	struct spi_device *new_device;
+	int count = 0;
+
+	while (devices[0]) {
+		dev_dbg(parent, " discovered new SPI device, name '%s'\n",
+			devices);
+		if ((new_device = spi_device_add(parent, devices)) == NULL)
+			break;
+		if (callback)
+			callback(parent, new_device);
+		devices += (strlen(devices) + 1);
+		count++;
+	}
+	return count;
+}
+
+int spi_bus_populate2(struct device *parent,
+			struct spi_device_desc* devices,
+			void (*callback) (struct device* bus,
+					  struct spi_device *new_dev,
+					  void* params))
+{
+	struct spi_device *new_device;
+	int count = 0;
+
+	while (devices->name) {
+		dev_dbg(parent, " discovered new SPI device, name '%s'\n",
+				devices->name );
+		if ((new_device = spi_device_add(parent, devices->name)) == NULL)
+			break;
+		if (callback)
+			callback(parent, new_device, devices->params);
+		devices++;
+		count++;
+	}
+	return count;
+}
+
+int __init spi_core_init(void)
+{
+	return bus_register(&spi_bus);
+}
+
+subsys_initcall(spi_core_init);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("dmitry pervushin <dpervushin@ru.mvista.com>");
+
+EXPORT_SYMBOL_GPL(spi_queue);
+EXPORT_SYMBOL_GPL(spi_device_add);
+EXPORT_SYMBOL_GPL(spi_bus_driver_unregister);
+EXPORT_SYMBOL_GPL(spi_bus_populate);
+EXPORT_SYMBOL_GPL(spi_bus_populate2);
+EXPORT_SYMBOL_GPL(spi_transfer);
+EXPORT_SYMBOL_GPL(spi_write);
+EXPORT_SYMBOL_GPL(spi_read);
+EXPORT_SYMBOL_GPL(spi_bus);
+EXPORT_SYMBOL_GPL(spi_bus_driver_init);
Index: linux-2.6.10/drivers/spi/spi-dev.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spi-dev.c
@@ -0,0 +1,219 @@
+/*
+    spi-dev.c - spi driver, char device interface
+
+    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, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/init.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/version.h>
+#include <linux/smp_lock.h>
+
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <linux/spi.h>
+
+#define SPI_TRANSFER_MAX	65535L
+
+struct spidev_driver_data {
+	int minor;
+};
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+			   loff_t * offset);
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+			    loff_t * offset);
+
+static int spidev_open(struct inode *inode, struct file *file);
+static int spidev_release(struct inode *inode, struct file *file);
+static int __init spidev_init(void);
+
+static void spidev_cleanup(void);
+
+static int spidev_probe(struct device *dev);
+static int spidev_remove(struct device *dev);
+
+static struct file_operations spidev_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = spidev_read,
+	.write = spidev_write,
+	.open = spidev_open,
+	.release = spidev_release,
+};
+
+static struct class_simple *spidev_class;
+
+static struct spi_driver spidev_driver = {
+	.driver = {
+		   .name = SPI_DEV_CHAR,
+		   .probe = spidev_probe,
+		   .remove = spidev_remove,
+		   },
+};
+
+static int spidev_minor;
+
+static int spidev_probe(struct device *dev)
+{
+	struct spidev_driver_data *drvdata;
+
+	drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL);
+	if (!drvdata) {
+		dev_dbg(dev, "allocating drvdata failed\n");
+		return -ENOMEM;
+	}
+
+	drvdata->minor = spidev_minor++;
+	dev_dbg(dev, "setting device's(%p) minor to %d\n", dev, drvdata->minor);
+	dev_set_drvdata(dev, drvdata);
+
+	class_simple_device_add(spidev_class,
+				MKDEV(SPI_MAJOR, drvdata->minor),
+				NULL, "spi%d", drvdata->minor);
+	dev_dbg(dev, " added\n");
+	return 0;
+}
+
+static int spidev_remove(struct device *dev)
+{
+	struct spidev_driver_data *drvdata;
+
+	drvdata = (struct spidev_driver_data *)dev_get_drvdata(dev);
+	class_simple_device_remove(MKDEV(SPI_MAJOR, drvdata->minor));
+	kfree(drvdata);
+	dev_dbg(dev, " removed\n");
+	return 0;
+}
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+			   loff_t * offset)
+{
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+	if (count > SPI_TRANSFER_MAX)
+		count = SPI_TRANSFER_MAX;
+	return spi_read(dev, buf, count);
+}
+
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+			    loff_t * offset)
+{
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+	if (count > SPI_TRANSFER_MAX)
+		count = SPI_TRANSFER_MAX;
+	return spi_write(dev, buf, count);
+}
+
+struct spidev_openclose {
+	unsigned int minor;
+	struct file *file;
+};
+
+static int spidev_do_open(struct device *the_dev, void *context)
+{
+	struct spidev_openclose *o = (struct spidev_openclose *)context;
+	struct spi_device *dev = TO_SPI_DEV(the_dev);
+	struct spidev_driver_data *drvdata;
+
+	drvdata = (struct spidev_driver_data *)dev_get_drvdata(the_dev);
+	if (!drvdata) {
+		pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__);
+		goto do_open_fail;
+	}
+
+	pr_debug("drvdata->minor = %d vs %d\n", drvdata->minor, o->minor);
+	if (drvdata->minor == o->minor) {
+		get_device(&dev->dev);
+		o->file->private_data = dev;
+		return 1;
+	}
+
+do_open_fail:
+	return 0;
+}
+
+int spidev_open(struct inode *inode, struct file *file)
+{
+	struct spidev_openclose o;
+	int status;
+
+	o.minor = iminor(inode);
+	o.file = file;
+	status = driver_for_each_dev(&spidev_driver.driver, &o, spidev_do_open);
+	if (status == 0) {
+		status = -ENODEV;
+	}
+	return status < 0 ? status : 0;
+}
+
+static int spidev_release(struct inode *inode, struct file *file)
+{
+	struct spi_device *dev = file->private_data;
+
+	if (dev)
+		put_device(&dev->dev);
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static int __init spidev_init(void)
+{
+	int res;
+
+	if ((res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) != 0) {
+		goto out;
+	}
+
+	spidev_class = class_simple_create(THIS_MODULE, "spi");
+	if (IS_ERR(spidev_class)) {
+		printk(KERN_ERR "%s: error creating class\n", __FUNCTION__);
+		res = -EINVAL;
+		goto out_unreg;
+	}
+
+	if ((res = spi_driver_add(&spidev_driver)) != 0)
+		goto out_unreg;
+
+	printk("SPI /dev entries driver.\n");
+	return 0;
+
+out_unreg:
+	unregister_chrdev(SPI_MAJOR, "spi");
+out:
+	printk(KERN_ERR "%s: Driver initialization failed\n", __FILE__);
+	return res;
+}
+
+static void spidev_cleanup(void)
+{
+	spi_driver_del(&spidev_driver);
+	class_simple_destroy(spidev_class);
+	unregister_chrdev(SPI_MAJOR, "spi");
+}
+
+MODULE_AUTHOR("dmitry pervushin <dpervushin@ru.mvista.com>");
+MODULE_DESCRIPTION("SPI /dev entries driver");
+MODULE_LICENSE("GPL");
+
+module_init(spidev_init);
+module_exit(spidev_cleanup);
Index: linux-2.6.10/Documentation/spi.txt
===================================================================
--- /dev/null
+++ linux-2.6.10/Documentation/spi.txt
@@ -0,0 +1,374 @@
+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:
+
+  +--------------+                    +---------+
+  | platform_bus |                    | spi_bus |
+  +--------------+                    +---------+
+       |..|                                |
+       |..|--------+               +---------------+
+     +------------+| is parent to  |  SPI devices  |
+     | SPI busses |+-------------> |               |
+     +------------+                +---------------+
+           |                               |
+     +----------------+          +----------------------+
+     | SPI bus driver |          |    SPI device driver |
+     +----------------+          +----------------------+
+
+3.2 How do the SPI devices gets discovered and probed ?
+
+In general, the SPI bus driver cannot effective discover devices
+on its bus. Fortunately, the devices on SPI bus usually implemented
+onboard, so the following method has been chosen: the SPI bus driver
+calls the function named spi_bus_populate and passed the `topology
+string' to it. The function will parse the string and call the callback
+for each device, just before registering it. This allows bus driver
+to determine parameters like CS# for each device, retrieve them from
+string and store somewhere like spi_device->platform_data. An example:
+	err = spi_bus_populate( the_spi_bus,
+			"Dev1 0 1 2\0" "Dev2 2 1 0\0",
+			extract_name )
+In this example, function like extract_name would put the '\0' on the
+1st space of device's name, so names will become just "Dev1", "Dev2",
+and the rest of string will become parameters of device.
+
+The another way is to create array of structures spi_device_desc and pass
+this array to function spi_bus_populate2, like this:
+  struct spi_device_desc spi_slaves[] = {
+    [0] = {
+	.name = "device1",
+        .param = device1_params,
+    },
+    [1] = {
+        .name = "device2",
+        .param = NULL,
+    }
+    [2] = {
+	NULL, NULL
+    };
+  spi_bus_populate2( the_spi_bus, spi_slaves, callback );
+
+3.3. DMA and SPI messages
+-------------------------
+
+To handle DMA transfers on SPI bus, any device driver might provide special
+callbacks to allocate/free/get access to buffer. These callbacks are defined
+in subsection iii of section 4.
+To send data using DMA, the buffers should be allocated using
+dma_alloc_coherent function. Usually buffers are allocated statically or
+using kmalloc function.
+To allow drivers to allocate buffers in non-standard
+When one allocates the structure for spi message, it needs to provide target
+device. If its driver wants to allocate buffer in driver-specific way, it may
+provide its own allocation/free methods: alloc and free. If driver does not
+provide these methods, kmalloc and kfree will be used.
+After allocation, the buffer must be accessed to copy the buffer to be send
+or retrieve buffer that has been just received from device. If buffer was
+allocated using driver's alloc method, it(buffer) will be accessed using
+get_buffer. Driver should provide accessible buffer that corresponds buffer
+allocated by driver's alloc method. If there is no get_buffer method,
+the result of alloc will be used.
+After reading/writing from/to buffer, it will be released by call to driver's
+release_buffer method.
+
+
+4. SPI functions are structures reference
+-----------------------------------------
+This section describes structures and functions that listed
+in include/linux/spi.h
+
+i. struct spi_msg
+~~~~~~~~~~~~~~~~~
+
+struct spi_msg {
+        unsigned char flags;
+        unsigned short len;
+        unsigned long clock;
+        struct spi_device* device;
+        void          *context;
+        struct list_head link;
+        void (*status)( struct spi_msg* msg, int code );
+        void *devbuf_rd, *devbuf_wr;
+        u8 *databuf_rd, *databuf_wr;
+};
+This structure represents the message that SPI device driver sends to the
+SPI bus driver to handle.
+Fields:
+	flags 	combination of message flags
+		SPI_M_RD	"read" operation (from device to host)
+		SPI_M_WR	"write" operation (from host to device)
+		SPI_M_CS	assert the CS signal before sending the message
+		SPI_M_CSREL	clear the CS signal after sending the message
+		SPI_M_CSPOL	set clock polarity to high
+		SPI_M_CPHA	set clock phase to high
+	len	length, in bytes, of allocated buffer
+	clock	reserved, set to zero
+	device	the target device of the message
+	context	user-defined field; to associate any user data with the message
+	link	used by bus driver to queue messages
+	status	user-provided callback function to inform about message flow
+	devbuf_rd, devbuf_wr
+		so-called "device buffers". These buffers allocated by the
+		device driver, if device driver provides approproate callback.
+		Otherwise, the kmalloc API will be used.
+	databuf_rd, databuf_wr
+		pointers to access content of device buffers. They are acquired
+		using get_buffer callback, if device driver provides one.
+		Otherwise, they are just pointers to corresponding
+		device buffers
+
+struct spi_msg* spimsg_alloc( struct spi_device* device,
+                              unsigned flags,
+                              unsigned short len,
+                              void (*status)( struct spi_msg*, int code ) )
+This functions is called to allocate the spi_msg structure and set the
+corresponding fields in structure. If device->platform_data provides callbacks
+to handle buffers, alloc/get_buffer are to be used. Returns NULL on errors.
+
+struct void spimsg_free( struct spi_msg* msg )
+Deallocate spi_msg as well as internal buffers. If msg->device->platform_data
+provides callbacks to handle buffers, release_buffer and free are to be used.
+
+u8* spimsg_buffer_rd( struct spi_msg* msg )
+u8* spimsg_buffer_wr( struct spi_msg* msg )
+u8* spimsg_buffer( struct spi_msg* )
+Return the corresponding data buffer, which can be directly modified by driver.
+spimsg_buffer checks flags and return either databuf_rd or databuf_wr basing on
+value of `flags' in spi_msg structure.
+
+ii. struct spi_device
+~~~~~~~~~~~~~~~~~~~~~
+
+struct spi_device
+{
+        char name[ BUS_ID_SIZE ];
+        struct device dev;
+};
+This structure represents the physical device on SPI bus. The SPI bus driver
+will create and register this structure for you.
+	name	the name of the device. It should match to the SPI device
+		driver name
+	dev	field used to be registered with core
+
+struct spi_device* spi_device_add( struct device* parent,
+                    		   char* name )
+This function registers the device on the spi bus, and set its parent
+to `parent', which represents the SPI bus. The device name will be set to name,
+that should be non-empty, non-NULL string. Returns pointer to allocated
+spi_device structure, NULL on error.
+
+void spi_device_del( struct spi_device* dev )
+Unregister the SPI device. Return value is ignored
+
+iii. struct spi_driver
+~~~~~~~~~~~~~~~~~~~~~~
+
+struct spi_driver {
+    	void* (*alloc)( size_t, int );
+    	void  (*free)( const void* );
+    	unsigned char* (*get_buffer)( struct spi_device*, void* );
+    	void (*release_buffer)( struct spi_device*, unsigned char*);
+    	void (*control)( struct spi_device*, int mode, u32 ctl );
+        struct device_driver driver;
+};
+This structure represents the SPI device driver object. Before registering,
+all fields of driver sub-structure should be properly filled, e.g., the
+`bus_type' should be set to spi_bus. Otherwise, the driver will be incorrectly
+registered and its callbacks might never been called. An example of will-
+formed spi_driver structure:
+	extern struct bus_type spi_bus;
+	static struct spi_driver pnx4008_eeprom_driver = {
+        	.driver = {
+                   	.bus = &spi_bus,
+                   	.name = "pnx4008-eeprom",
+                   	.probe = pnx4008_spiee_probe,
+                   	.remove = pnx4008_spiee_remove,
+                   	.suspend = pnx4008_spiee_suspend,
+                   	.resume = pnx4008_spiee_resume,
+               	},
+};
+The method control gets called during the processing of SPI message.
+For detailed description of malloc/free/get_buffer/release_buffer, please
+look to section 3.3, "DMA and SPI messages"
+
+
+int spi_driver_add( struct spi_driver* driver )
+Register the SPI device driver with core; returns 0 on no errors, error code
+otherwise.
+
+void spi_driver_del( struct spi_driver* driver )
+Unregisters the SPI device driver; return value ignored.
+
+iv. struct spi_bus_driver
+~~~~~~~~~~~~~~~~~~~~~~~~~
+To handle transactions over the SPI bus, the spi_bus_driver structure must
+be prepared and registered with core. Any transactions, either synchronous
+or asynchronous, go through spi_bus_driver->xfer function.
+
+struct spi_bus_driver
+{
+        int (*xfer)( struct spi_msg* msgs );
+        void (*select) ( struct spi_device* arg );
+        void (*deselect)( struct spi_device* arg );
+
+        struct device_driver driver;
+};
+
+Fields:
+	xfer	pointer to function to execute actual transaction on SPI bus
+		msg	message to handle
+	select	pointer to function that gets called when bus needs to
+		select another device to be target of transfers
+	deselect
+		pointer to function that gets called before another device
+		is selected to be the target of transfers
+
+
+spi_bus_driver_register( struct spi_bus_driver* )
+
+Register the SPI bus driver with the system. The driver sub-structure should
+be properly filled before using this function, otherwise you may get unpredi-
+ctable results when trying to exchange data. An example of correctly prepared
+spi_bus_driver structure:
+	static struct spi_bus_driver spipnx_driver = {
+        .driver = {
+                   .bus = &platform_bus_type,
+                   .name = "spipnx",
+                   .probe = spipnx_probe,
+                   .remove = spipnx_remove,
+                   .suspend = spipnx_suspend,
+                   .resume = spipnx_resume,
+                   },
+        .xfer = spipnx_xfer,
+};
+The driver and corresponding platform device are matched by name, so, in
+order the example abive to work, the platform_device named "spipnx" should
+be registered somewhere.
+
+void spi_bus_driver_unregister( struct spi_bus_driver* )
+
+Unregister the SPI bus driver registered by call to spi_buys_driver_register
+function; returns void.
+
+int spi_bus_populate( struct device* parent,
+			      char* devices,
+			      void (*callback)( struct device* parent, struct spi_device* new_one ) )
+This function usually called by SPI bus drivers in order to populate the SPI
+bus (see also section 3.2, "How the SPI devices gets discovered and probed ?").
+After creating the spi_device, the spi_bus_populate calls the `callback'
+function to allow to modify spi_device's fields before registering it with core.
+	parent	pointer to SPI bus
+	devices	string representing the current topology of SPI bus. It should
+		be formed like
+		"dev-1_and_its_info\0dev-2_and_its_info\0another_device\0\0"
+		the spi_bus_populate delimits this string by '\0' characters,
+		creates spi_device and after calling the callback registers the
+		spi_device
+	callback
+		pointer to function which could modify spi_device fields just
+		before registering them with core
+
+int spi_bus_populate2 (struct device *parent, struct spi_device_desc *devices,
+			void (*callback) (struct device* parent, struct spi_device* new_dev,
+					  void *params ))
+This is another way to populate the bus; but instead of string with device names and
+parameters, the array of structures spi_device_desc is passed. Each item in array describes
+the SPI slave device to create.
+
+
+v. spi_transfer and spi_queue
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The driver that uses SPI core can initiate transfers either by calling
+spi_transfer function (that will wait till the transfer is funished) or
+queueing the message using spi_queue function (you need to provide function
+that will be called during message is processed). In any way, you need to
+prepare the spimsg structure and know the target device to your message to
+be sent.
+
+int spi_transfer( struct spi_msg msgs,
+                  void (*callback)( struct spi_msg* msg, int ) )
+If callback is zero, start synchronous transfer. Otherwise, queue
+the message.
+	msg		message to be handled
+	callback	the callback function to be called during
+			message processing. If NULL, the function
+			will wait until end of processing.
+
+int spi_queue( struct spi_device* device,
+               struct spi_msg* msg )
+Queue the only message to the device. Returns status of queueing. To obtain
+status of message processing, you have to provide `status' callback in message
+and examine its parameters
+	msg	message to be queued
+
+vi. the spi_bus variable
+~~~~~~~~~~~~~~~~~~~~~~~~
+This variable is created during initialization of spi core, and has to be
+specified as `bus' on any SPI device driver (look to section iii, "struct
+spi_driver" ). If you do not specify spi_bus, your driver will be never
+matched to spi_device and never be probed with hardware. Note that
+spi_bus.match points to function that matches drivers and devices by name,
+so SPI devices and their drivers should have the same name.
+
+5. How to contact authors
+-------------------------
+Do you have any comments ? Enhancements ? Device driver ? Feel free
+to contact me:
+	dpervushin@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.10/include/linux/spi.h
===================================================================
--- /dev/null
+++ linux-2.6.10/include/linux/spi.h
@@ -0,0 +1,232 @@
+/*
+ *  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>
+
+struct spi_device;
+struct spi_driver;
+struct spi_msg;
+struct spi_bus_driver;
+
+extern struct bus_type spi_bus;
+
+struct spi_bus_data {
+	struct semaphore lock;
+	struct list_head msgs;
+	atomic_t exiting;
+	struct task_struct *thread;
+	wait_queue_head_t queue;
+	struct spi_device *selected_device;
+	struct spi_bus_driver *bus;
+};
+
+#define TO_SPI_BUS_DRIVER(drv) container_of( drv, struct spi_bus_driver, driver )
+struct spi_bus_driver {
+	int 	(*xfer) (struct spi_msg * msg);
+	void 	(*select) (struct spi_device * dev);
+	void 	(*deselect) (struct spi_device * dev);
+	void 	(*set_clock) (struct device * bus_device, u32 clock_hz);
+	struct device_driver driver;
+};
+
+#define TO_SPI_DEV(device) container_of( device, struct spi_device, dev )
+struct spi_device {
+	char name[BUS_ID_SIZE];
+	struct device dev;
+};
+
+#define TO_SPI_DRIVER(drv) container_of( drv, struct spi_driver, driver )
+struct spi_driver {
+	void 	       *(*alloc) (size_t, int);
+	void 	 	(*free) (const void *);
+	unsigned char  *(*get_buffer) (struct spi_device *, void *);
+	void 		(*release_buffer) (struct spi_device *, unsigned char *);
+	void 		(*control) (struct spi_device *, int mode, u32 ctl);
+	struct device_driver driver;
+};
+
+#define SPI_DEV_DRV( device )  TO_SPI_DRIVER( (device)->dev.driver )
+
+#define spi_device_lock( dev )		/* down( dev->dev.sem ) */
+#define spi_device_unlock( dev )	/* up( dev->dev.sem ) */
+
+/*
+ * struct spi_msg
+ *
+ * This structure represent the SPI message internally. You should never use fields of this structure directly
+ * Please use corresponding functions to create/destroy/access fields
+ *
+ */
+struct spi_msg {
+	unsigned char flags;
+#define SPI_M_RD	0x01
+#define SPI_M_WR	0x02	/**< Write mode flag */
+#define SPI_M_CSREL	0x04	/**< CS release level at end of the frame  */
+#define SPI_M_CS	0x08	/**< CS active level at begining of frame ( default low ) */
+#define SPI_M_CPOL	0x10	/**< Clock polarity */
+#define SPI_M_CPHA	0x20	/**< Clock Phase */
+	unsigned short len;	/* msg length           */
+	unsigned long clock;
+	struct spi_device *device;
+	void *context;
+	struct list_head link;
+	void (*status) (struct spi_msg * msg, int code);
+	void *devbuf_rd, *devbuf_wr;
+	u8 *databuf_rd, *databuf_wr;
+};
+
+static inline struct spi_msg *spimsg_alloc(struct spi_device *device,
+					   unsigned flags,
+					   unsigned short len,
+					   void (*status) (struct spi_msg *,
+							   int code))
+{
+	struct spi_msg *msg;
+	struct spi_driver *drv = SPI_DEV_DRV(device);
+
+	msg = kmalloc(sizeof(struct spi_msg), GFP_KERNEL);
+	if (!msg)
+		return NULL;
+	memset(msg, 0, sizeof(struct spi_msg));
+	msg->len = len;
+	msg->status = status;
+	msg->device = device;
+	msg->flags = flags;
+	INIT_LIST_HEAD(&msg->link);
+	if (flags & SPI_M_RD) {
+		msg->devbuf_rd = drv->alloc ?
+		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
+		msg->databuf_rd = drv->get_buffer ?
+		    drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd;
+	}
+	if (flags & SPI_M_WR) {
+		msg->devbuf_wr = drv->alloc ?
+		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
+		msg->databuf_wr = drv->get_buffer ?
+		    drv->get_buffer(device, msg->devbuf_wr) : msg->devbuf_wr;
+	}
+	pr_debug("%s: msg = %p, RD=(%p,%p) WR=(%p,%p). Actual flags = %s+%s\n",
+		 __FUNCTION__,
+		 msg,
+		 msg->devbuf_rd, msg->databuf_rd,
+		 msg->devbuf_wr, msg->databuf_wr,
+		 msg->flags & SPI_M_RD ? "RD" : "~rd",
+		 msg->flags & SPI_M_WR ? "WR" : "~wr");
+	return msg;
+}
+
+static inline void spimsg_free(struct spi_msg *msg)
+{
+	void (*do_free) (const void *) = kfree;
+	struct spi_driver *drv = SPI_DEV_DRV(msg->device);
+
+	if (msg) {
+		if (drv->free)
+			do_free = drv->free;
+		if (drv->release_buffer) {
+			if (msg->databuf_rd)
+				drv->release_buffer(msg->device,
+						    msg->databuf_rd);
+			if (msg->databuf_wr)
+				drv->release_buffer(msg->device,
+						    msg->databuf_wr);
+		}
+		if (msg->devbuf_rd)
+			do_free(msg->devbuf_rd);
+		if (msg->devbuf_wr)
+			do_free(msg->devbuf_wr);
+		kfree(msg);
+	}
+}
+
+static inline u8 *spimsg_buffer_rd(struct spi_msg *msg)
+{
+	return msg ? msg->databuf_rd : NULL;
+}
+
+static inline u8 *spimsg_buffer_wr(struct spi_msg *msg)
+{
+	return msg ? msg->databuf_wr : NULL;
+}
+
+static inline u8 *spimsg_buffer(struct spi_msg *msg)
+{
+	if (!msg)
+		return NULL;
+	if ((msg->flags & (SPI_M_RD | SPI_M_WR)) == (SPI_M_RD | SPI_M_WR)) {
+		printk(KERN_ERR "%s: what buffer do you really want ?\n",
+		       __FUNCTION__);
+		return NULL;
+	}
+	if (msg->flags & SPI_M_RD)
+		return msg->databuf_rd;
+	if (msg->flags & SPI_M_WR)
+		return msg->databuf_wr;
+}
+
+#define SPIMSG_OK 	0x01
+#define SPIMSG_FAILED 	0x80
+#define SPIMSG_STARTED  0x02
+#define SPIMSG_DONE	0x04
+
+#define SPI_MAJOR	153
+
+struct spi_driver;
+struct spi_device;
+
+static inline int spi_bus_driver_register(struct spi_bus_driver *bus_driver)
+{
+	return driver_register(&bus_driver->driver);
+}
+
+void spi_bus_driver_unregister(struct spi_bus_driver *);
+int spi_bus_driver_init(struct spi_bus_driver *driver, struct device *dev);
+struct spi_device* spi_device_add(struct device *parent, char *name);
+
+static inline void spi_device_del(struct spi_device *dev)
+{
+	device_unregister(&dev->dev);
+}
+static inline int spi_driver_add(struct spi_driver *drv)
+{
+	drv->driver.bus = &spi_bus;
+	return driver_register(&drv->driver);
+}
+static inline void spi_driver_del(struct spi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+
+#define SPI_DEV_CHAR "spi-char"
+
+extern int spi_write(struct spi_device *dev, const char *buf, int len);
+extern int spi_read(struct spi_device *dev, char *buf, int len);
+
+extern int spi_queue(struct spi_msg *message);
+extern int spi_transfer(struct spi_msg *message,
+			void (*status) (struct spi_msg *, int));
+extern int spi_bus_populate(struct device *parent, char *device,
+			    void (*assign) (struct device *parent,
+					    struct spi_device *));
+struct spi_device_desc {
+	char* name;
+	void* params;
+};
+extern int spi_bus_populate2(struct device *parent,
+			     struct spi_device_desc *devices,
+			     void (*assign) (struct device *parent,
+				             struct spi_device *,
+					     void *));
+
+#endif				/* SPI_H */




^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
@ 2005-10-03 16:26 David Brownell
  0 siblings, 0 replies; 38+ messages in thread
From: David Brownell @ 2005-10-03 16:26 UTC (permalink / raw)
  To: vwool; +Cc: linux-kernel

> >>>It'd be fine if for example your PNX controller driver worked that way
> >>>internally.  But other drivers shouldn't be forced to allocate kernel
> >>>threads when they don't need them.
> ...
> FYI in brief: for PREEMPT_RT case all the interrupt handlers are working 
> in a separate thread each unless explicitly specified otherwise.

I'm fully aware of that; not that it matters much for folk who aren't
building and deploying systems with PREEMPT_RT.


> We will definitely have less SPI busses => less kernel threads, so I 
> doubt there's a rationale in your opinion.

The rationale is simple:  you're trying to force one implementation
strategy.  Needlessly forcing one strategy, even when others may be
better (I already gave three examples), is a bad idea.  QED.  :)


> >Well "prevent" may be a bit strong, if you like hopping levels in
> >the software stack.  I don't; without such hopping (or without a
> >separate out-of-band mechanism like device tables), I don't see
> >a way to solve that problem.
>
> Aren't the tables you're suggesting also kinda out-of-band stuff?

I just described them that way; yes.  They're not layer hopping though;
they preserve the distinctions in roles and responsibilities which help
keep components from interfering with each other.

One general point is that when hardware doesn't support autoconfiguration,
something out-of-band is required to plug that hole.  In this case,
those tables can be segmented to handle SPI devices on both mainboards
and add-on boards.  Ditto for SPI controllers, but that mostly matters
for developer tools like parport adapters.

- Dave



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
  2005-10-03  5:01 David Brownell
@ 2005-10-03  6:20 ` Vitaly Wool
  0 siblings, 0 replies; 38+ messages in thread
From: Vitaly Wool @ 2005-10-03  6:20 UTC (permalink / raw)
  To: David Brownell; +Cc: linux-kernel

David Brownell wrote:

>>>You're imposing the same implementation strategy Mark Underwood was.
>>>I believe I persuaded him not to want that, pointing out three other
>>>implementation strategies that can be just as reasonable:
>>>
>>>  http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2
>>>
>>>It'd be fine if for example your PNX controller driver worked that way
>>>internally.  But other drivers shouldn't be forced to allocate kernel
>>>threads when they don't need them.
>>>      
>>>
>>Hm, so does that imply that the whole -rt patches from 
>>Ingo/Sven/Daniel/etc. are implementing wrong strategy (interrupts in 
>>threads)?
>>    
>>
>
>In an RT context, it may make sense to impose a policy like that.
>
>But I recall those folk have said they're making things so that sanely
>behaved kernel code will work with no changes.  And also that not all
>kernels should be enabling RT support ...
>
>  
>
FYI in brief: for PREEMPT_RT case all the interrupt handlers are working 
in a separate thread each unless explicitly specified otherwise.
This strategy proven itself to be quite functional and not significantly 
impacting the performance.
So, typically this means allocation of five to eight threads for 
interrupt handlers.
We will definitely have less SPI busses => less kernel threads, so I 
doubt there's a rationale in your opinion.

>>How will your strategy work with that BTW?
>>    
>>
>
>If they meet their goals, it'll work just fine.  Sanely behaved
>implementations will continue to work, and not even notice.
>  
>
Hm. How are you going to call something from an interrupt context? I 
guess that's gonna happen from an interrupt handler.
So there're 2 ways for PREEMPT_RT:
1) you're playing according to the rules  and don't specify SA_NODELAY 
=> the interrupt handler is in thread => bigger latencies for SPI than 
if we had separate kernel threads for each bus
2) you're not following the rules as specify SA_NODELAY => you're 
working in interrupt context creating a significant load to the 
interrupt context and slowing down everything that *is* following the rules.
So either SPI performance suffers, or RT performance does. :(

>
>
>  
>
>>>>+  [ picture deleted ]
>>>>        
>>>>
>>>That seems wierd even if I assume "platform_bus" is just an example.
>>>For example there are two rather different "spi bus" notions there,
>>>and it looks like neither one is the physical parent of any SPI device ...
>>>      
>>>
>>Not sure if I understand you :(
>>    
>>
>
>Why couldn't for example SPI sit on a PCI bus?
>
>And call the two boxes different things, if they're really different.
>The framework I've posted has "spi_master" as a class implmented
>by certain controller drivers.  (Others might use "spi_slave", and
>both would be types of "SPI bus".)  That would at least clarify
>the confusion on the left half of that picture.
>
>
>
>  
>
>>>>+3.2 How do the SPI devices gets discovered and probed ?
>>>>   
>>>>        
>>>>
>>>Better IMO to have tables that get consulted when the SPI master controller
>>>drivers register the parent ... tables that are initialized by the board
>>>specific __init section code, early on.  (Or maybe by __setup commandline
>>>parameters.)
>>>
>>>Doing it the way you are prevents you from declaring all the SPI devices in
>>>a single out-of-the-way location like the arch/.../board-specific.c file,
>>>which is normally responsible for declaring devices that are hard-wired on
>>>a given board and can't be probed.
>>> 
>>>
>>>      
>>>
>>By what means does it prevent that?
>>    
>>
>
>Well "prevent" may be a bit strong, if you like hopping levels in
>the software stack.  I don't; without such hopping (or without a
>separate out-of-band mechanism like device tables), I don't see
>a way to solve that problem.
>  
>
Aren't the tables you're suggesting also kinda out-of-band stuff?

>
>  
>
>>>>+#define SPI_MAJOR	153
>>>>+
>>>>+...
>>>>+
>>>>+#define SPI_DEV_CHAR "spi-char"
>>>>   
>>>>
>>>>        
>>>>
>>I thought 153 was the official SPI device number.
>>    
>>
>
>So it is (at least for minors 0..15, so long as they use some
>API I can't find a spec for), but that wasn't the point.  The
>point is to keep that sort of driver-specific information from
>cluttering headers which are addressed to _every_ driver.
>  
>
Yup, thanks for clarifications.

Vitaly

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
@ 2005-10-03  5:01 David Brownell
  2005-10-03  6:20 ` Vitaly Wool
  0 siblings, 1 reply; 38+ messages in thread
From: David Brownell @ 2005-10-03  5:01 UTC (permalink / raw)
  To: Vitaly Wool; +Cc: linux-kernel

> >	<linux/spi/spi.h>	... main header
> >	<linux/spi/CHIP.h>	... platform_data, for CHIP.c driver
> >
> >Not all chips would need them, but it might be nice to have some place
> >other than <linux/CHIP.h> for such things.  The platform_data would have
> >various important data that can't be ... chip variants, initialization
> >data, and similar stuff that differs between boards is knowable only by
> >board-specific init code, yet is needed by board-agnostic driver code.
> >  
>
> What about SPI busses that are common for different boards?

I don't understand your question.  It's simple enough to clone
the board-specific.c files for related designs; that's the only
sense I can imagine in which two boards might have the "same"
bus.  (Using the same controller is a different topic.)


> >You're imposing the same implementation strategy Mark Underwood was.
> >I believe I persuaded him not to want that, pointing out three other
> >implementation strategies that can be just as reasonable:
> >
> >   http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2
> >
> >It'd be fine if for example your PNX controller driver worked that way
> >internally.  But other drivers shouldn't be forced to allocate kernel
> >threads when they don't need them.
>
>
> Hm, so does that imply that the whole -rt patches from 
> Ingo/Sven/Daniel/etc. are implementing wrong strategy (interrupts in 
> threads)?

In an RT context, it may make sense to impose a policy like that.

But I recall those folk have said they're making things so that sanely
behaved kernel code will work with no changes.  And also that not all
kernels should be enabling RT support ...


> How will your strategy work with that BTW?

If they meet their goals, it'll work just fine.  Sanely behaved
implementations will continue to work, and not even notice.



> >>+  [ picture deleted ]
> >
> >That seems wierd even if I assume "platform_bus" is just an example.
> >For example there are two rather different "spi bus" notions there,
> >and it looks like neither one is the physical parent of any SPI device ...
>
>
> Not sure if I understand you :(

Why couldn't for example SPI sit on a PCI bus?

And call the two boxes different things, if they're really different.
The framework I've posted has "spi_master" as a class implmented
by certain controller drivers.  (Others might use "spi_slave", and
both would be types of "SPI bus".)  That would at least clarify
the confusion on the left half of that picture.



> >>+3.2 How do the SPI devices gets discovered and probed ?
> >>    
> >
> >Better IMO to have tables that get consulted when the SPI master controller
> >drivers register the parent ... tables that are initialized by the board
> >specific __init section code, early on.  (Or maybe by __setup commandline
> >parameters.)
> >
> >Doing it the way you are prevents you from declaring all the SPI devices in
> >a single out-of-the-way location like the arch/.../board-specific.c file,
> >which is normally responsible for declaring devices that are hard-wired on
> >a given board and can't be probed.
> >  
> >
> By what means does it prevent that?

Well "prevent" may be a bit strong, if you like hopping levels in
the software stack.  I don't; without such hopping (or without a
separate out-of-band mechanism like device tables), I don't see
a way to solve that problem.


> >>+#define SPI_MAJOR	153
> >>+
> >>+...
> >>+
> >>+#define SPI_DEV_CHAR "spi-char"
> >>    
> >>
> I thought 153 was the official SPI device number.

So it is (at least for minors 0..15, so long as they use some
API I can't find a spec for), but that wasn't the point.  The
point is to keep that sort of driver-specific information from
cluttering headers which are addressed to _every_ driver.

- Dave


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
@ 2005-10-03  4:56 David Brownell
  0 siblings, 0 replies; 38+ messages in thread
From: David Brownell @ 2005-10-03  4:56 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: linux-kernel, dpervushin

Hi Dmitry,

> > around the I/O model of a queue of async messages; and even 
> > names for some data structures.
> 
> It seems we are talking about similar things, aren't we ?

Sometimes, yes.  :)


> > 	<linux/spi/spi.h>	... main header
> > 	<linux/spi/CHIP.h>	... platform_data, for CHIP.c driver
> > 
> > Not all chips would need them, but it might be nice to have 
> > some place other than <linux/CHIP.h> for such things.  The 
> > platform_data would have various important data that can't be 
> > ... chip variants, initialization data, and similar stuff 
> > that differs between boards is knowable only by 
> > board-specific init code, yet is needed by board-agnostic driver code.
> 
> I would prefer not to have subdirectory spi in include/linux. Take a look to
> pci, for example. I guess that chip data are spi-bus specific, and should
> not be exported to world.

You misunderstand.  Consider something like a touchscreen driver, where
different boards may use the same controller (accessed using SPI) but
with different touchscreens and wiring.

That driver may need to know about those differences, much like it needs
to know about using a different IRQ number or clock rate.  Details like
"X plate resistance is 430 ohms" are not bus-specific, neither are
details like rise time (if any) for the reference voltage which affect
timings for some requests.  (Real world examples!)

Those are the sort of thing that board-specific.c files publish.
They have to be exported from arch/.../mach-.../board-specific.c into
somewhere in drivers/.../*.c; that's not really different from "exported
to world".


> > that way internally.  But other drivers shouldn't be forced 
> > to allocate kernel threads when they don't need them.
> 
> Really :) ? I'd like to have the worker thread for bus (and all devices on
> the bus) instead of several workqueues (one per each device on bus, right ?)

That is: you want to force each SPI Master Controller driver to allocate
a kernel thread (one per workqueue).  And I'm saying I'd rather not; the
API would be much more flexible without imposing that particular style.


> > Hmm, this seems to be missing a few important things ... from 
> > the last SPI patch I posted to this list (see the URL right above):
> > 
> > 	struct bus_type spi_bus_type = {
> > 		.name           = "spi",
> > 		.dev_attrs      = spi_dev_attrs,
> > 		.match          = spi_match_device,
> > 		.hotplug        = spi_hotplug,
> > 		.suspend        = spi_suspend,
> > 		.resume         = spi_resume,
> > 	};
> > 
> > That supports new-school "modprobe $MODALIAS" hotplugging and 
> > .../modalias style coldplugging, as well as passing PM calls 
> > down to the drivers.  (Those last recently got some tweaking, 
> > to work better through sysfs.)  And the core is STILL only 
> > about 2 KB on ARM; significantly less than yours.
> 
> Are you counting bytes on your sources ? Or bytes in object files ? As for
> spi_bus_type, I agree. Hotplu/suspend/resume have to be included.

Object code in the ".text" segment of whatever "core" code everyone
would need to keep in-memory.  The other numbers don't much matter.

You should be able to snarf the next version of suspend/resume code
pretty directly.

The hotplug stuff will require your init model to accumulate enough
description about each SPI device to identify the driver that should
be bound to it.  The last patch you posted didn't seem to have any
support for such things.



> > You don't seem to have any ability to record essential 
> > board-specific information that the drivers will need.  I 
> > hope you're not planning on making that stuff clutter up the 
> > driver files??  board-specific.c files seem the better model, 
> > with a way to pass that data to the drivers that need it 
> > (using the driver model).
> > 
> > ...
> 
> This is responsibility of bus driver. The driver for device on the SPI bus
> might request the hardware info from the bus driver, which is referenced via
> spi_device->device->parent.

Sounds like you're just shifting clutter from one driver to another.
I'd rather see it in _neither_ driver.  :)

What you're talking about would normally be spi_device->dev.platform_data;
I hope we can agree on that much.  Getting it there is a separate issue.
It's something I see as a basic role of the "SPI core", using information
from a board-specific.c file.


> > Why are you hard-wiring such an unfair scheduling policy ... 
> > and preventing use of better ones?  I'd use FIFO rather than 
> > something as unfair as that; and FIFO is much simpler to code, too.
> 
> OK, the policy is hardcoded and seems to be not the only available. This can
> be solved by adding a function to pull out the message that is "next by
> current". Does this sound reasonable ? 

My preference is different:  all queue management policies should be 
the responsibility of that controller driver.  You're assuming that
it's the core's responsibility (it would call that function).

   http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2


> > I don't really understand why you'd want to make this so 
> > expensive though.  Why not just do the IO directly into the 
> > buffer provided for that purpose?  One controller might 
> > require dma bounce buffers; but don't penalize all others by 
> > imposing those same costs.
> 
> Drivers might want to allocate theyr own buffers, for example, using
> dma_alloc_coherent. Such drivers also need to store the dma handle
> somewhere. Drivers might use pre-allocated buffers. 

The simple solution is to just have the driver provide both CPU and DMA
pointers for each buffer ... which implies using the spi_message level
API, not the CPU-pointer-only single buffer spi_read()/spi_write() calls
I was referring to.

A PIO controller driver would use the CPU pointer; a DMA one would use
the dma_addr_t.  The DMA pointers may have come from DMA mapping calls,
or from dma_alloc_coherent().  Each of those cases can be implemented
without that needless memcpy().  Only drivers on hardware that really
needs bounce buffers should pay those costs.


> > > +		msg->devbuf_rd = drv->alloc ?
> > > +		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
> > > +		msg->databuf_rd = drv->get_buffer ?
> > > +		    drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd;
> > 
> > Oy.  More dynamic allocation.  (Repeated for write buffers 
> > too ...) See above; don't force such costs on all drivers, 
> > few will ever need it.
> 
> That's not necessarily allocation. That depends on driver that uses
> spimsg_alloc, and possibly provides callback for allocating
> buffers/accessing them

Still, it's dynamic ... and it's quite indirect, since it's too early.
Simpler to have the driver stuff the right pointers into that "msg" in
its next steps. The driver has lots of relevant task context (say, in
stack frames) that's hidden from those alloc() or get_buffer() calls.

Plus it's still allocating something that's _always_ used as a bounce
buffer, even for drivers that doesn't need it.  Maybe they're PIO, or
maybe normal DMA mappings work ... no matter, most hardware doesn't
need bounce buffers.

- Dave



^ permalink raw reply	[flat|nested] 38+ messages in thread

* RE: [PATCH] SPI
  2005-09-30 17:59 David Brownell
  2005-09-30 18:30 ` Vitaly Wool
@ 2005-09-30 19:20 ` dpervushin
  1 sibling, 0 replies; 38+ messages in thread
From: dpervushin @ 2005-09-30 19:20 UTC (permalink / raw)
  To: 'David Brownell'; +Cc: linux-kernel, spi-devel-general

Hello all,
> around the I/O model of a queue of async messages; and even 
> names for some data structures.
It seems we are talking about similar things, aren't we ?
> 	<linux/spi/spi.h>	... main header
> 	<linux/spi/CHIP.h>	... platform_data, for CHIP.c driver
> 
> Not all chips would need them, but it might be nice to have 
> some place other than <linux/CHIP.h> for such things.  The 
> platform_data would have various important data that can't be 
> ... chip variants, initialization data, and similar stuff 
> that differs between boards is knowable only by 
> board-specific init code, yet is needed by board-agnostic driver code.
I would prefer not to have subdirectory spi in include/linux. Take a look to
pci, for example. I guess that chip data are spi-bus specific, and should
not be exported to world.
> that way internally.  But other drivers shouldn't be forced 
> to allocate kernel threads when they don't need them.
Really :) ? I'd like to have the worker thread for bus (and all devices on
the bus) instead of several workqueues (one per each device on bus, right ?)
> Hmm, this seems to be missing a few important things ... from 
> the last SPI patch I posted to this list (see the URL right above):
> 
> 	struct bus_type spi_bus_type = {
> 		.name           = "spi",
> 		.dev_attrs      = spi_dev_attrs,
> 		.match          = spi_match_device,
> 		.hotplug        = spi_hotplug,
> 		.suspend        = spi_suspend,
> 		.resume         = spi_resume,
> 	};
> 
> That supports new-school "modprobe $MODALIAS" hotplugging and 
> .../modalias style coldplugging, as well as passing PM calls 
> down to the drivers.  (Those last recently got some tweaking, 
> to work better through sysfs.)  And the core is STILL only 
> about 2 KB on ARM; significantly less than yours.
Are you counting bytes on your sources ? Or bytes in object files ? As for
spi_bus_type, I agree. Hotplu/suspend/resume have to be included.

> You don't seem to have any ability to record essential 
> board-specific information that the drivers will need.  I 
> hope you're not planning on making that stuff clutter up the 
> driver files??  board-specific.c files seem the better model, 
> with a way to pass that data to the drivers that need it 
> (using the driver model).
> 
> That minimally includes stuff like the IRQ used by that chip, 
> the clock rate it supports on this board, and the SPI 
> clocking mode (0, 1, 2, 3) used to get data in and out of the 
> chip.  But there seem to be a few other things needed too, 
> given the ways SPI chips tweak the protocol.
This is responsibility of bus driver. The driver for device on the SPI bus
might request the hardware info from the bus driver, which is referenced via
spi_device->device->parent.
> 
> 
> > +				/*
> > +				 * all messages for current 
> selected_device
> > +				 * are processed.
> > +				 * let's switch to another device
> > +				 */
> 
> Why are you hard-wiring such an unfair scheduling policy ... 
> and preventing use of better ones?  I'd use FIFO rather than 
> something as unfair as that; and FIFO is much simpler to code, too.
OK, the policy is hardcoded and seems to be not the only available. This can
be solved by adding a function to pull out the message that is "next by
current". Does this sound reasonable ? 
> 
> 
> > +{
> > +	int ret;
> > +	struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL);
> > +
> > +	ret = spi_transfer(msg, NULL);
> > +	memcpy(buf, spimsg_buffer_rd(msg), len);
> 
> I don't really understand why you'd want to make this so 
> expensive though.  Why not just do the IO directly into the 
> buffer provided for that purpose?  One controller might 
> require dma bounce buffers; but don't penalize all others by 
> imposing those same costs.
Drivers might want to allocate theyr own buffers, for example, using
dma_alloc_coherent. Such drivers also need to store the dma handle
somewhere. Drivers might use pre-allocated buffers. 
> 
> Also, spimsg_alloc() is huge ... even if you expect the 
> inliner will remove some of it.  It's doing several dynamic 
> allocations.  I honestly don't understand why there's a need 
> for even _one_ dynamic allocation in this "core" code path 
> (much less the memcpy).
The allocations might be avoided if drivers provide their callback to
"allocate" buffer. Then, there is the only alloc -- for spi_msg itself
> Also, you don't have any "board specific init" component in 
> this code...
spi_bus_populate calls the callback to initialize device with void* context.

> 
> 
> > +  +--------------+                    +---------+
> > +  | platform_bus |                    | spi_bus |
> > +  +--------------+                    +---------+
> > +       |..|                                |
> > +       |..|--------+               +---------------+
> > +     +------------+| is parent to  |  SPI devices  |
> > +     | SPI busses |+-------------> |               |
> > +     +------------+                +---------------+
> > +           |                               |
> > +     +----------------+          +----------------------+
> > +     | SPI bus driver |          |    SPI device driver |
> > +     +----------------+          +----------------------+
> 
> That seems wierd even if I assume "platform_bus" is just an example.
> For example there are two rather different "spi bus" notions 
> there, and it looks like neither one is the physical parent 
> of any SPI device ...
"SPI busses" means several 'struct device' that corresponds to real device
that acts as spi controller. "spi_bus" is the variable of type "bus_type"

> > +		msg->devbuf_rd = drv->alloc ?
> > +		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, 
> GFP_KERNEL);
> > +		msg->databuf_rd = drv->get_buffer ?
> > +		    drv->get_buffer(device, msg->devbuf_rd) : 
> msg->devbuf_rd;
> 
> Oy.  More dynamic allocation.  (Repeated for write buffers 
> too ...) See above; don't force such costs on all drivers, 
> few will ever need it.
That's not necessarily allocation. That depends on driver that uses
spimsg_alloc, and possibly provides callback for allocating
buffers/accessing them
> > +#define SPI_MAJOR	153

--
cheers, dmitry pervushin


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
  2005-09-30 17:59 David Brownell
@ 2005-09-30 18:30 ` Vitaly Wool
  2005-09-30 19:20 ` dpervushin
  1 sibling, 0 replies; 38+ messages in thread
From: Vitaly Wool @ 2005-09-30 18:30 UTC (permalink / raw)
  To: David Brownell; +Cc: dpervushin, dpervushin, linux-kernel

Greetings,

<snip>

>> drivers/spi/Kconfig    |   33 +++
>> drivers/spi/Makefile   |   14 +
>> include/linux/spi.h    |  232 ++++++++++++++++++++++
>>    
>>
>
>Looks familiar.  :)  But another notion for the headers would be
>
>	<linux/spi/spi.h>	... main header
>	<linux/spi/CHIP.h>	... platform_data, for CHIP.c driver
>
>Not all chips would need them, but it might be nice to have some place
>other than <linux/CHIP.h> for such things.  The platform_data would have
>various important data that can't be ... chip variants, initialization
>data, and similar stuff that differs between boards is knowable only by
>board-specific init code, yet is needed by board-agnostic driver code.
>  
>
What about SPI busses that are common for different boards?

<snip>

>>+static int spi_thread(void *context);
>>    
>>
>
>You're imposing the same implementation strategy Mark Underwood was.
>I believe I persuaded him not to want that, pointing out three other
>implementation strategies that can be just as reasonable:
>
>   http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2
>
>It'd be fine if for example your PNX controller driver worked that way
>internally.  But other drivers shouldn't be forced to allocate kernel
>threads when they don't need them.
>  
>
Hm, so does that imply that the whole -rt patches from 
Ingo/Sven/Daniel/etc. are implementing wrong strategy (interrupts in 
threads)?
How will your strategy work with that BTW?

<snip>

>>+				/*
>>+				 * all messages for current selected_device
>>+				 * are processed.
>>+				 * let's switch to another device
>>+				 */
>>    
>>
>
>Why are you hard-wiring such an unfair scheduling policy ... and
>preventing use of better ones?  I'd use FIFO rather than something
>as unfair as that; and FIFO is much simpler to code, too.
>  
>
Sounds reasonable to me.

>>--- /dev/null
>>+++ linux-2.6.10/drivers/spi/spi-dev.c
>>@@ -0,0 +1,219 @@
>>+/*
>>+    spi-dev.c - spi driver, char device interface
>>+
>>    
>>
>
>Do you have userspace drivers that work with this?  I can see how to use
>it with read-only sensors (each read generates a 12bit sample, etc) and
>certain write-only devices, I guess.
>
>But it doesn't look like this character device can handle RPC-ish things
>like "give me an ADC conversion for line 3" (which commonly maps to a
>"write 8 bits, then start reading 12 data bits one half clock after
>the last bit is written" ... hard to make that work with separate
>read and write transactions, given the half clock rule).
>  
>
Hm. I thought half-clock cases were to be programmed kernel-wise.

<snip>

>  
>
>>+  +--------------+                    +---------+
>>+  | platform_bus |                    | spi_bus |
>>+  +--------------+                    +---------+
>>+       |..|                                |
>>+       |..|--------+               +---------------+
>>+     +------------+| is parent to  |  SPI devices  |
>>+     | SPI busses |+-------------> |               |
>>+     +------------+                +---------------+
>>+           |                               |
>>+     +----------------+          +----------------------+
>>+     | SPI bus driver |          |    SPI device driver |
>>+     +----------------+          +----------------------+
>>    
>>
>
>That seems wierd even if I assume "platform_bus" is just an example.
>For example there are two rather different "spi bus" notions there,
>and it looks like neither one is the physical parent of any SPI device ...
>
>  
>
Not sure if I understand you :(

>  
>
>>+3.2 How do the SPI devices gets discovered and probed ?
>>    
>>
>
>Better IMO to have tables that get consulted when the SPI master controller
>drivers register the parent ... tables that are initialized by the board
>specific __init section code, early on.  (Or maybe by __setup commandline
>parameters.)
>
>Doing it the way you are prevents you from declaring all the SPI devices in
>a single out-of-the-way location like the arch/.../board-specific.c file,
>which is normally responsible for declaring devices that are hard-wired on
>a given board and can't be probed.
>  
>
By what means does it prevent that?

>>+static inline struct spi_msg *spimsg_alloc(struct spi_device *device,
>>+					   unsigned flags,
>>+					   unsigned short len,
>>+					   void (*status) (struct spi_msg *,
>>+							   int code))
>>+{
>>+	... 30+ lines including...
>>+
>>+	msg = kmalloc(sizeof(struct spi_msg), GFP_KERNEL);
>>+	memset(msg, 0, sizeof(struct spi_msg));
>>    
>>
>
>If these things aren't going to be refcounted, then it'd be easier
>to just let them be stack-allocated; they ARE that small.  And if
>they've _got_ to be on the heap, then there's a new "kzalloc()" call
>you should be looking at ...
>
>
>  
>
>>+		msg->devbuf_rd = drv->alloc ?
>>+		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
>>+		msg->databuf_rd = drv->get_buffer ?
>>+		    drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd;
>>    
>>
>
>Oy.  More dynamic allocation.  (Repeated for write buffers too ...)
>See above; don't force such costs on all drivers, few will ever need it.
>  
>
I guess that won't necessarily be actual allocation, it's a matter of 
drv callbacks.

<snip>

>>+#define SPI_MAJOR	153
>>+
>>+...
>>+
>>+#define SPI_DEV_CHAR "spi-char"
>>    
>>
I thought 153 was the official SPI device number.


Best regards,
   Vitaly

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] SPI
@ 2005-09-30 17:59 David Brownell
  2005-09-30 18:30 ` Vitaly Wool
  2005-09-30 19:20 ` dpervushin
  0 siblings, 2 replies; 38+ messages in thread
From: David Brownell @ 2005-09-30 17:59 UTC (permalink / raw)
  To: dpervushin; +Cc: dpervushin, linux-kernel

> Here is the revised SPI-core patch.

Also, in a few days I'll post an update to the first of these patches,
with a smaller SPI core you've seen before:

  http://marc.theaimsgroup.com/?l=linux-kernel&m=112631114922427&w=2
  http://marc.theaimsgroup.com/?l=linux-kernel&m=112631175219099&w=2

Among the folk who've recently posted SPI "framework" patches (you, Mark
Underwood, me), there seems to be some convergence around the I/O model
of a queue of async messages; and even names for some data structures.

That's a useful start ... 


>  drivers/spi/Kconfig    |   33 +++
>  drivers/spi/Makefile   |   14 +
>  include/linux/spi.h    |  232 ++++++++++++++++++++++

Looks familiar.  :)  But another notion for the headers would be

	<linux/spi/spi.h>	... main header
	<linux/spi/CHIP.h>	... platform_data, for CHIP.c driver

Not all chips would need them, but it might be nice to have some place
other than <linux/CHIP.h> for such things.  The platform_data would have
various important data that can't be ... chip variants, initialization
data, and similar stuff that differs between boards is knowable only by
board-specific init code, yet is needed by board-agnostic driver code.

Comments?


> +static int spi_thread(void *context);

You're imposing the same implementation strategy Mark Underwood was.
I believe I persuaded him not to want that, pointing out three other
implementation strategies that can be just as reasonable:

   http://marc.theaimsgroup.com/?l=linux-kernel&m=112684135722116&w=2

It'd be fine if for example your PNX controller driver worked that way
internally.  But other drivers shouldn't be forced to allocate kernel
threads when they don't need them.


> +static int spi_bus_match_name(struct device *dev, struct device_driver *drv)
> +{
> +	return !strcmp(drv->name, SPI_DEV_CHAR) ||
> +	    !strcmp(TO_SPI_DEV(dev)->name, drv->name);
> +}

I don't like seeing special cases like that.  Is there some problem
using the sysfs "bind this driver to that device" mechanism?

Having a wildcard "I'll bind to anything" driver mode would be nice,
so long as any driver was able to use it.


> +struct bus_type spi_bus = {
> +	.name = "spi",
> +	.match = spi_bus_match_name,
> +};

Hmm, this seems to be missing a few important things ... from the last
SPI patch I posted to this list (see the URL right above):

	struct bus_type spi_bus_type = {
		.name           = "spi",
		.dev_attrs      = spi_dev_attrs,
		.match          = spi_match_device,
		.hotplug        = spi_hotplug,
		.suspend        = spi_suspend,
		.resume         = spi_resume,
	};

That supports new-school "modprobe $MODALIAS" hotplugging and .../modalias
style coldplugging, as well as passing PM calls down to the drivers.  (Those
last recently got some tweaking, to work better through sysfs.)  And the
core is STILL only about 2 KB on ARM; significantly less than yours.


> +struct spi_device* spi_device_add(struct device *parent, char *name)

You don't seem to have any ability to record essential board-specific
information that the drivers will need.  I hope you're not planning on
making that stuff clutter up the driver files??  board-specific.c files
seem the better model, with a way to pass that data to the drivers
that need it (using the driver model).

That minimally includes stuff like the IRQ used by that chip, the clock
rate it supports on this board, and the SPI clocking mode (0, 1, 2, 3)
used to get data in and out of the chip.  But there seem to be a few
other things needed too, given the ways SPI chips tweak the protocol.


> +				/*
> +				 * all messages for current selected_device
> +				 * are processed.
> +				 * let's switch to another device
> +				 */

Why are you hard-wiring such an unfair scheduling policy ... and
preventing use of better ones?  I'd use FIFO rather than something
as unfair as that; and FIFO is much simpler to code, too.


> +/*
> + * spi_write
> + * 	receive data from a device on an SPI bus
> + * Parameters:
> + * 	spi_device* dev		the target device
> + *	char* buf		buffer to be sent
> + *	int len			number of bytes to receive
> + * Return:
> + * 	 the number of bytes transferred, or negative error code.
> + */
> +int spi_read(struct spi_device *dev, char *buf, int len)

There's a cut'n'paste error here.  Also, you should be using
standard kerneldoc instead of this stuff.


> +{
> +	int ret;
> +	struct spimsg *msg = spimsg_alloc(dev, SPI_M_RD, len, NULL);
> +
> +	ret = spi_transfer(msg, NULL);
> +	memcpy(buf, spimsg_buffer_rd(msg), len);

I don't really understand why you'd want to make this so expensive
though.  Why not just do the IO directly into the buffer provided
for that purpose?  One controller might require dma bounce buffers;
but don't penalize all others by imposing those same costs.

Also, spimsg_alloc() is huge ... even if you expect the inliner will
remove some of it.  It's doing several dynamic allocations.  I honestly
don't understand why there's a need for even _one_ dynamic allocation
in this "core" code path (much less the memcpy).


> +/*
> + * spi_bus_populate and spi_bus_populate2
> + *
> + * These two functions intended to populate the SPI bus corresponding to
> + * device passed as 1st parameter. The difference is in the way to describe
> + * new SPI slave devices: the spi_bus_populate takes the ASCII string delimited
> + * by '\0', where each section matches one SPI device name _and_ its parameters,
> + * and the spi_bus_populate2 takes the array of structures spi_device_desc.

The place I can see wanting ASCII strings to help is on the kernel command
line, but those strings aren't formatted in a way __setup() could handle.
(Embedded nulls, for starters.)

Also, you don't have any "board specific init" component in this code...


> --- /dev/null
> +++ linux-2.6.10/drivers/spi/spi-dev.c
> @@ -0,0 +1,219 @@
> +/*
> +    spi-dev.c - spi driver, char device interface
> +

Do you have userspace drivers that work with this?  I can see how to use
it with read-only sensors (each read generates a 12bit sample, etc) and
certain write-only devices, I guess.

But it doesn't look like this character device can handle RPC-ish things
like "give me an ADC conversion for line 3" (which commonly maps to a
"write 8 bits, then start reading 12 data bits one half clock after
the last bit is written" ... hard to make that work with separate
read and write transactions, given the half clock rule).

Also, you might have a look at the at91_spidev.c code [1]; it's much
lower overhead for bulk read/write, and uses dma right to the usespace
pages in its spi_read() analogue.  Those kernels also support DataFlash
for the root file system ... that is, the SPI stack is worth looking at
since it's more functional than the one you're proposing.  (For all
that it's pretty much kernel 2.4 code ported to 2.6, and doesn't use
the driver model yet.)


> +  +--------------+                    +---------+
> +  | platform_bus |                    | spi_bus |
> +  +--------------+                    +---------+
> +       |..|                                |
> +       |..|--------+               +---------------+
> +     +------------+| is parent to  |  SPI devices  |
> +     | SPI busses |+-------------> |               |
> +     +------------+                +---------------+
> +           |                               |
> +     +----------------+          +----------------------+
> +     | SPI bus driver |          |    SPI device driver |
> +     +----------------+          +----------------------+

That seems wierd even if I assume "platform_bus" is just an example.
For example there are two rather different "spi bus" notions there,
and it looks like neither one is the physical parent of any SPI device ...


> +3.2 How do the SPI devices gets discovered and probed ?

Better IMO to have tables that get consulted when the SPI master controller
drivers register the parent ... tables that are initialized by the board
specific __init section code, early on.  (Or maybe by __setup commandline
parameters.)

Doing it the way you are prevents you from declaring all the SPI devices in
a single out-of-the-way location like the arch/.../board-specific.c file,
which is normally responsible for declaring devices that are hard-wired on
a given board and can't be probed.


> +	http://spi-devel.sourceforge.net

That seems like potentially a good place for SPI work to cook for a while,
especially if you start merging updates from other folk.


> +       void (*status) (struct spi_msg * msg, int code);

These per-message "status" callbacks are really wierd ... why so complex?

Drivers are inflicted with several different calls per message, and will
need to filter out all except the two different kinds that indicate errors.
None of those calls uses the standard Linux convention for status codes:
zero == success, negative numbers == errno values.

And the callback is even for some reason optional ... just bite the bullet
and insist that a (streamlined) call always be made.  You only need one way
to report transfer completion, it only needs to work asynchronously.


> +static inline struct spi_msg *spimsg_alloc(struct spi_device *device,
> +					   unsigned flags,
> +					   unsigned short len,
> +					   void (*status) (struct spi_msg *,
> +							   int code))
> +{
> +	... 30+ lines including...
> +
> +	msg = kmalloc(sizeof(struct spi_msg), GFP_KERNEL);
> +	memset(msg, 0, sizeof(struct spi_msg));

If these things aren't going to be refcounted, then it'd be easier
to just let them be stack-allocated; they ARE that small.  And if
they've _got_ to be on the heap, then there's a new "kzalloc()" call
you should be looking at ...


> +		msg->devbuf_rd = drv->alloc ?
> +		    drv->alloc(len, GFP_KERNEL) : kmalloc(len, GFP_KERNEL);
> +		msg->databuf_rd = drv->get_buffer ?
> +		    drv->get_buffer(device, msg->devbuf_rd) : msg->devbuf_rd;

Oy.  More dynamic allocation.  (Repeated for write buffers too ...)
See above; don't force such costs on all drivers, few will ever need it.

> +}

That seems like spimsg_alloc is much too long a function to inline.  And
it's easy to imagine cases where the costs of all those allocations will
exceed the cost of actually doing the I/O ...


> +#define SPI_MAJOR	153
> +
> +...
> +
> +#define SPI_DEV_CHAR "spi-char"

Those things are specific to that particular userspace driver support;
they don't belong in APIs that are visible to non-userspace drivers.

- Dave


[1] http://maxim.org.za/AT91RM9200/2.6/

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-09 19:29             ` Andy Isaacson
@ 2005-08-15  7:51               ` Denis Vlasenko
  0 siblings, 0 replies; 38+ messages in thread
From: Denis Vlasenko @ 2005-08-15  7:51 UTC (permalink / raw)
  To: Andy Isaacson, Marc Singer; +Cc: Marcel Holtmann, Linux Kernel Mailing List

> > It was explained to me that the !pointer test wasn't guaranteed to be
> > equivalent because of the way that the test is handled.
> 
> Whoever explained that to you was wrong.  6.5.3.3 is the final word on
> how "!x" is interpreted, and it *says* in the *text* that
> "!x" === "x!=0".  I don't see how this could be any clearer.
> 
> > The spec fragments above don't address how the boolean test is
> > coerced.  Does it cast pointer to an integer and perform the test, or
> > does it cast the 0 to a pointer and perform the test.  The C++ spec I
> > have is vague on this point.  The only reference it makes to pointers
> > is that the operand for ! may be a pointer.
> 
> Because of the equivalence *given in the text of 6.5.3.3* we can simply
> follow the money.  (I'm not concerned, here, about what ambiguities the
> C++ folks may or may not have introduced into their monstrosity.  The
> Linux kernel is written in C, and the C standard is unambiguous on this
> point.  Though frankly I'd be suprised if C++ breaks something so
> straightforward and useful.)
> 
> The section that defines != says
> 
> 6.5.9 Equality operators
>   Syntax
> (1)      equality-expression:
>                 relational-expression
>                 equality-expression == relational-expression
>                 equality-expression != relational-expression
>   Constraints
> 
> (2) One of the following shall hold:
>   ...
>   -- one operand is a pointer and the other is a null pointer constant.
> 
> (5) ... If one operand is a pointer and the other is a null pointer
>   constant, the null pointer constant is converted to the type of the
>   pointer. ...
> 
> So:
> 1. !x is defined equivalent to x!=0.
> 2. 0 is a "null pointer constant".
> 3. (assuming x is a pointer) 0 will be promoted to pointer type in the
>    expression "x!=0".

You are right to 99.9% ;)

The last 0.1% of wrongness comes from linux/stddef.h:
...
#define NULL ((void *)0)

Thus, !ptr is equivalent to ptr==0 but not equivalent to ptr==NULL
in general case for the kernel code (it is equivalent if ptr is
a variable of a _pointer type_ because ptr then implicitly converted
to (void*)).

Our NULL isn't 0 by design. it's not a bug, regardless what Stroustrup says
about NULL define.
--
vda


^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
                     ` (3 preceding siblings ...)
  2005-08-08 22:58   ` Andrew Morton
@ 2005-08-10 13:10   ` Pavel Machek
  4 siblings, 0 replies; 38+ messages in thread
From: Pavel Machek @ 2005-08-10 13:10 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List

Hi!

> +menu "SPI support"
> +
> +config SPI
> +	default Y
> +	tristate "SPI support"
> +        default false
> +	help
> +	  Say Y if you need to enable SPI support on your kernel

I'd expect explanation what "SPI" means somewhere around here...
								Pavel

-- 
if you have sharp zaurus hardware you don't need... you know my address

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-09 19:05           ` Marc Singer
@ 2005-08-09 19:29             ` Andy Isaacson
  2005-08-15  7:51               ` Denis Vlasenko
  0 siblings, 1 reply; 38+ messages in thread
From: Andy Isaacson @ 2005-08-09 19:29 UTC (permalink / raw)
  To: Marc Singer; +Cc: Marcel Holtmann, Linux Kernel Mailing List

The code in question is

On Tue, Aug 09, 2005 at 12:05:00PM -0700, Marc Singer wrote:
> > > On Mon, Aug 08, 2005 at 07:35:36PM +0200, Marcel Holtmann wrote:
> > > > > > +	if (NULL == dev || NULL == driver) {
> > > > 	if (!dev || !driver) {
> > > 

You said:

> > > That's not a guaranteed equivalence in the C standard.  Null pointers
> > > may not be zero.  I don't think we have any targets that work this
> > > way, however there is nothing wrong with explicitly testing for NULL.

I quoted chapter and verse why that statement is not true:

> > [1] ISO/IEC 9899:1999 6.5.3.3 Unary arithmetic operators
> > 
> >   (5) The result of the logical negation operator ! is 0 if the value of
> >   its operand compares unequal to 0, 1 if the value of its operand
> >   compares equal to 0. The result has type int.  The expression !E is
> >   equivalent to (0==E).
> > 
> > [2] ISO/IEC 9899:1999 7.17
> > 
> >   The following types and macros are defined in the standard header
> >   <stddef.h>.  ...
> >          NULL
> >   which expands to an implementation-defined null pointer constant...
> > 
> >  and 6.3.2.3 Pointers
> >   (3) An integer constant expression with the value 0, or such an
> >   expression cast to type void *, is called a null pointer constant.
> 
> It was explained to me that the !pointer test wasn't guaranteed to be
> equivalent because of the way that the test is handled.

Whoever explained that to you was wrong.  6.5.3.3 is the final word on
how "!x" is interpreted, and it *says* in the *text* that
"!x" === "x!=0".  I don't see how this could be any clearer.

> The spec fragments above don't address how the boolean test is
> coerced.  Does it cast pointer to an integer and perform the test, or
> does it cast the 0 to a pointer and perform the test.  The C++ spec I
> have is vague on this point.  The only reference it makes to pointers
> is that the operand for ! may be a pointer.

Because of the equivalence *given in the text of 6.5.3.3* we can simply
follow the money.  (I'm not concerned, here, about what ambiguities the
C++ folks may or may not have introduced into their monstrosity.  The
Linux kernel is written in C, and the C standard is unambiguous on this
point.  Though frankly I'd be suprised if C++ breaks something so
straightforward and useful.)

The section that defines != says

6.5.9 Equality operators
  Syntax
(1)      equality-expression:
                relational-expression
                equality-expression == relational-expression
                equality-expression != relational-expression
  Constraints

(2) One of the following shall hold:
  ...
  -- one operand is a pointer and the other is a null pointer constant.

(5) ... If one operand is a pointer and the other is a null pointer
  constant, the null pointer constant is converted to the type of the
  pointer. ...

So:
1. !x is defined equivalent to x!=0.
2. 0 is a "null pointer constant".
3. (assuming x is a pointer) 0 will be promoted to pointer type in the
   expression "x!=0".



With the facts taken care of, we can move on to

> No, I'm not confused about the representation of a NULL.  Keep in mind
> that telling someone what they do or don't
> understand/believe/think/feel is the fast track to being flamed.

On Linux-kernel, being wrong is the fast track to being flamed.  When
I'm wrong, I expect to be corrected.  (Frankly, I'm wrong and *not*
corrected much more frequently than I find comfortable.)  Adjust your
expectations accordingly and you may be more comfortable here.

Your original statement was wrong, so I corrected you (as much to keep
the disinformation level to a low roar, as anything).  The code
transformation quoted at the top of this message is both
(1) well-defined by the C standard and (2) in keeping with kernel coding
standards.

HTH, HAND.
-andy

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-09 17:54         ` Andy Isaacson
@ 2005-08-09 19:05           ` Marc Singer
  2005-08-09 19:29             ` Andy Isaacson
  0 siblings, 1 reply; 38+ messages in thread
From: Marc Singer @ 2005-08-09 19:05 UTC (permalink / raw)
  To: Andy Isaacson; +Cc: Marcel Holtmann, Linux Kernel Mailing List

On Tue, Aug 09, 2005 at 10:54:34AM -0700, Andy Isaacson wrote:
> On Mon, Aug 08, 2005 at 10:47:21AM -0700, Marc Singer wrote:
> > On Mon, Aug 08, 2005 at 07:35:36PM +0200, Marcel Holtmann wrote:
> > > > > +	if (NULL == dev || NULL == driver) {
> > > > 
> > > > Put the variable on the left side, gcc will complain if you incorrectly
> > > > put a "=" instead of a "==" here, which is all that you are defending
> > > > against with this style.
> > > 
> > > I think in this case the preferred way is
> > > 
> > > 	if (!dev || !driver) {
> > > 
> > 
> > That's not a guaranteed equivalence in the C standard.  Null pointers
> > may not be zero.  I don't think we have any targets that work this
> > way, however there is nothing wrong with explicitly testing for NULL.
> 
> False.  The expression  "!x" is precisely equivalent to "x==0", no
> matter what the type of x is. [1]  And furthermore, NULL==0. [2]
> Ergo, "NULL == dev" and "!dev" are defined to be equivalent.
> 
> What you're confused about is that the *representation* of a null
> pointer constant does not necessarily have to be all-bits-zero.  That
> is, the following code fragment might print something on a
> standard-compliant C implementation:

No, I'm not confused about the representation of a NULL.  Keep in mind
that telling someone what they do or don't
understand/believe/think/feel is the fast track to being flamed.

> 
> 	void *a = 0; unsigned char *p = (unsigned char *)&a;
> 	int i;
> 	for(i=0; i<sizeof(a); i++)
> 		if(p[i] != 0) printf("p[%d] = %02x!\n", i, p[i]);
> 
> That does not change the fact that the source-code fragment "NULL" is
> defined to be equivalent to the source-code fragment "0".  Simply the
> compiler must do whatever trickery necessary to ensure the correct
> values get generated in the object code for my above hypothetical
> architecture when I say "void *a = 0;".
> 
> This is very similar to how floating point is handled in the abstract
> machine definition of the standard.  Consider a weird FP implementation
> where 0.0 has a not-all-bits-zero representation, and change 'a' in my
> example above to type 'double'.  Just because 0.0 is stored as the bit
> pattern 0x8000000000000000 does not mean that I have to write something
> other than "double a = 0;"!
> 
> And furthermore, all of this was well-understood in the C89 standard;
> it's not new in the C99 standard, although there are some
> clarifications.
> 
> [1] ISO/IEC 9899:1999 6.5.3.3 Unary arithmetic operators
> 
>   (5) The result of the logical negation operator ! is 0 if the value of
>   its operand compares unequal to 0, 1 if the value of its operand
>   compares equal to 0. The result has type int.  The expression !E is
>   equivalent to (0==E).
> 
> [2] ISO/IEC 9899:1999 7.17
> 
>   The following types and macros are defined in the standard header
>   <stddef.h>.  ...
>          NULL
>   which expands to an implementation-defined null pointer constant...
> 
>  and 6.3.2.3 Pointers
>   (3) An integer constant expression with the value 0, or such an
>   expression cast to type void *, is called a null pointer constant.

It was explained to me that the !pointer test wasn't guaranteed to be
equivalent because of the way that the test is handled.  The spec
fragments above don't address how the boolean test is coerced.  Does
it cast pointer to an integer and perform the test, or does it cast
the 0 to a pointer and perform the test.  The C++ spec I have is vague
on this point.  The only reference it makes to pointers is that the
operand for ! may be a pointer.




^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 17:47       ` Marc Singer
@ 2005-08-09 17:54         ` Andy Isaacson
  2005-08-09 19:05           ` Marc Singer
  0 siblings, 1 reply; 38+ messages in thread
From: Andy Isaacson @ 2005-08-09 17:54 UTC (permalink / raw)
  To: Marc Singer; +Cc: Marcel Holtmann, Linux Kernel Mailing List

On Mon, Aug 08, 2005 at 10:47:21AM -0700, Marc Singer wrote:
> On Mon, Aug 08, 2005 at 07:35:36PM +0200, Marcel Holtmann wrote:
> > > > +	if (NULL == dev || NULL == driver) {
> > > 
> > > Put the variable on the left side, gcc will complain if you incorrectly
> > > put a "=" instead of a "==" here, which is all that you are defending
> > > against with this style.
> > 
> > I think in this case the preferred way is
> > 
> > 	if (!dev || !driver) {
> > 
> 
> That's not a guaranteed equivalence in the C standard.  Null pointers
> may not be zero.  I don't think we have any targets that work this
> way, however there is nothing wrong with explicitly testing for NULL.

False.  The expression  "!x" is precisely equivalent to "x==0", no
matter what the type of x is. [1]  And furthermore, NULL==0. [2]
Ergo, "NULL == dev" and "!dev" are defined to be equivalent.

What you're confused about is that the *representation* of a null
pointer constant does not necessarily have to be all-bits-zero.  That
is, the following code fragment might print something on a
standard-compliant C implementation:

	void *a = 0; unsigned char *p = (unsigned char *)&a;
	int i;
	for(i=0; i<sizeof(a); i++)
		if(p[i] != 0) printf("p[%d] = %02x!\n", i, p[i]);

That does not change the fact that the source-code fragment "NULL" is
defined to be equivalent to the source-code fragment "0".  Simply the
compiler must do whatever trickery necessary to ensure the correct
values get generated in the object code for my above hypothetical
architecture when I say "void *a = 0;".

This is very similar to how floating point is handled in the abstract
machine definition of the standard.  Consider a weird FP implementation
where 0.0 has a not-all-bits-zero representation, and change 'a' in my
example above to type 'double'.  Just because 0.0 is stored as the bit
pattern 0x8000000000000000 does not mean that I have to write something
other than "double a = 0;"!

And furthermore, all of this was well-understood in the C89 standard;
it's not new in the C99 standard, although there are some
clarifications.

[1] ISO/IEC 9899:1999 6.5.3.3 Unary arithmetic operators

  (5) The result of the logical negation operator ! is 0 if the value of
  its operand compares unequal to 0, 1 if the value of its operand
  compares equal to 0. The result has type int.  The expression !E is
  equivalent to (0==E).

[2] ISO/IEC 9899:1999 7.17

  The following types and macros are defined in the standard header
  <stddef.h>.  ...
         NULL
  which expands to an implementation-defined null pointer constant...

 and 6.3.2.3 Pointers
  (3) An integer constant expression with the value 0, or such an
  expression cast to type void *, is called a null pointer constant.

-andy

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 23:07 [PATCH] spi david-b
@ 2005-08-09  9:38 ` Mark Underwood
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Underwood @ 2005-08-09  9:38 UTC (permalink / raw)
  To: david-b, dmitry pervushin; +Cc: linux-kernel


--- david-b@pacbell.net wrote:

> Well I like this a bit better, but it's still in
> transition from
> the old I2C style stuff over to a newer driver model
> based one.
> (As other folk have noted, with the "bus" v.
> "adapter" confusion.)
> 
>   - Can you make the SPI messages work with an async
> API?
>     It suffices to have a callback and a "void *"
> for the
>     caller's data.  Those callbacks should be able
> to start
>     the next stage of a device protocol request ...
> e.g. the
>     first one issues some command bytes, and its
> completion
>     callback starts the data transfer.  (It's easy
> to build
>     synchronous models over async ones; but not the
> other way.)
> 
>     (I see Mark Underwood commented that he was
> working with
>     one like that.)

Yep. I'm doing some tidying up. The work is still in
development but I'm trying to get some patches ready
so people and compare and contrast :-).

> 
>   - The basic thing that bothers me is that, like
> original I2C,
>     the roles and responsibilities here don't
> correspond in any
>     consistent way to the driver model.  For new
> code like SPI,
>     there's no excuse for that to happen.
> 
>   - It should probably also not assume Linux can
> only act in
>     the "master" role.  SPI controllers are simple,
> and often
>     can implement slave roles just as well.  (This
> is one of
>     several technical details that bother me...)

I'm afraid my SPI subsystem doesn't either :-(. But it
hopefully shouldn't be hard to add :-)

> 
> 
> One thing I've been looking for in your posts about
> SPI is an
> example of how to configure a system using it.  Some
> examples
> come quickly to mind (all Linux-based boards):
> 
>   * System 1 has one SPI bus with two chipselects
> wired.
>     CS0 is for a DataFlash chip on the motherboard;
> while
>     CS3 is a MMC/SD/SPI socket (cards can be
> added/removed)
>     that's primarily used for DataFlash cards.
> 
>   * System 2 uses SPI to talk to to a multi function
> chip,
>     with sensors for battery, temperature, and
> touchscreen
>     (and others not used) as well as stereo audio
> over what's
>     esentially an I2S channel.  Plus an MMC/SD/SPI
> socket,
>     not yet used in SPI mode (it'd use the MMC
> controller in
>     SPI mode, not yet implemented or required).
> 
>   * System 3 uses SPI to talk to an AVR based
> microcontroller,
>     using application-specific protocols to collect
> sensor
>     data and to issue (robotics) commands.  (AVR is
> an 8-bit
>     microcontroller.  In this case its firmware is
> open, but
>     clearly not running under Linux.  In other
> cases, there's
>     no reason both sides can't run Linux.)
> 
> Given that those SPI devices can't usefully be
> probed, and that
> things like some CAN drivers will cheerfully bind to
> a DataFlash
> device, how do you see systems like those getting
> configured?
> Lots of board-specific logic in the SPI bus and
> device drivers?
> (I'd hope to avoid that, though it clearly works!)
> 

The way my subsystem gets around this is that each
adaptor has a CS table which describes what clients
are on what chip select. This is then used in the core
layer to 'probe' for devices. If a match is found (the
name of a client driver matches a name in the CS
table) then the clients probe function gets called
from which the client could also do some checking
(like I2C) and if it is happy that it is talking to
the device it was written for then it attaches the
cline to the bus.

> 
> Anyway, more detailed comments below.  I'm afraid I
> jumped
> right to the end of your post, where you had the
> highest level
> overview I could find:  the <linux/spi.h> header. 
> Next time
> it might be quicker to just review that part.  :)
> 
> I recently came across a FAQ entry that read "SPI is
> actually a
> lot simpler than for example I2C".  True; but I
> don't think it
> looks that way yet in this API!

Hmm, SPI as no protocol like I2C but, to me, that
makes it harder as you can do more things with it. You
also have different clock modes and speeds unlike I2C.

> 
> - Dave
> 

=== message truncated ===

Hopefully more SPI patches coming soon. You have been
warned :-)

Mark



		
___________________________________________________________ 
To help you stay safe and secure online, we've developed the all new Yahoo! Security Centre. http://uk.security.yahoo.com

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
@ 2005-08-08 23:07 david-b
  2005-08-09  9:38 ` Mark Underwood
  0 siblings, 1 reply; 38+ messages in thread
From: david-b @ 2005-08-08 23:07 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: linux-kernel

Well I like this a bit better, but it's still in transition from
the old I2C style stuff over to a newer driver model based one.
(As other folk have noted, with the "bus" v. "adapter" confusion.)

  - Can you make the SPI messages work with an async API?
    It suffices to have a callback and a "void *" for the
    caller's data.  Those callbacks should be able to start
    the next stage of a device protocol request ... e.g. the
    first one issues some command bytes, and its completion
    callback starts the data transfer.  (It's easy to build
    synchronous models over async ones; but not the other way.)

    (I see Mark Underwood commented that he was working with
    one like that.)

  - The basic thing that bothers me is that, like original I2C,
    the roles and responsibilities here don't correspond in any
    consistent way to the driver model.  For new code like SPI,
    there's no excuse for that to happen.

  - It should probably also not assume Linux can only act in
    the "master" role.  SPI controllers are simple, and often
    can implement slave roles just as well.  (This is one of
    several technical details that bother me...)


One thing I've been looking for in your posts about SPI is an
example of how to configure a system using it.  Some examples
come quickly to mind (all Linux-based boards):

  * System 1 has one SPI bus with two chipselects wired.
    CS0 is for a DataFlash chip on the motherboard; while
    CS3 is a MMC/SD/SPI socket (cards can be added/removed)
    that's primarily used for DataFlash cards.

  * System 2 uses SPI to talk to to a multi function chip,
    with sensors for battery, temperature, and touchscreen
    (and others not used) as well as stereo audio over what's
    esentially an I2S channel.  Plus an MMC/SD/SPI socket,
    not yet used in SPI mode (it'd use the MMC controller in
    SPI mode, not yet implemented or required).

  * System 3 uses SPI to talk to an AVR based microcontroller,
    using application-specific protocols to collect sensor
    data and to issue (robotics) commands.  (AVR is an 8-bit
    microcontroller.  In this case its firmware is open, but
    clearly not running under Linux.  In other cases, there's
    no reason both sides can't run Linux.)

Given that those SPI devices can't usefully be probed, and that
things like some CAN drivers will cheerfully bind to a DataFlash
device, how do you see systems like those getting configured?
Lots of board-specific logic in the SPI bus and device drivers?
(I'd hope to avoid that, though it clearly works!)


Anyway, more detailed comments below.  I'm afraid I jumped
right to the end of your post, where you had the highest level
overview I could find:  the <linux/spi.h> header.  Next time
it might be quicker to just review that part.  :)

I recently came across a FAQ entry that read "SPI is actually a
lot simpler than for example I2C".  True; but I don't think it
looks that way yet in this API!

- Dave



(A) One thing that should still change is the division of
responsibility between "device" and "message".


> +struct spi_msg {
> +	unsigned char addr;	/* slave address        */

You mean which chip select to use?  That's clearly a device
attribute, not a message attribute.  And it sort of assumes
that Linux isn't being the SPI slave this time, too ... :)

Devices on SPI don't have addresses; it's just three wires,
clock, shift in, shift out.  Some controllers don't even
support the notion of multiple chipselects ... unless of
course you throw external gates to switch those lines.
Regardless, the device only sees "selected" or not.


> +	unsigned char flags;
> +#define SPI_M_RD	0x01
> +#define SPI_M_WR	0x02	/**< Write mode flag */

OK, that's per-message.  A single bit would suffice though,
unless you aim to say what to do with the bits flowing in
the other direction?  In which case it's problem that you
only provided a single buffer, below!  And NULL for one
buffer or the other would remove the need for that bit.

SPI the techonlogy is not always half duplex; unlike this API,
or all the Linux SPI code I've seen.  :)


> +#define SPI_M_CSREL	0x04	/**< CS release level at end of the frame  */
> +#define SPI_M_CS	0x08	/**< CS active level at begining of frame (default low ) */

And this seems safely per-message too, since device protocols can
involve requests can be built up out of many messages.  Though
they do assume Linux is being the master...


> +#define SPI_M_CPOL	0x10	/**< Clock polarity */
> +#define SPI_M_CPHA	0x20	/**< Clock Phase */

Aren't those statically known, according to what each of the
chips requires?   Mode 0, Mode 3, and so on.  And that'd seem
to be where variants in the SPI family would all show up...  for
example, the PXA 25x processors have one "NSSP" controller to
handle SPI, SSP, PSP, and Microwire protocols.  MCBSP on OMAP is
even more flexible.


> +#define SPI_M_NOADDR	0x80

What's a "NOADDR"?


> +
> +	unsigned short len;	/* msg length           */
> +	unsigned char *buf;	/* pointer to msg data  */

What kind of memory does this use?  I'll guess it's kernel DMA-ready
memory.  In which case it'd be a good idea to let the drivers provide
pre-mapped memory ... include a dma_addr_t and a flag to let the
bus driver know it doesn't need to map/unmap "buf" this time.

> +	unsigned long clock;

The SPI clock rate should be per-device too.  If it's SPI flash that
takes only 3 MHz max, vs one that handles 20 MHz, that won't change
per-request.  Devices could have a "change speed' request if that's
important, though I happen not to have come across any device drivers
that need that.

On the assumption that the SPI stack shouldn't conflict unduly
with the MMC/SD stack -- since all MMC and SD cards can be
accessed as SPI devices!! -- it's worth pointing out that the MMC
driver framework has a separate call to set i/o characteristics
including the clock speed.

And of course, when Linux is the SPI slave, it just takes whatever
clock the master gives it.

> +};
> 


(B) Similarly the division of responsibility between driver and bus
seems pretty wierd.  Example:

> +struct spi_ops {
> +	int (*open) (struct spi_driver *);
> +	int (*command) (struct spi_driver *, int cmd, void *arg);
> +	void (*close) (struct spi_driver *);
> +};

Odd, all other kernel device driver operations take a DEVICE as
the parameter, not the driver.  And they won't generally have open
or close methods; those would be file_operations notions.  SPI as
a hardware protocol doesn't have a session notion, either...


> +
> +#define SPI_ID_ANY "* ANY *"

How's this supposed to work?  What does it mean?

No driver can possibly look at every SPI device and
safely interrogate its capabilities, so the most
obvious meaning wouldn't seem workable... even just
for a master-mode SPI stack.


> +
> +struct spi_driver {
> +	struct spi_ops *ops;
> +	struct module *owner;
> +	struct device_driver driver;
> +	unsigned int minor;
> +	char *(*supported_ids)[];
> +};
> 

OK, that looks a bit odd.  First, the bit about how the driver ops
don't actually say what device they refer to (above) ... and there's
no typesafe way to even pass a device in!!  Second, "module *owner"
is part of the standard "struct device_driver"; not needed.  Third,
"minor".  It'd be a lot more clear if you just showed me a kernel
driver API without also including userspace API sketches.  :)

Also, it's not clear what the namespace of those IDs should be.
I'd have expected "all device names", as with platform_bus.


(C) And again for busses.

> +struct spi_bus
> +{
> +	struct bus_type the_bus;

Erm, there should be one of those for all the different SPI busses
in the system.  There should one global instance "spi_bus_type" at
/sys/bus/spi that is shared by all of them.


> +	struct platform_device platform_device;

Erm, this seems wrong.  It should be a "struct device *" to fit
the normal model -- where hardware-specific code registers the
devices, which would be a platform_device on most SOC chips -- and
there can be a struct "device" or "class_device" for each logical
SPI bus (I'd go for "class_device" only).


> +	struct list_head bus_list;
> +	struct semaphore lock;

Bus_list?  Part of the bus_type.  I think it no longer needs
a widely scoped semaphore either ...

> +	int (*xfer)( struct spi_bus* this, struct spi_device* device,
>		struct spi_msg msgs[], int num, int flags );

I suppose this can work -- it does for i2c -- but I'd still be
happier seeing this take one async request at a time.  That way the
entire set of protocol interactions can be delegated to async
processing provided by the device driver ... rather than expecting
some mid-layer to handle all possible protocols, synchronously.

> +	int (*chip_cs)( int op, void* context );

What's this for?  Surely any chipselects would be handled by the
driver as part of figuring out which device the transfer goes to...
assuming this driver is taking the "master" role on that specific
SPI link!

> +	struct resource *rsrc;

Well, clearly you aren't using platform_device above to take
advantage of the _counted_ resources it provides.  :(


> +};
> +


> +extern int spi_write(struct spi_device *dev, int addr, const char *buf, int len);
> +extern int spi_read(struct spi_device *dev, int addr, char *buf, int len);

Odd because they assume SPI devices don't know their own addresses,
and that they matter even for Linux running as an SPI slave. :)

And because there's no inlined async fast-pathed version that just
calls the bus adapter code directly with an (async) request.


(D) The device struct looks pretty wierd ...

> +struct spi_device {
> +
> +	void* bus_data;
> +	void* drv_data;

Per-bus data will be held in dev->parent->driver_data.
Per-device data will be held in dev->driver_data.

These aren't needed.

> +	struct semaphore lock;

Doesn't dev->sem suffice?

> +	void (*select)( int op, struct spi_device* this );

Isn't it going to be implicitly activated by making a request to it?
Though, I'm not sure what it'd mean to "select" from an SPI slave.

> +	void *(*alloc) (size_t, int);
> +	void (*free) (const void *);

Why?  There's no explanation ... and clearly it's not to provide
DMA-safe memory, since these don't pass DMA addresses.

> +	unsigned long (*copy_from_user) (void *to, const void *from_user,
> +					 unsigned long len);
> +	unsigned long (*copy_to_user) (void *to_user, const void *from,
> +				       unsigned long len);

These don't belong here at all.  If a driver talking to this
device needs to copy to/from userspace, there are several
ways to do that.  One is to call copy_to_user() and friends;
another is to pin the pages and get their DMA addresses, then
unpin them when the transfer finishes.  (I'd expect the former
would be the normal situation!)

> +	struct device dev;
> +};
> +
> +struct spidev_driver_data {
> +	unsigned int minor;
> +	void *private_data;
> +};
> +
> 

Sorry, I doubt that particular driver data is what I'd want to
be storing in my SPI device driver's dev->driver_data!

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
                     ` (2 preceding siblings ...)
  2005-08-08 14:55   ` Greg KH
@ 2005-08-08 22:58   ` Andrew Morton
  2005-08-10 13:10   ` Pavel Machek
  4 siblings, 0 replies; 38+ messages in thread
From: Andrew Morton @ 2005-08-08 22:58 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List

dmitry pervushin <dpervushin@gmail.com> wrote:
>

A few coding style nits.

> +#include "spi_locals.h"
> +
> +static LIST_HEAD( spi_busses );

Please don't put spaces after '(' and before ')'.

> +		if (0 == strncmp(*id, SPI_ID_ANY, strlen(SPI_ID_ANY))) {

And this trick isn't really needed.  If you do

	if (whatever = constant)

then the compiler will generate a warning.  Please do these comparisons in
the conventional way, with the constant on the right hand side.

> +	if( bus ) {
> +		init_MUTEX( &bus->lock );
> +
> +		bus->platform_device.name = NULL;
> +		bus->the_bus.name = NULL;
> +
> +		strncpy( busname, name ? name : "SPI", sizeof( busname ) );

Lots more extraneous spaces after '(' and before ')'.

> +		if( bus->the_bus.name ) {
> +			strcpy( bus->the_bus.name, fullname );
> +		}

No braces here.

> +		err = bus_register( &bus->the_bus );
> +		if( err ) {
> +			goto out;
> +		}

And here.

> +		list_add_tail( &bus->bus_list, &spi_busses );
> +		bus->platform_device.name = kmalloc( strlen( busname )+1, GFP_KERNEL );
> +		if( bus->platform_device.name ) {
> +			strcpy( bus->platform_device.name, busname );
> +		}

and here...

> +void spi_bus_unregister( struct spi_bus* bus )
> +{
> +	if( bus ) {

We do put a space after `if', so this line should be

	if (bus) {

> +struct spi_bus* spi_bus_find( char* id )

The asterisk goes with the variable, not with the type.  So the above should be

	struct spi_bus *spi_bus_find(char *id)

> +int spi_device_add( struct spi_bus* bus, struct spi_device *dev, char* name)

Here too.

> +int spi_do_probe( struct device* dev, void* device_driver )

You seem to have an awful lot of non-static functions.  Please check
whether they all really need to have global scope.

> +	if (NULL == dev) {

	if (dev == NULL) {

> +static int spidev_do_open(struct device *the_dev, void *context)
> +{
> +	struct spidev_openclose *o = (struct spidev_openclose *) context;

Don't typecast void* when assigning to and from pointers.  It adds clutter
and defeats typechecking.

> +	struct spi_device *dev = SPI_DEV(the_dev);
> +	struct spidev_driver_data *drvdata;
> +
> +	drvdata = (struct spidev_driver_data *) dev_get_drvdata(the_dev);

Ditto.

> +
> +      out_unreg:

Labels go in column zero.

> +	void *(*alloc) (size_t, int);
> +	void (*free) (const void *);
> +	unsigned long (*copy_from_user) (void *to, const void *from_user,
> +					 unsigned long len);
> +	unsigned long (*copy_to_user) (void *to_user, const void *from,
> +				       unsigned long len);

The above names are risky.  Some platform may implement copy_to_user() as a
macro.



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 16:41     ` dmitry pervushin
@ 2005-08-08 18:51       ` Mark Underwood
  0 siblings, 0 replies; 38+ messages in thread
From: Mark Underwood @ 2005-08-08 18:51 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List


--- dmitry pervushin <dpervushin@gmail.com> wrote:

> 
> > Surely this should be locked with bus lock?
> Why not ? Until the transfer on device is not
> finished, the bus will be
> locked. Otherwise, the another device (on the same
> bus) might want to
> transfer something...

OK. My confusion was that we are also working on a SPI
subsystem which works in a different way.
Our SPI subsystem works much like the USB subsystem in
that drivers can queue up transfers and they will get
a callback when the transfer is complete.
Actually I think we have 4 modes of operation which
also allows transfers to be done in interrupt context,
with callback, wait for completion or high priority
(used to transfer high priority messages by bypassing
the transfer queue). I'll see if I can submit a patch
to the mailing list as I know its not only ideas but
code that counts ;-).


> > 
> > -= snip =-
> > 
> > Some other comments:
> > 1) I think you need to fix some of your comments
> > especially those describing how the interfaces
> work.
> > 2) I take it spi adaptor drivers now use
> > spi_bus_register/spi_bus_unregister?
> > 3) Different clients on one bus will want to run
> at
> > different speeds, how will you handle this?
> > 3) This subsystem can only handle small transfers
> like
> > I2C. SPI peripherals like SPI Ethernet devices
> will
> > have to do lots of large transfers and with your
> > current subsystem the device will be forced to
> wait
> > until its transfer has finished (as well as other
> > clients) when it might have other important work
> to
> > do.
> Hmm.. In the sample (it needs some polishing!), the
> bus initiates the
> DMA transfers and waits for completion on it. Do you
> want to have
> something like state machine (the function that will
> be called upon the
> end of transfer ?)

Yes, please see above.

> 
> 
>  Kconfig             |   12 +
>  Makefile            |    7
>  pnxalloc.c          |   70 ++++++
>  pnxalloc.h          |    9
>  spi-pnx010x_atmel.c |   91 ++++++++
>  spipnx-resources.h  |  138 ++++++++++++
>  spipnx.c            |  581
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 
>  spipnx.h            |  309
> +++++++++++++++++++++++++++
>  8 files changed, 1455 insertions(+)
> 
> Index: linux-2.6.10/drivers/spi/spipnx-resources.h
>
===================================================================
> --- /dev/null
> +++ linux-2.6.10/drivers/spi/spipnx-resources.h
> @@ -0,0 +1,138 @@
> +#ifdef CONFIG_MACH_PNX0106_GH450
> +struct resource spipnx_010x_resources_0[] = 
> +{
> +	{ 
> +	  .start = BLAS_SPI0_BASE, 
> +	  .end = BLAS_SPI0_BASE + SZ_4K, 
> +	  .flags = IORESOURCE_MEM,
> +	}, { 
> +	  .start = SPI0_FIFO_DMA_SLAVE_NR, 
> +	  .flags = IORESOURCE_DMA,
> +	},
> +	/*
> +	 * Note that the clocks are shutdown in this order
> and resumed
> +	 * in the opposite order.
> +	 */
> +       	{ 
> +	  .start = CGU_SWITCHBOX_BLAS_SPI0_PCLK_ID, 
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	  .start = CGU_SWITCHBOX_BLAS_SPI0_PCLK_GAT_ID,
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	  .start = CGU_SWITCHBOX_BLAS_SPI0_FIFO_PCLK_ID,
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, { 
> +	   .start =
> CGU_SWITCHBOX_BLAS_SPI0_DUMMY_VPBCLK_ID, 
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	  .start = VH_INTC_INT_NUM_BLAS_SPI0_INT, 
> +	  .flags =  IORESOURCE_IRQ,
> +	}, {
> +	   .flags = 0,
> +	}
> +};
> +
> +struct resource spipnx_010x_resources_1[] = 
> +{
> +	{ 
> +	  .start = BLAS_SPI1_BASE, 
> +	  .end = BLAS_SPI1_BASE + SZ_4K, 
> +	  .flags = IORESOURCE_MEM,
> +	}, { 
> +	  .start = SPI1_FIFO_DMA_SLAVE_NR, 
> +	  .flags = IORESOURCE_DMA,
> +	}, 
> +	/*
> +	 * Note that the clocks are shutdown in this order
> and resumed
> +	 * in the opposite order.
> +	 */
> +	{ 
> +	  .start = CGU_SWITCHBOX_BLAS_SPI1_PCLK_ID, 
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, { 
> +	   .start = CGU_SWITCHBOX_BLAS_SPI1_PCLK_GAT_ID,
> +	   .flags = IORESOURCE_CLOCK_ID,
> +	}, { 
> +	  .start = CGU_SWITCHBOX_BLAS_SPI1_FIFO_PCLK_ID,
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	   .start =
> CGU_SWITCHBOX_BLAS_SPI1_DUMMY_VPBCLK_ID, 
> +	  .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	  .start = VH_INTC_INT_NUM_BLAS_SPI1_INT, 
> +	  .flags =  IORESOURCE_IRQ,
> +	}, {
> +	   .flags = 0,
> +	}
> +};
> +#endif
> +
> +#ifdef CONFIG_MACH_PNX0105_GH448
> +struct resource spipnx_010x_resources[] = 
> +{
> +	{ 
> +	  .start = BLAS_SPI_BASE, 
> +	  .end = BLAS_SPI_BASE + SZ_4K, 
> +	  .flags = IORESOURCE_MEM,
> +	}, { 
> +	  .start = BLAS_SPI_DMA_SLAVE_NR, 
> +	  .flags = IORESOURCE_DMA,
> +	}, 
> +	/*
> +	 * Note that the clocks are shutdown in this order
> and resumed
> +	 * in the opposite order.
> +	 */
> +	{
> +	   .start =  CGU_SWITCHBOX_BLAS_SPI_PCLK_ID,
> +	   .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	   .start = CGU_SWITCHBOX_BLAS_SPI_PCLK_GAT_ID,
> +	   .flags = IORESOURCE_CLOCK_ID,
> +	}, {
> +	   .start = CGU_SWITCHBOX_BLAS_SPI_FIFO_PCLK_ID,
> +	   .flags = IORESOURCE_CLOCK_ID,
> +	}, { 
> +	  .start = VH_INTC_INT_NUM_BLAS_SPI_INT, 
> +	  .flags =  IORESOURCE_IRQ,
> +	}, {
> +	   .flags = 0,
> +	}
> +};
> +#endif
> +
> +#ifdef CONFIG_ARCH_PNX4008
> +struct resource spipnx_4008_resources_0[] = 
> +{
> +	{ 	      
> +	  .start = PNX4008_SPI1_BASE, 
> +	  .end = PNX4008_SPI1_BASE + SZ_4K, 
> +	  .flags = IORESOURCE_MEM,
> +	}, { 
> +	  .start = 11 /* SPI1_DMA_PERIPHERAL_ID */, 
> +	  .flags = IORESOURCE_DMA,
> +	}, { 
> +	  .start = SPI1_INT, 
> +	  .flags =  IORESOURCE_IRQ,
> +	}, {
> +	   .flags = 0,
> +	}
> +};
> +
> +struct resource spipnx_4008_resources_1[] = 
> +{
> +	{ 	      
> +	  .start = PNX4008_SPI2_BASE, 
> +	  .end = PNX4008_SPI2_BASE + SZ_4K, 
> +	  .flags = IORESOURCE_MEM,
> +	}, { 
> +	  .start = 12 /* SPI2_DMA_PERIPHERAL_ID */, 
> +	  .flags = IORESOURCE_DMA,
> +	}, { 
> +	  .start = SPI2_INT, 
> +	  .flags =  IORESOURCE_IRQ,
> +	}, {
> +	   .flags = 0,
> +	}
> +};
> +#endif
> Index: linux-2.6.10/drivers/spi/spipnx.c
> 
=== message truncated ===



	
	
		
___________________________________________________________ 
Yahoo! Messenger - NEW crystal clear PC to PC calling worldwide with voicemail http://uk.messenger.yahoo.com

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 17:35     ` Marcel Holtmann
@ 2005-08-08 17:47       ` Marc Singer
  2005-08-09 17:54         ` Andy Isaacson
  0 siblings, 1 reply; 38+ messages in thread
From: Marc Singer @ 2005-08-08 17:47 UTC (permalink / raw)
  To: Marcel Holtmann, Linux Kernel Mailing List

On Mon, Aug 08, 2005 at 07:35:36PM +0200, Marcel Holtmann wrote:
> > > +	if (NULL == dev || NULL == driver) {
> > 
> > Put the variable on the left side, gcc will complain if you incorrectly
> > put a "=" instead of a "==" here, which is all that you are defending
> > against with this style.
> 
> I think in this case the preferred way is
> 
> 	if (!dev || !driver) {
> 

That's not a guaranteed equivalence in the C standard.  Null pointers
may not be zero.  I don't think we have any targets that work this
way, however there is nothing wrong with explicitly testing for NULL.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 14:55   ` Greg KH
@ 2005-08-08 17:35     ` Marcel Holtmann
  2005-08-08 17:47       ` Marc Singer
  0 siblings, 1 reply; 38+ messages in thread
From: Marcel Holtmann @ 2005-08-08 17:35 UTC (permalink / raw)
  To: Greg KH; +Cc: dmitry pervushin, Linux Kernel Mailing List

Hi Greg,

> No spaces after ( or before ) please.  You do this all over the place in
> the code, please fix it up.
> 
> > +	if (NULL == dev || NULL == driver) {
> 
> Put the variable on the left side, gcc will complain if you incorrectly
> put a "=" instead of a "==" here, which is all that you are defending
> against with this style.

I think in this case the preferred way is

	if (!dev || !driver) {

Regards

Marcel



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08 13:16   ` Mark Underwood
@ 2005-08-08 16:41     ` dmitry pervushin
  2005-08-08 18:51       ` Mark Underwood
  0 siblings, 1 reply; 38+ messages in thread
From: dmitry pervushin @ 2005-08-08 16:41 UTC (permalink / raw)
  To: Mark Underwood; +Cc: Linux Kernel Mailing List


> Surely this should be locked with bus lock?
Why not ? Until the transfer on device is not finished, the bus will be
locked. Otherwise, the another device (on the same bus) might want to
transfer something...
> 
> -= snip =-
> 
> Some other comments:
> 1) I think you need to fix some of your comments
> especially those describing how the interfaces work.
> 2) I take it spi adaptor drivers now use
> spi_bus_register/spi_bus_unregister?
> 3) Different clients on one bus will want to run at
> different speeds, how will you handle this?
> 3) This subsystem can only handle small transfers like
> I2C. SPI peripherals like SPI Ethernet devices will
> have to do lots of large transfers and with your
> current subsystem the device will be forced to wait
> until its transfer has finished (as well as other
> clients) when it might have other important work to
> do.
Hmm.. In the sample (it needs some polishing!), the bus initiates the
DMA transfers and waits for completion on it. Do you want to have
something like state machine (the function that will be called upon the
end of transfer ?)


 Kconfig             |   12 +
 Makefile            |    7
 pnxalloc.c          |   70 ++++++
 pnxalloc.h          |    9
 spi-pnx010x_atmel.c |   91 ++++++++
 spipnx-resources.h  |  138 ++++++++++++
 spipnx.c            |  581 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 
 spipnx.h            |  309 +++++++++++++++++++++++++++
 8 files changed, 1455 insertions(+)

Index: linux-2.6.10/drivers/spi/spipnx-resources.h
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spipnx-resources.h
@@ -0,0 +1,138 @@
+#ifdef CONFIG_MACH_PNX0106_GH450
+struct resource spipnx_010x_resources_0[] = 
+{
+	{ 
+	  .start = BLAS_SPI0_BASE, 
+	  .end = BLAS_SPI0_BASE + SZ_4K, 
+	  .flags = IORESOURCE_MEM,
+	}, { 
+	  .start = SPI0_FIFO_DMA_SLAVE_NR, 
+	  .flags = IORESOURCE_DMA,
+	},
+	/*
+	 * Note that the clocks are shutdown in this order and resumed
+	 * in the opposite order.
+	 */
+       	{ 
+	  .start = CGU_SWITCHBOX_BLAS_SPI0_PCLK_ID, 
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	  .start = CGU_SWITCHBOX_BLAS_SPI0_PCLK_GAT_ID,
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	  .start = CGU_SWITCHBOX_BLAS_SPI0_FIFO_PCLK_ID,
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, { 
+	   .start = CGU_SWITCHBOX_BLAS_SPI0_DUMMY_VPBCLK_ID, 
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	  .start = VH_INTC_INT_NUM_BLAS_SPI0_INT, 
+	  .flags =  IORESOURCE_IRQ,
+	}, {
+	   .flags = 0,
+	}
+};
+
+struct resource spipnx_010x_resources_1[] = 
+{
+	{ 
+	  .start = BLAS_SPI1_BASE, 
+	  .end = BLAS_SPI1_BASE + SZ_4K, 
+	  .flags = IORESOURCE_MEM,
+	}, { 
+	  .start = SPI1_FIFO_DMA_SLAVE_NR, 
+	  .flags = IORESOURCE_DMA,
+	}, 
+	/*
+	 * Note that the clocks are shutdown in this order and resumed
+	 * in the opposite order.
+	 */
+	{ 
+	  .start = CGU_SWITCHBOX_BLAS_SPI1_PCLK_ID, 
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, { 
+	   .start = CGU_SWITCHBOX_BLAS_SPI1_PCLK_GAT_ID,
+	   .flags = IORESOURCE_CLOCK_ID,
+	}, { 
+	  .start = CGU_SWITCHBOX_BLAS_SPI1_FIFO_PCLK_ID,
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	   .start = CGU_SWITCHBOX_BLAS_SPI1_DUMMY_VPBCLK_ID, 
+	  .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	  .start = VH_INTC_INT_NUM_BLAS_SPI1_INT, 
+	  .flags =  IORESOURCE_IRQ,
+	}, {
+	   .flags = 0,
+	}
+};
+#endif
+
+#ifdef CONFIG_MACH_PNX0105_GH448
+struct resource spipnx_010x_resources[] = 
+{
+	{ 
+	  .start = BLAS_SPI_BASE, 
+	  .end = BLAS_SPI_BASE + SZ_4K, 
+	  .flags = IORESOURCE_MEM,
+	}, { 
+	  .start = BLAS_SPI_DMA_SLAVE_NR, 
+	  .flags = IORESOURCE_DMA,
+	}, 
+	/*
+	 * Note that the clocks are shutdown in this order and resumed
+	 * in the opposite order.
+	 */
+	{
+	   .start =  CGU_SWITCHBOX_BLAS_SPI_PCLK_ID,
+	   .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	   .start = CGU_SWITCHBOX_BLAS_SPI_PCLK_GAT_ID,
+	   .flags = IORESOURCE_CLOCK_ID,
+	}, {
+	   .start = CGU_SWITCHBOX_BLAS_SPI_FIFO_PCLK_ID,
+	   .flags = IORESOURCE_CLOCK_ID,
+	}, { 
+	  .start = VH_INTC_INT_NUM_BLAS_SPI_INT, 
+	  .flags =  IORESOURCE_IRQ,
+	}, {
+	   .flags = 0,
+	}
+};
+#endif
+
+#ifdef CONFIG_ARCH_PNX4008
+struct resource spipnx_4008_resources_0[] = 
+{
+	{ 	      
+	  .start = PNX4008_SPI1_BASE, 
+	  .end = PNX4008_SPI1_BASE + SZ_4K, 
+	  .flags = IORESOURCE_MEM,
+	}, { 
+	  .start = 11 /* SPI1_DMA_PERIPHERAL_ID */, 
+	  .flags = IORESOURCE_DMA,
+	}, { 
+	  .start = SPI1_INT, 
+	  .flags =  IORESOURCE_IRQ,
+	}, {
+	   .flags = 0,
+	}
+};
+
+struct resource spipnx_4008_resources_1[] = 
+{
+	{ 	      
+	  .start = PNX4008_SPI2_BASE, 
+	  .end = PNX4008_SPI2_BASE + SZ_4K, 
+	  .flags = IORESOURCE_MEM,
+	}, { 
+	  .start = 12 /* SPI2_DMA_PERIPHERAL_ID */, 
+	  .flags = IORESOURCE_DMA,
+	}, { 
+	  .start = SPI2_INT, 
+	  .flags =  IORESOURCE_IRQ,
+	}, {
+	   .flags = 0,
+	}
+};
+#endif
Index: linux-2.6.10/drivers/spi/spipnx.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spipnx.c
@@ -0,0 +1,581 @@
+/*
+ * drivers/spi/spi-pnx.c
+ *
+ * SPI support for PNX 010x/4008 boards.
+ *
+ * Author: dmitry pervushin <dpervushin@ru.mvista.com>
+ * Based on Dennis Kovalev's <dkovalev@ru.mvista.com> bus driver for pnx010x
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#define CONFIG_ARCH_PNX4008_R2
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/spi.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+
+#if defined(CONFIG_ARCH_PNX010X)
+#include <asm/arch/sdma.h>
+#include <asm/arch/cgu.h>
+#include <vhal/cgu_switchbox.h>
+#endif
+
+#if defined (CONFIG_ARCH_PNX4008)
+#include <asm/arch/dma.h>
+#include <asm/arch/gpio.h>
+#endif
+
+#define IORESOURCE_CLOCK_ID 0x80000000
+#include "spipnx.h"
+#include "spipnx-resources.h"
+#include "spi_locals.h"
+
+
+#define SPIPNX_BUS_TAG "spipnx"
+
+extern int spi_bus_match(struct device *dev, struct device_driver *driver);
+static int spipnx_interrupt( int irq, void* context, struct pt_regs* regs );
+static void spipnx_spi_init( struct spipnx_bus* );
+static int spipnx_xfer_nondma( struct spi_bus* bus, struct spi_device* device, struct spi_msg msgs[], int len, int flags );
+static int spipnx_xfer_dma( struct spi_bus* bus, struct spi_device* device, struct spi_msg msgs[], int len, int flags );
+static int spipnx_xfer( struct spi_bus* bus, struct spi_device* device, struct spi_msg msgs[], int len, int flags );
+static int spipnx_probe( struct device* dev );
+static int spipnx_remove( struct device* dev );
+static int spipnx_suspend(struct device *dev, u32 state, u32 level);
+static int spipnx_resume(struct device *dev, u32 level);
+
+struct bus_type spipnx_template = 
+{
+	.match = spi_bus_match,
+};
+
+struct device_driver spipnx_platform_driver = {
+	.bus = &platform_bus_type,
+	.name = SPIPNX_BUS_TAG,
+	.probe = spipnx_probe,
+	.remove = spipnx_remove,
+	.suspend = spipnx_suspend,
+	.resume = spipnx_resume,
+};
+
+
+static void spipnx_bus_set_mode( struct spipnx_bus* bus, int mode )
+{
+	bus->dma_mode = mode;
+}
+
+struct spipnx_bus spipnx_busses[] = 
+{
+#if defined( CONFIG_MACH_PNX0105_GH448 )
+	[0] = { 
+		.bus = { .rsrc = spipnx_010x_resources },
+		.set_mode = spipnx_bus_set_mode,
+		.state = SPIPNX_STATE_UNINITIALIZED,
+	},
+#elif defined( CONFIG_MACH_PNX0106_GH450 )	
+	[0] = { 
+		.bus = { .rsrc = spipnx_010x_resources_0 },
+		.set_mode = spipnx_bus_set_mode,
+		.state = SPIPNX_STATE_UNINITIALIZED,
+	},
+	[1] = { 
+		.bus = { .rsrc = spipnx_010x_resources_1 },
+		.set_mode = spipnx_bus_set_mode,
+		.state = SPIPNX_STATE_UNINITIALIZED,
+	},
+#elif defined( CONFIG_ARCH_PNX4008 )
+	[0] = { 
+		.bus = { .rsrc = spipnx_4008_resources_0 },
+		.set_mode = spipnx_bus_set_mode,
+		.state = SPIPNX_STATE_UNINITIALIZED,
+	},
+	[1] = { 
+		.bus = { .rsrc = spipnx_4008_resources_1 },
+		.set_mode = spipnx_bus_set_mode,
+		.state = SPIPNX_STATE_UNINITIALIZED,
+	},
+#endif
+};
+
+static void spipnx_free_hardware( struct spipnx_bus* pnx_bus )
+{
+	struct spi_bus *bus = &pnx_bus->bus;
+	struct resource* rsrc = bus->rsrc;
+	
+	down( &bus->lock );
+	for( rsrc = bus->rsrc; rsrc->flags; rsrc ++ ) {
+		if( rsrc->flags & IORESOURCE_IRQ ) {
+			free_irq( rsrc->start, bus->the_bus.name );
+		}
+		else if( rsrc->flags & IORESOURCE_MEM ) {
+			pnx_bus->spi_regs = ioremap( rsrc->start, SZ_4K );
+			release_mem_region( 
+					rsrc->start,
+					rsrc->end - rsrc->start + 1 );
+		}
+		else if( rsrc->flags & IORESOURCE_DMA ) {
+			spipnx_release_dma( pnx_bus );
+		}
+	}
+	up( &bus->lock );
+}
+int spipnx_request_hardware( struct spipnx_bus* pnx_bus )
+{	
+	struct spi_bus *bus = &pnx_bus->bus;
+	int err = 0;
+	struct resource* rsrc;
+	int* clk = pnx_bus->clk_id;
+	
+	down( &bus->lock );
+	pnx_bus->clk_id_num = 0;
+	for( rsrc = bus->rsrc; rsrc->flags; rsrc ++ ) {
+		if( rsrc->flags & IORESOURCE_IRQ ) {
+			pnx_bus->spi_regs->ier = 0;
+			if( !request_irq( 
+					rsrc->start, 
+					spipnx_interrupt,
+					SA_INTERRUPT,
+					bus->the_bus.name,
+					pnx_bus ) ) {
+				err = -ENODEV;
+			}
+		}
+		else if( rsrc->flags & IORESOURCE_MEM ) {
+			pnx_bus->phys_data_reg = (void*)rsrc->start + SPIPNX_DATA;
+			pnx_bus->spi_regs = ioremap( rsrc->start, SZ_4K );
+			if( NULL == request_mem_region( 
+					rsrc->start, 
+					rsrc->end - rsrc->start + 1, 
+					bus->the_bus.name ) ) {
+				err = -ENODEV;
+			}
+		}
+		else if( rsrc->flags & IORESOURCE_DMA ) {
+			pnx_bus->slave_nr = rsrc->start;
+			err = spipnx_request_dma( pnx_bus );
+		}
+		else if( rsrc->flags & IORESOURCE_CLOCK_ID ) {
+			if( pnx_bus->clk_id_num >= ARRAY_SIZE( pnx_bus->clk_id )) {
+				printk( KERN_ERR"%s: too many clocks defined\n", __FUNCTION__ );
+				err = -ENODEV;
+			} else {
+				*clk++ = rsrc->start;
+				pnx_bus->clk_id_num++;
+			}
+		}
+		else {
+			printk( "Unknown resource type 0x%08lx\n", rsrc->flags );
+		}
+	}
+	up( &bus->lock );
+	return err;
+}
+
+void spipnx_set_mode( struct spi_bus* bus, int mode )
+{
+	struct spipnx_bus* spipnx = TO_SPIPNX_BUS( bus );
+
+	down( &bus->lock );	
+	if( bus && spipnx->set_mode ) 
+		spipnx->set_mode( spipnx, mode );
+	up( &bus->lock );
+}
+
+int __init spipnx_init( void )
+{
+	int i;
+	int status;
+	
+	printk( "SPI bus driver for PNX010x\n" );
+	spipnx_platform_driver.name = SPIPNX_BUS_TAG;
+	for( i = 0; i < ARRAY_SIZE( spipnx_busses ); i ++ ) {
+		spipnx_busses[ i ].bus.the_bus = spipnx_template;
+		spipnx_busses[ i ].bus.xfer = spipnx_xfer;
+		spipnx_busses[ i ].dma_mode = 0;
+		spipnx_busses[ i ].dma_channel = -1;
+		status = spi_bus_register( &spipnx_busses[ i ].bus, SPIPNX_BUS_TAG );
+		if( status ) {
+			printk( "SPI Bus#%d cannot be registered: error %d\n", i, status );
+			continue;
+		}
+		spipnx_request_hardware( &spipnx_busses[ i ] );
+		spipnx_busses[ i ].state = SPIPNX_STATE_READY;
+	}
+	driver_register( &spipnx_platform_driver );
+	return 0;	
+}
+
+void __exit spipnx_cleanup( void )
+{
+	int i;
+	
+	driver_unregister( &spipnx_platform_driver );
+	for( i = 0; i < ARRAY_SIZE( spipnx_busses );  i ++ ) {
+		spipnx_busses[ i ].state = SPIPNX_STATE_UNINITIALIZED;
+		spi_bus_unregister( &spipnx_busses[ i ].bus );
+		spipnx_free_hardware( &spipnx_busses[ i ] );
+	}
+}
+
+static irqreturn_t spipnx_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct spipnx_bus *bus = dev_id;
+	unsigned int i_stat = bus->spi_regs->stat;
+	unsigned int ier = bus->spi_regs->ier;
+
+	if ((i_stat & SPIPNX_STAT_EOT) &&
+	    (ier & SPIPNX_IER_EOT)) {
+		bus->spi_regs->ier &= ~SPIPNX_IER_EOT;
+		complete(&bus->op_complete);
+	}
+	if ((i_stat & SPIPNX_STAT_THR) &&
+	    (ier & SPIPNX_IER_THR)) {
+		bus->spi_regs->ier &= ~SPIPNX_IER_THR;
+		complete(&bus->threshold);
+	}
+	bus->spi_regs->stat |= SPIPNX_STAT_CLRINT;	/*     clear interrupt   */
+	bus->spi_regs->timer_status = 0L;
+
+	return IRQ_HANDLED;
+}
+
+static void spipnx_spi_init( struct spipnx_bus* bus)
+{
+	bus->spi_regs->global = SPIPNX_GLOBAL_RESET_SPI |
+	                        SPIPNX_GLOBAL_SPI_ON;	
+	mdelay(5);
+	bus->spi_regs->global = SPIPNX_GLOBAL_SPI_ON;
+	bus->spi_regs->con =
+		SPIPNX_CON_MS | SPIPNX_CLOCK;
+	bus->spi_regs->con |= SPIPNX_CON_BIDIR | ( 7<<9 );
+#ifdef CONFIG_ARCH_PNX4008	
+	bus->spi_regs->stat |= SPIPNX_STAT_CLRINT;
+	bus->spi_regs->con |= SPI_CON_SPI_MODE0 |  SPI_CON_RATE_13 | SPI_CON_THR; 
+#endif	
+}
+
+void  spipnx_default_cs( int type, struct spi_device* device )
+{
+#ifdef CONFIG_MACH_PNX0105_GH448
+	unsigned reg;
+	
+	switch( type ) 
+	{
+	case SELECT:
+	    reg = gpio_read_reg(PADMUX1_MODE0);
+	    gpio_write_reg((reg & ~GPIO_PIN_SPI_CE), PADMUX1_MODE0);
+	    break;
+	case UNSELECT:
+	    reg = gpio_read_reg(PADMUX1_MODE0);
+	    gpio_write_reg(reg | GPIO_PIN_SPI_CE, PADMUX1_MODE0);
+	    break;
+	}
+#endif
+}
+
+static int spipnx_xfer_nondma(struct spi_bus *spibus, struct spi_device* dev, struct spi_msg msgs[],
+			int num, int flags )
+{
+	struct spi_msg *pmsg;
+	int i, j, bptr;
+	raw_spinlock_t lock;
+	void (*chip_cs) ( int type, struct spi_device *device ) = dev->select ? dev->select : spipnx_default_cs;
+	struct spipnx_bus* bus = TO_SPIPNX_BUS( spibus );
+
+	pr_debug("Non-SDMA processing %d messages ...\n", num);
+
+	chip_cs(SELECT, dev );
+	bus->spi_regs->con |= SPIPNX_CON_THR; 
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+		
+		chip_cs( SPIMSG_FLAGS | ( pmsg->flags & SPI_M_CS ), dev );
+		if (pmsg->flags & SPI_M_RD) {	
+			/*  here we have to read data */
+			chip_cs(BEFORE_READ, dev );
+			bptr = 0;
+			init_completion(&bus->op_complete);
+			bus->spi_regs->frm = 0x0000FFFF & (pmsg->len);	
+			bus->spi_regs->con &= ~SPIPNX_CON_RXTX; 
+			bus->spi_regs->con |= SPIPNX_CON_SHIFT_OFF; 
+			bus->spi_regs->ier = SPIPNX_IER_EOT; 
+			bus->spi_regs->dat = 0x00;
+
+			while (bptr < pmsg->len) {
+				if ((pmsg->len) - bptr > FIFO_CHUNK_SIZE) {	/*  if there's data left for another  */
+					init_completion(&bus->threshold);	/*  init wait queue for another chunk */
+					spin_lock_irq(&lock);
+					bus->spi_regs->ier |= SPIPNX_IER_THR; 	/*  chunk, then enable THR interrupt  */
+					spin_unlock_irq(&lock);
+					wait_for_completion(&bus->threshold);
+				} else {
+					wait_for_completion(&bus->op_complete);
+				}
+				for (j = bptr;
+				     j <
+				     (((pmsg->len) - bptr <
+				       FIFO_CHUNK_SIZE) ? (pmsg->len) : (bptr +
+									 FIFO_CHUNK_SIZE));
+				     j++)
+					pmsg->buf[j] = bus->spi_regs->dat;
+				bptr +=
+				    ((pmsg->len) - bptr <
+				     FIFO_CHUNK_SIZE) ? (pmsg->len -
+							 bptr) :
+				    FIFO_CHUNK_SIZE;
+			}
+			pr_debug("[i]:SPI_M_RD: %x\n", *pmsg->buf);
+			chip_cs(AFTER_READ, dev);
+		} 
+		else if( pmsg->flags & SPI_M_WR ) {
+			/*  now we have to transmit data        */
+			chip_cs(BEFORE_WRITE, dev);
+			bus->spi_regs->con |= SPIPNX_CON_RXTX;
+			bus->spi_regs->frm = 0x0000FFFF & (pmsg->len);
+			bptr = 0;
+			init_completion(&bus->op_complete);
+			bus->spi_regs->ier = SPIPNX_IER_EOT;
+
+			while (bptr < pmsg->len) {
+				for (j = bptr;
+				     j <
+				     (((pmsg->len) - bptr <
+				       FIFO_CHUNK_SIZE) ? (pmsg->len) : (bptr +
+									 FIFO_CHUNK_SIZE));
+				     j++) {
+					bus->spi_regs->dat = pmsg->buf[j];
+				}
+				if ((pmsg->len) - bptr > FIFO_CHUNK_SIZE) {
+					init_completion(&bus->threshold);
+					spin_lock_irq(&lock);
+					bus->spi_regs->ier |= SPIPNX_IER_THR;
+					spin_unlock_irq(&lock);
+					wait_for_completion(&bus->threshold);
+				}
+				bptr +=
+				    ((pmsg->len) - bptr <
+				     FIFO_CHUNK_SIZE) ? (pmsg->len -
+							 bptr) :
+				    FIFO_CHUNK_SIZE;
+			}
+			wait_for_completion(&bus->op_complete);
+			pr_debug("[i]:SPI_M_WR: %x\n", *pmsg->buf);
+			chip_cs(AFTER_WRITE, dev);
+		}
+		chip_cs( SPIMSG_FLAGS | ( pmsg->flags & SPI_M_CSREL ), dev );
+	}	
+	chip_cs(UNSELECT, dev);
+	bus->spi_regs->ier &= ~SPIPNX_IER_EOT;
+
+	return num;
+}
+
+int spipnx_xfer_dma(struct spi_bus *spibus, struct spi_device* dev, struct spi_msg msgs[], int num, int flags)
+{
+	struct spi_msg *pmsg;
+	int i;
+	spi_pnx_msg_buff_t *buff;
+	void (*chip_cs) (int type, struct spi_device *device) = dev->select ? dev->select : spipnx_default_cs;
+	struct spipnx_bus *bus = TO_SPIPNX_BUS( spibus );
+
+	pr_debug("SDMA processing %d messages ...\n", num);
+
+	chip_cs(SELECT, dev);
+
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+		buff = (spi_pnx_msg_buff_t *) pmsg->buf;
+		pr_debug("SDMA processing [%d] message ...\n", i);
+
+		chip_cs( SPIMSG_FLAGS | ( pmsg->flags & SPI_M_CS ), dev );
+
+		if (pmsg->flags & SPI_M_RD) {
+			chip_cs(BEFORE_READ, dev);
+
+			init_completion(&bus->op_complete);
+			bus->spi_regs->con &= ~SPIPNX_CON_RXTX; 
+			bus->spi_regs->con |= SPIPNX_CON_SHIFT_OFF; 
+			bus->spi_regs->frm = 0x0000FFFF & (pmsg->len);
+			bus->spi_regs->ier = SPIPNX_IER_EOT; 
+
+			bus->spi_regs->dat = 0;
+			while (!
+			       (bus->spi_regs->
+				stat & SPIPNX_STAT_THR)) {
+					cpu_relax();
+			}
+			spipnx_setup_dma( bus, DMA_MODE_READ, buff,  pmsg->len  );
+			spipnx_start_dma( bus->dma_channel ); 
+			wait_for_completion(&bus->op_complete);
+			spipnx_stop_dma( bus->dma_channel ); 
+			pr_debug("[i]:SPI_M_RD: %x\n", *buff->io_buffer);
+			chip_cs(AFTER_READ, dev);
+		} else {
+			chip_cs(BEFORE_WRITE, dev );
+			bus->spi_regs->con |= SPIPNX_CON_RXTX; 
+			bus->spi_regs->frm = 0x0000FFFF & (pmsg->len);
+			bus->spi_regs->con &= ~SPIPNX_CON_SHIFT_OFF; 
+
+			spipnx_setup_dma( bus, DMA_MODE_WRITE, buff, pmsg->len );
+			bus->spi_regs->ier = SPIPNX_IER_EOT; 
+			init_completion(&bus->op_complete);
+			spipnx_start_dma( bus->dma_channel );
+			wait_for_completion(&bus->op_complete);
+			spipnx_stop_dma( bus->dma_channel);
+			pr_debug("[i]:SPI_M_WR: %x\n", *buff->io_buffer);
+			chip_cs(AFTER_WRITE, dev);
+		}
+		chip_cs( SPIMSG_FLAGS | ( pmsg->flags & SPI_M_CSREL ), dev );
+	}
+	chip_cs(UNSELECT, dev );
+
+	return num;
+}
+
+int spipnx_xfer(struct spi_bus *spibus, struct spi_device* dev, struct spi_msg msgs[], int num, int flags)
+{
+	int  i;
+	struct spi_msg *pmsg;
+	struct spipnx_bus* bus = TO_SPIPNX_BUS( spibus );
+	int status;
+
+	down( &spibus->lock );
+	if( !SPIPNX_IS_READY( bus ) ) {
+		status = -EIO;
+		goto unlock_and_out;
+	}
+	pr_debug("processing %d messages ...\n", num);
+	for (i = 0; i < num; i++) {
+		pmsg = &msgs[i];
+		if ((pmsg->len >= 0xFFFF) || (!pmsg->buf))
+			return -EINVAL;
+		pr_debug("%d) %c - %d bytes\n", i, pmsg->flags ? 'R' : 'W',
+			 pmsg->len);
+	}
+#ifdef CONFIG_ARCH_PNX010X		
+	if (bus->dma_mode) {
+		bus->sdma_config.transfer_size = SDMA_TRANSFER_BYTE;
+		bus->sdma_config.invert_endian = SDMA_INVERT_ENDIAN_NO;
+		bus->sdma_config.companion_channel = 0;
+		bus->sdma_config.companion_enable = SDMA_COMPANION_DISABLE;
+		bus->sdma_config.circular_buffer = SDMA_CIRC_BUF_DISABLE;
+	}
+#endif
+	spipnx_spi_init( bus );
+
+	status =  bus->dma_mode ? 
+		spipnx_xfer_dma( spibus, dev, msgs, num, flags) : 
+		spipnx_xfer_nondma( spibus, dev, msgs, num, flags );
+unlock_and_out:	
+	up( &spibus->lock );
+	return status;
+}
+
+static int spipnx_probe( struct device* dev )
+{
+	struct platform_device *pldev = to_platform_device( dev );
+
+	if( strncmp( pldev->name, SPIPNX_BUS_TAG, strlen( SPIPNX_BUS_TAG ) ) != 0 ) {
+		return -ENODEV;
+	}
+	spipnx_resume( dev, RESUME_POWER_ON );
+	spipnx_resume( dev, RESUME_ENABLE );
+	return 0;
+}
+
+static int spipnx_remove( struct device* dev )
+{
+	spipnx_suspend( dev, SUSPEND_DISABLE, 0 );
+	spipnx_suspend( dev, SUSPEND_POWER_DOWN, 0 );
+	return 0;
+}
+
+static int spipnx_suspend(struct device *dev, u32 state, u32 level)
+{
+	int err = 0;
+#ifdef CONFIG_PM
+	int c;
+	struct platform_device* pldev = to_platform_device(dev);
+	struct spi_bus *spibus = TO_SPI_BUS_PLDEV( pldev );
+	struct spipnx_bus* bus = TO_SPIPNX_BUS( spibus );
+
+	down( &spibus->lock );
+	switch (level) 
+	{
+	case SUSPEND_DISABLE:
+		spipnx_release_dma( bus );
+		bus->state = SPIPNX_STATE_SUSPENDED;              
+		break;
+	case SUSPEND_SAVE_STATE:
+		break;
+	case SUSPEND_POWER_DOWN:
+		for( c = 0; c < bus->clk_id_num; c ++ ) {
+		    cgu_set_clock_run( bus->clk_id[ c ], 0 );
+		}
+		break;
+	}
+	up( &spibus->lock );
+#endif
+	return err;
+}
+
+static int spipnx_resume(struct device *dev, u32 level)
+{
+	int err = 0;
+#ifdef CONFIG_PM
+	int c;
+	struct platform_device* pldev = to_platform_device(dev);
+	struct spi_bus *spibus = TO_SPI_BUS_PLDEV( pldev );
+	struct spipnx_bus* bus = TO_SPIPNX_BUS( spibus );
+
+	down( &spibus->lock );	
+	switch (level) 
+	{
+	case RESUME_POWER_ON:
+		for( c = bus->clk_id_num - 1; c >= 0; c -- ) {
+			cgu_set_clock_run(bus->clk_id[ c ], 1);
+		}
+		break;
+	case RESUME_RESTORE_STATE:
+		break;
+	case RESUME_ENABLE:
+		spipnx_request_dma( bus );
+		bus->state = SPIPNX_STATE_READY;
+		break;
+	}
+	up( &spibus->lock );
+#endif				/* CONFIG_PM */
+	return err;
+}
+
+
+EXPORT_SYMBOL_GPL( spipnx_set_mode );
+MODULE_AUTHOR("dmitry pervushin <dpervushin@ru.mvista.com>");
+MODULE_DESCRIPTION("SPI driver for Philips' PNX boards");
+MODULE_LICENSE("GPL");
+module_init(spipnx_init);
+module_exit(spipnx_cleanup);
Index: linux-2.6.10/drivers/spi/spi-pnx010x_atmel.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spi-pnx010x_atmel.c
@@ -0,0 +1,91 @@
+/*
+ * drivers/spi/spi-pnx010x_atmel.c
+ *
+ * Provides Atmel SPI chip support for pnx010x.
+ *
+ * Author: Dennis Kovalev <dkovalev@ru.mvista.com>
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/spi.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/timer.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+// #include <asm/arch/sdma.h>
+
+#include "spipnx.h"
+#include "spi_locals.h"
+#include "pnxalloc.h"
+
+static int mode = 0;
+static int sdma_mode = 0;
+static char* spi_bus_atmel = "spipnx_00"; 
+MODULE_PARM( sdma_mode, "i" );
+MODULE_PARM( spi_bus_atmel, "s" );
+
+static struct spi_device atmel = {
+	.alloc = NULL,
+	.free = NULL,
+	.copy_from_user = NULL,
+	.copy_to_user = NULL,
+};
+
+int __init atmel_init( void )
+{
+        struct spi_bus* bus = spi_bus_find( spi_bus_atmel );
+        int err;
+
+        if( NULL == bus ) {
+                err = -ENODEV;
+                goto out;
+	}
+				
+	if (sdma_mode) {
+		atmel.alloc = pnx_memory_alloc;
+		atmel.free = pnx_memory_free;
+		atmel.copy_from_user = pnx_copy_from_user;
+		atmel.copy_to_user = pnx_copy_to_user;
+		spipnx_set_mode( bus, 1 );
+	}
+	
+	err = spi_device_add( bus, &atmel, "atmel" );
+	if( err ) {
+		goto out;
+	}
+	printk( "ATMEL driver for the SPI bus loaded.\n" );
+out:
+	if( err ) {
+		printk( "ATMEL driver for the SPI bus: error = %d\n", err );
+	}
+	return err;
+}
+
+void __exit atmel_exit( void )
+{
+	spi_device_del( &atmel );
+}
+
+module_init( atmel_init );
+module_exit( atmel_exit );
+
Index: linux-2.6.10/drivers/spi/spipnx.h
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/spipnx.h
@@ -0,0 +1,309 @@
+/*
+ * SPI support for pnx010x.
+ *
+ * Author: Dennis Kovalev <dkovalev@ru.mvista.com>
+ *
+ * 2004 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+
+//#ifndef CONFIG_ARCH_PNX010X
+//#error This driver cannot be run on non-pnx010x
+//#endif
+
+#ifndef _SPI_PNX_BUS_DRIVER
+#define _SPI_PNX_BUS_DRIVER
+
+#include <asm/arch/platform.h>
+#include <asm/arch/dma.h>
+#include <asm/dma.h>
+
+
+
+#if defined (CONFIG_ARCH_PNX010X)
+#include <vhal/spi_pnx0105.h>
+#include <vhal/ioconf.h>
+#elif defined( CONFIG_ARCH_PNX4008)
+typedef volatile struct  {
+        u32 global ;			/* 0x000             */
+        u32 con ;			/* 0x004             */
+        u32 frm ;			/* 0x008             */
+        u32 ier ;			/* 0x00C             */
+        u32 stat ;			/* 0x010             */
+        u32 dat ;			/* 0x014             */
+        u32 dat_msk ;		/* 0x018             */
+        u32 mask ;			/* 0x01C             */
+        u32 addr ;			/* 0x020             */
+    	u32 _d0[(SPI_ADR_OFFSET_TIMER_CTRL_REG - (SPI_ADR_OFFSET_ADDR + sizeof( u32))) / sizeof( u32 )];
+        u32 timer_ctrl ;		/* 0x400             */
+        u32 timer_count ;		/* 0x404             */
+        u32 timer_status ;		/* 0x408             */
+} vhblas_spiregs, *pvhblas_spiregs;
+#endif
+
+#if defined( CONFIG_ARCH_PNX010X )
+#define SPIPNX_STAT_EOT (1 << VH_BLASSPI_STAT_REG_SPI_EOT_POS )
+#define SPIPNX_STAT_THR (1 << VH_BLASSPI_STAT_REG_SPI_THR_POS )
+#define SPIPNX_IER_EOT  (1 << VH_BLASSPI_IER_REG_SPI_INTEOT_POS )
+#define SPIPNX_IER_THR  (1 << VH_BLASSPI_IER_REG_SPI_INTTHR_POS )
+#define SPIPNX_STAT_CLRINT (1 << VH_BLASSPI_STAT_REG_SPI_INTCLR_POS )
+#define SPIPNX_GLOBAL_RESET_SPI (1 << VH_BLASSPI_GLOBAL_REG_BLRES_SPI_POS)
+#define SPIPNX_GLOBAL_SPI_ON (1 << VH_BLASSPI_GLOBAL_REG_SPI_ON_POS)
+#define SPIPNX_CON_MS (1 << VH_BLASSPI_CON_REG_MS_POS)
+#define SPIPNX_CON_BIDIR (1 << VH_BLASSPI_CON_REG_SPI_BIDIR_POS) 
+#define SPIPNX_CON_RXTX VH_BLASSPI_CON_REG_RXTX_MSK 
+#define SPIPNX_CON_THR  (1 << VH_BLASSPI_CON_REG_THR_POS)
+#define SPIPNX_CON_SHIFT_OFF (1 << VH_BLASSPI_CON_REG_SHIFT_OFF_POS)
+#define SPIPNX_DATA  VH_BLASSPI_ADDR_REG
+#define SPIPNX_CLOCK SPI_CLOCK
+
+#elif defined( CONFIG_ARCH_PNX4008 )
+#define SPIPNX_STAT_EOT SPI_STAT_SPI_EOT
+#define SPIPNX_STAT_THR SPI_STAT_SPI_THR
+#define SPIPNX_IER_EOT SPI_IER_SPI_INTEOT
+#define SPIPNX_IER_THR SPI_IER_SPI_INTTHR
+#define SPIPNX_STAT_CLRINT SPI_STAT_SPI_INTCLR
+
+#define SPIPNX_GLOBAL_RESET_SPI SPI_GLOBAL_BLRES_SPI
+#define SPIPNX_GLOBAL_SPI_ON 	SPI_GLOBAL_SPI_ON
+#define SPIPNX_CON_MS		SPI_CON_MS
+#define SPIPNX_CON_BIDIR	SPI_CON_SPI_BIDIR
+#define SPIPNX_CON_RXTX		SPI_CON_RxTx
+#define SPIPNX_CON_THR		SPI_CON_THR
+#define SPIPNX_CON_SHIFT_OFF	SPI_CON_SHIFT_OFF
+#define SPIPNX_DATA		SPI_ADR_OFFSET_DAT
+#define SPI_PNX4008_CLOCK_IN  104000000
+#define SPI_PNX4008_CLOCK 2670000
+#define SPIPNX_CLOCK ((((SPI_PNX4008_CLOCK_IN / SPI_PNX4008_CLOCK) - 2) / 2) & SPI_CON_RATE_MASK )
+#endif
+
+/* exports */
+extern void spipnx_set_mode( struct spi_bus* bus, int mode );
+
+/* structures */
+#define TO_SPIPNX_BUS(spipnx) container_of( spipnx, struct spipnx_bus, bus )
+struct spipnx_bus 
+{
+	int dma_mode;
+	void (*set_mode)( struct spipnx_bus* this, int dma );
+	struct spi_bus bus;
+	void* phys_data_reg;	
+	int dma_channel;
+#ifdef CONFIG_ARCH_PNX010X
+	sdma_setup_t sdma_setup;
+	sdma_config_t sdma_config;
+#endif	
+ 	vhblas_spiregs*	spi_regs;
+	int slave_nr;
+	int clk_id[ 4 ];
+	int clk_id_num;
+	int sdma_use_count;
+	struct completion threshold;
+	struct completion op_complete;
+	int state;
+};
+#define SPIPNX_STATE_UNINITIALIZED 0
+#define SPIPNX_STATE_READY         1
+#define SPIPNX_STATE_SUSPENDED     2
+
+#define SPIPNX_IS_READY( bus )  ( (bus)->state == SPIPNX_STATE_READY )
+
+typedef enum {
+	SELECT = 0,
+	UNSELECT,
+	INIT,
+	BEFORE_READ,
+	AFTER_READ,
+	BEFORE_WRITE,
+	AFTER_WRITE,
+} spi_pnx010x_cb_type_t;
+#define SPIMSG_FLAGS 0x80
+
+typedef struct {
+	char *io_buffer;
+	dma_addr_t dma_buffer;
+	size_t size;
+} spi_pnx_msg_buff_t;
+
+static inline int spipnx_request_dma ( struct spipnx_bus* bus )
+{
+ 	int err;
+	
+	if( bus->dma_channel != -1 ) {
+		err = bus->dma_channel;
+		goto out;
+	}
+#if defined ( CONFIG_ARCH_PNX010X )
+      	err = sdma_request_channel( bus->bus.the_bus.name, NULL, NULL );
+#elif defined( CONFIG_ARCH_PNX4008 )
+	err = pnx4008_request_channel( bus->bus.the_bus.name, -1, NULL, NULL );
+#endif
+	if( err >= 0 ) {
+		bus->dma_channel = err;
+	}
+out:	
+	return err < 0 ? err : 0;
+}
+
+static inline void spipnx_release_dma( struct spipnx_bus* bus )
+{
+	if( bus->dma_channel >= 0 ) {
+#if defined ( CONFIG_ARCH_PNX010X )
+	      	sdma_release_channel( bus->dma_channel );
+#elif defined( CONFIG_ARCH_PNX4008 )
+		pnx4008_free_channel( bus->dma_channel );
+#endif	
+	}
+	bus->dma_channel = -1;
+}
+
+static inline void spipnx_start_dma( int dma_channel ) 
+{
+#if defined( CONFIG_ARCH_PNX010X )
+	sdma_start_channel( dma_channel );
+#elif defined( CONFIG_ARCH_PNX4008 )
+	pnx4008_dma_ch_enable( dma_channel );
+#else
+#warning spipnx_start_dma does nothing
+	dma_channel = dma_channel;
+#endif	
+}
+
+static inline void spipnx_stop_dma( int dma_channel )
+{
+#if defined( CONFIG_ARCH_PNX010X )
+	sdma_stop_channel( dma_channel );
+#elif defined( CONFIG_ARCH_PNX4008 )
+	pnx4008_dma_ch_disable( dma_channel );
+#else
+#warning spipnx_stop_dma does nothing
+	dma_channel = dma_channel;
+#endif	
+}
+
+static inline int spipnx_setup_dma( struct spipnx_bus* bus, int mode, spi_pnx_msg_buff_t* buff, int len )
+{
+	int err = 0;
+
+#if defined( CONFIG_ARCH_PNX010X )
+	if( mode == DMA_MODE_READ ) {
+		bus->sdma_setup.src_address =  
+			    (unsigned int)bus->phys_data_reg;
+		bus->sdma_setup.dest_address =
+			    (unsigned int)buff->dma_buffer;
+		bus->sdma_setup.trans_length = len;
+		bus->sdma_config.write_slave_nr = MEMORY_DMA_SLAVE_NR;
+		bus->sdma_config.read_slave_nr = bus->slave_nr;
+	}
+	else if( mode == DMA_MODE_WRITE ) {
+		bus->sdma_setup.dest_address =  
+			    (unsigned int)bus->phys_data_reg;
+		bus->sdma_setup.src_address =
+			    (unsigned int)buff->dma_buffer;
+		bus->sdma_setup.trans_length = len;
+		bus->sdma_config.read_slave_nr = MEMORY_DMA_SLAVE_NR;
+		bus->sdma_config.write_slave_nr = bus->slave_nr;
+	}
+	else {
+		err = -EINVAL;
+		goto out;	
+	}
+	err = sdma_pack_config(&bus->sdma_config,
+			 &bus->sdma_setup.packed_config);
+	if( err ) {
+		goto out;
+	}
+	err = sdma_prog_channel(bus->dma_channel, &bus->sdma_setup);
+#elif defined( CONFIG_ARCH_PNX4008 )
+        pnx4008_dma_config_t cfg;
+	pnx4008_dma_ch_config_t ch_cfg;
+        pnx4008_dma_ch_ctrl_t ch_ctrl;
+
+	memset( &cfg, 0, sizeof( cfg ) );
+
+	if( mode == DMA_MODE_READ ) {
+ 	        cfg.dest_addr = buff->dma_buffer;
+		cfg.src_addr = (u32)bus->phys_data_reg;
+	        ch_cfg.flow_cntrl = FC_PER2MEM_DMA;
+		ch_cfg.src_per = bus->slave_nr;
+		ch_cfg.dest_per = 0;
+		ch_ctrl.di = 1;
+	        ch_ctrl.si = 0;
+	}
+	else if ( mode == DMA_MODE_WRITE ) {
+ 	        cfg.src_addr = buff->dma_buffer;
+		cfg.dest_addr = (u32)bus->phys_data_reg;
+	        ch_cfg.flow_cntrl = FC_MEM2PER_DMA;
+		ch_cfg.dest_per = bus->slave_nr;
+		ch_cfg.src_per = 0;
+		ch_ctrl.di = 0;
+	        ch_ctrl.si = 1;
+	}
+	else {
+		err = -EINVAL;
+	}
+
+	ch_cfg.halt = 0;
+        ch_cfg.active = 1;
+	ch_cfg.lock = 0;
+	ch_cfg.itc = 1;
+	ch_cfg.ie = 1;
+        ch_ctrl.tc_mask = 1;
+        ch_ctrl.cacheable = 0;
+        ch_ctrl.bufferable = 0;
+        ch_ctrl.priv_mode = 1;
+	ch_ctrl.dest_ahb1 = 0;
+	ch_ctrl.src_ahb1 = 0;
+	ch_ctrl.dwidth = WIDTH_BYTE;
+        ch_ctrl.swidth = WIDTH_BYTE;
+        ch_ctrl.dbsize = 1;
+        ch_ctrl.sbsize = 1;
+        ch_ctrl.tr_size = len;
+        if( 0 > ( err = pnx4008_dma_pack_config(&ch_cfg, &cfg.ch_cfg) ) ) {
+		goto out;
+	}
+        if( 0 > ( err = pnx4008_dma_pack_control(&ch_ctrl, &cfg.ch_ctrl) ) ) {
+		goto out;
+	}
+        err = pnx4008_config_channel( bus->dma_channel, &cfg);
+#endif
+out:
+	return err;
+}
+
+#define IORESOURCE_CLOCK_ID 0x80000000
+#define FIFO_CHUNK_SIZE		56
+#define SPI_RECEIVE                        0
+#define SPI_TRANSMIT                       1
+
+#define SPI_ENDIAN_SWAP_NO                 0
+#define SPI_ENDIAN_SWAP_YES                1
+
+#if defined( CONFIG_ARCH_PNX010X )
+#define SPI_CLOCK		0x10
+
+/*  GPIO related definitions  */
+#define PADMUX1_PINS		(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_PINS)
+#define PADMUX1_MODE0		(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE0)
+#define PADMUX1_MODE0SET	(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE0_SET)
+#define PADMUX1_MODE0RESET	(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE0_RESET)
+#define PADMUX1_MODE1		(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE1)
+#define PADMUX1_MODE1SET	(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE1_SET)
+#define PADMUX1_MODE1RESET	(IOCONF_PNX0105_PADMUX0 + VH_IOCONF_REG_MODE1_RESET)
+
+#define GPIO_PIN_SPI_CE			(1<<VH_IOCONF_PNX0105_PADMUX1_MSPI_CE_POS)
+#define PADMUX1_BASE_ADDR		IO_ADDRESS(IOCONF_BASE)
+
+#define GPIO_PIN_SPI_CE			(1<<VH_IOCONF_PNX0105_PADMUX1_MSPI_CE_POS)
+#define PADMUX1_BASE_ADDR		IO_ADDRESS(IOCONF_BASE)
+
+#define gpio_write_reg(val,reg)		writel (val, PADMUX1_BASE_ADDR + reg)
+#define gpio_read_reg(reg)		readl (PADMUX1_BASE_ADDR + reg)
+#endif
+
+#endif // __SPI_PNX_BUS_DRIVER
+
+
+
Index: linux-2.6.10/drivers/spi/pnxalloc.c
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/pnxalloc.c
@@ -0,0 +1,70 @@
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <asm/uaccess.h>
+#include <asm/dma.h>
+
+#include <linux/spi.h>
+
+#include "spipnx.h"
+#include "pnxalloc.h"
+
+void *pnx_memory_alloc(size_t size, int base)
+{
+	spi_pnx_msg_buff_t *buff;
+
+	buff = (spi_pnx_msg_buff_t *) kmalloc(sizeof(spi_pnx_msg_buff_t), base);
+	buff->size = size;
+	buff->io_buffer =
+	    dma_alloc_coherent(NULL, size, &buff->dma_buffer, base);
+
+	pr_debug
+	    ("%s:allocated memory(%p) for io_buffer = %p dma_buffer = %08x\n",
+	     __FUNCTION__, buff, buff->io_buffer, buff->dma_buffer);
+
+	return (void *)buff;
+}
+
+void pnx_memory_free(const void *data)
+{
+	spi_pnx_msg_buff_t *buff;
+
+	buff = (spi_pnx_msg_buff_t *) data;
+	pr_debug("%s:deleted memory(%p) for io_buffer = %p dma_buffer = %08x\n",
+		 __FUNCTION__, buff, buff->io_buffer, buff->dma_buffer);
+
+	dma_free_coherent(NULL, buff->size, buff->io_buffer, buff->dma_buffer);
+	kfree(buff);
+}
+
+unsigned long pnx_copy_from_user(void *to, const void *from_user,
+				 unsigned long len)
+{
+	spi_pnx_msg_buff_t *buff;
+	int ret;
+
+	buff = (spi_pnx_msg_buff_t *) to;
+	ret = copy_from_user(buff->io_buffer, from_user, len);
+
+	return ret;
+}
+
+unsigned long pnx_copy_to_user(void *to_user, const void *from,
+			       unsigned long len)
+{
+	spi_pnx_msg_buff_t *buff;
+	int ret;
+
+	buff = (spi_pnx_msg_buff_t *) from;
+	ret = copy_to_user(to_user, buff->io_buffer, len);
+
+	return ret;
+}
+
+EXPORT_SYMBOL( pnx_copy_to_user );
+EXPORT_SYMBOL( pnx_copy_from_user );
+EXPORT_SYMBOL( pnx_memory_alloc );
+EXPORT_SYMBOL( pnx_memory_free );
Index: linux-2.6.10/drivers/spi/pnxalloc.h
===================================================================
--- /dev/null
+++ linux-2.6.10/drivers/spi/pnxalloc.h
@@ -0,0 +1,9 @@
+#ifndef __PNX_ALLOC_H
+#define __PNX_ALLOC_H
+void *pnx_memory_alloc(size_t size, int base);
+void pnx_memory_free(const void *data);
+unsigned long pnx_copy_from_user(void *to, const void *from_user,
+				 unsigned long len);
+unsigned long pnx_copy_to_user(void *to_user, const void *from,
+			       unsigned long len);
+#endif
Index: linux-2.6.10/drivers/spi/Kconfig
===================================================================
--- linux-2.6.10.orig/drivers/spi/Kconfig
+++ linux-2.6.10/drivers/spi/Kconfig
@@ -29,5 +29,17 @@
 	  This support is also available as a module.  If so, the module 
 	  will be called spi-dev.
 
+config SPI_PNX
+	tristate "PNX SPI bus support"
+	depends on SPI
+
+config SPI_PNX010X_ATMEL
+	tristate "Atmel Flash chip on PNX010x SPI support"
+	depends on SPI_PNX && ARCH_PNX010X
+
+config SPI_PNX4008_EEPROM
+	tristate "Dummy EEPROM driver"
+	depends on SPI_PNX && ARCH_PNX4008
+
 endmenu
 
Index: linux-2.6.10/drivers/spi/Makefile
===================================================================
--- linux-2.6.10.orig/drivers/spi/Makefile
+++ linux-2.6.10/drivers/spi/Makefile
@@ -4,6 +4,13 @@
 
 obj-$(CONFIG_SPI) += spi-core.o helpers.o
 
+obj-$(CONFIG_SPI_PNX)            += spipnx.o 
+obj-$(CONFIG_SPI_PNX010X_ATMEL)  += spi-pnx010x_atmel.o 
+
+obj-$(CONFIG_ARCH_PNX4008)       += pnxalloc.o
+obj-$(CONFIG_ARCH_PNX010X)       += pnxalloc.o
+
 obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o
 
 ifeq ($(CONFIG_SPI_DEBUG),y)



^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
  2005-08-08 10:41   ` Jiri Slaby
  2005-08-08 13:16   ` Mark Underwood
@ 2005-08-08 14:55   ` Greg KH
  2005-08-08 17:35     ` Marcel Holtmann
  2005-08-08 22:58   ` Andrew Morton
  2005-08-10 13:10   ` Pavel Machek
  4 siblings, 1 reply; 38+ messages in thread
From: Greg KH @ 2005-08-08 14:55 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List

On Mon, Aug 08, 2005 at 01:12:18PM +0400, dmitry pervushin wrote:
> +config SPI
> +	default Y
> +	tristate "SPI support"
> +        default false
> +	help
> +	  Say Y if you need to enable SPI support on your kernel

What about the "the module will be called..." text for this?

> +obj-$(CONFIG_SPI) += spi-core.o helpers.o

Hm, this will not build :(

> +static LIST_HEAD( spi_busses );

No spaces after ( or before ) please.  You do this all over the place in
the code, please fix it up.

> +	if (NULL == dev || NULL == driver) {

Put the variable on the left side, gcc will complain if you incorrectly
put a "=" instead of a "==" here, which is all that you are defending
against with this style.


> +		printk(KERN_ERR
> +		       "%s: error - both dev and driver should not be NULL !\n",
> +		       __FUNCTION__);
> +		found = 0;
> +		goto spi_match_done;
> +	}
> +
> +	if (NULL == spidrv->supported_ids) {
> +		printk
> +		    ("%s: driver has no ids of devices to support, assuming ALL\n",
> +		     __FUNCTION__);

No KERN_ level here.  Also, please use dev_dbg() and friends if you
possibly can, it's much better for log messages.

> +int spi_bus_register( struct spi_bus* bus, char* name )
> +{
> +	int err = -EINVAL;
> +	static int count = 0;
> +	char busname[ BUS_ID_SIZE ];
> +	char fullname[ BUS_ID_SIZE ];
> +
> +	ENTER();	

Tracing?  ick, not really needed...

> +	if( bus ) {
> +		init_MUTEX( &bus->lock );
> +
> +		bus->platform_device.name = NULL;
> +		bus->the_bus.name = NULL;
> +
> +		strncpy( busname, name ? name : "SPI", sizeof( busname ) );
> +		bus->platform_device.id = count++ % 100;
> +		sprintf( fullname, "%s_%02d", busname, bus->platform_device.id);
> +		bus->the_bus.name = kmalloc( strlen( fullname )+1, GFP_KERNEL );
> +		if( bus->the_bus.name ) {
> +			strcpy( bus->the_bus.name, fullname );
> +		}
> +

{ } are not needed for 1 line if statements.

> +/**
> + * spi_add_adapter - register a new SPI bus adapter
> + * @spidev: spi_device structure for the registering adapter
> + *
> + * Make the adapter available for use by clients using name adap->name.
> + * The adap->adapters list is initialised by this function.
> + *
> + * Returns error code ( 0 on success ) ;
> + */
> +struct spi_bus* spi_bus_find( char* id )

Wrong comment.

> +{
> +	struct bus_type* the_bus = find_bus( id );
> +
> +	return the_bus ? container_of( the_bus, struct spi_bus, the_bus ) : NULL;
> +}
> +
> +EXPORT_SYMBOL( spi_bus_find );

EXPORT_SYMBOL_GPL ?


> +int spi_device_add( struct spi_bus* bus, struct spi_device *dev, char* name)

No space between that and the next function?

No comment for a public function ike this?

> +/**
> + * spi_del_adapter - unregister a SPI bus adapter
> + * @dev: spi_device structure to unregister
> + *
> + * Remove an adapter from the list of available SPI Bus adapters.
> + *
> + * Returns error code (0 on success);
> + */
> +
> +void spi_device_del(struct spi_device *dev)

Again, comments out of sync with the code.

> +void spi_driver_del( struct spi_driver* drv )
> +{
> +	driver_unregister( &drv->driver );
> +}
> +/**
> + * spi_transfer - transfer information on an SPI bus

Please, just 1 line of whitespace between functions, not none.

> +/**
> + * spi_write - send data to a device on an SPI bus
> + * @client: registered client structure
> + * @addr: SPI bus address
> + * @buf: buffer for bytes to send
> + * @len: number of bytes to send
> + *
> + * Send len bytes pointed to by buf to device address addr on the SPI bus
> + * described by client.
> + *
> + * Returns the number of bytes transferred, or negative error code.
> + */
> +int spi_write(struct spi_device *dev, int addr, const char *buf, int len)

Comment doesn't match function.  You will catch all of these errors if
you add the files to the kernel api docbook document.

> ===================================================================
> --- linux-2.6.10.orig/drivers/spi/spi-dev.c	1970-01-01 00:00:00.000000000 +0000
> +++ linux-2.6.10/drivers/spi/spi-dev.c	2005-07-15 06:57:39.000000000 +0000
> @@ -0,0 +1,303 @@
> +/*#ifdef CONFIG_DEVFS_FS	

What's with the #ifdef here?

> +
> +/* $Id: common_spi_core-2.patch,v 1.1.2.6 2005/07/15 07:24:40 tpoynor Exp $ */

No CVS ids in kernel code please.

> +/* struct file_operations changed too often in the 2.1 series for nice code */

I don't think this comment is necessary anymore :)

> +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);

You didn't run this through sparse :(

That's enough to start with...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
  2005-08-08 10:41   ` Jiri Slaby
@ 2005-08-08 13:16   ` Mark Underwood
  2005-08-08 16:41     ` dmitry pervushin
  2005-08-08 14:55   ` Greg KH
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 38+ messages in thread
From: Mark Underwood @ 2005-08-08 13:16 UTC (permalink / raw)
  To: dmitry pervushin, Linux Kernel Mailing List


--- dmitry pervushin <dpervushin@gmail.com> wrote:

> Hello all, 
> 
> 
> Here is the spi core patch (slightly redesigned
> again). Now it operates
> with three abstractions:
> a) the spi bus, which is registered in system and is
> resposible for
> general things like registering devices on it,
> handling PM events for
> entire bus, providing bus-wide operations;
> b) the spi device, which is responsible for
> interactions between the
> device and the bus (selecting/deselecting device)
> and PM events for the
> specifi device;
> c) the driver, which is attached to spi devices and
> (possibly) provide
> interface to the upper level like block device
> interface. The spi-dev is
> the good starting point for people who does not want
> anything but simple
> character device access.
> The new abstraction is the spi bus, which
> functionality was represented
> by spi_device structure.
> 
> Especially for Greg K-H: yes, I ran this code
> through sparse :), thank
> you.
> 

Please can we have an example client driver as it
would aid understanding :-). But in the mean time.

-= snip =-

+/**
+ * spi_add_adapter - register a new SPI bus adapter
+ * @spidev: spi_device structure for the registering
adapter
+ *
+ * Make the adapter available for use by clients
using name 
adap->name.
+ * The adap->adapters list is initialised by this
function.
+ *
+ * Returns error code ( 0 on success ) ;
+ */
+struct spi_bus* spi_bus_find( char* id )
+{
+	struct bus_type* the_bus = find_bus( id );
+
+	return the_bus ? container_of( the_bus, struct
spi_bus, the_bus ) : 
NULL;
+}

Eh? The comment is for spi_add_adapter but the
function is spi_bus_find! Where is spi_add_adapter?

-= snip =-


+/**
+ * spi_del_adapter - unregister a SPI bus adapter
+ * @dev: spi_device structure to unregister
+ *
+ * Remove an adapter from the list of available SPI
Bus adapters.
+ *
+ * Returns error code (0 on success);
+ */
+
+void spi_device_del(struct spi_device *dev)
+{
+	device_unregister(&dev->dev);
+}

Eh? The comment is for spi_del_adapter but the
function is spi_device_del! Where is spi_del_adapter?

-= snip =-

+/**
+ * spi_transfer - transfer information on an SPI bus
+ * @adap: adapter structure to perform transfer on
+ * @msgs: array of spi_msg structures describing
transfer
+ * @num: number of spi_msg structures
+ *
+ * Transfer the specified messages to/from a device
on the SPI bus.
+ *
+ * Returns number of messages successfully
transferred, otherwise 
negative
+ * error code.
+ */
+int spi_transfer(struct spi_device *dev, struct
spi_msg msgs[], int 
num)
+{
+	int ret = -ENOSYS;
+	struct spi_bus* bus;
+
+	bus = TO_SPI_BUS( dev->dev.bus );
+
+	if (bus->xfer) {
+		down( &dev->lock );
+		ret = bus->xfer(bus, dev, msgs, num, 0);
+		up(&dev->lock);
+	}
+	return ret;
+}

Surely this should be locked with bus lock?

-= snip =-

Some other comments:
1) I think you need to fix some of your comments
especially those describing how the interfaces work.
2) I take it spi adaptor drivers now use
spi_bus_register/spi_bus_unregister?
3) Different clients on one bus will want to run at
different speeds, how will you handle this?
3) This subsystem can only handle small transfers like
I2C. SPI peripherals like SPI Ethernet devices will
have to do lots of large transfers and with your
current subsystem the device will be forced to wait
until its transfer has finished (as well as other
clients) when it might have other important work to
do.

Best Regards,

Mark


	
	
		
___________________________________________________________ 
Yahoo! Messenger - NEW crystal clear PC to PC calling worldwide with voicemail http://uk.messenger.yahoo.com

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH] spi
  2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
@ 2005-08-08 10:41   ` Jiri Slaby
  2005-08-08 13:16   ` Mark Underwood
                     ` (3 subsequent siblings)
  4 siblings, 0 replies; 38+ messages in thread
From: Jiri Slaby @ 2005-08-08 10:41 UTC (permalink / raw)
  To: dmitry pervushin; +Cc: Linux Kernel Mailing List

dmitry pervushin napsal(a):

>Index: linux-2.6.10/drivers/spi/Makefile
>===================================================================
>--- linux-2.6.10.orig/drivers/spi/Makefile	1970-01-01 00:00:00.000000000 +0000
>+++ linux-2.6.10/drivers/spi/Makefile	2005-07-15 06:57:39.000000000 +0000
>@@ -0,0 +1,12 @@
>+#
>+# Makefile for the kernel spi bus driver.
>+#
>+
>+obj-$(CONFIG_SPI) += spi-core.o helpers.o
>  
>
But where are helpers?

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH] spi
  2005-07-10 20:01 [PATCH 3/3] kconfig: linux.pot for all arch Egry Gábor
@ 2005-08-08  9:12 ` dmitry pervushin
  2005-08-08 10:41   ` Jiri Slaby
                     ` (4 more replies)
  0 siblings, 5 replies; 38+ messages in thread
From: dmitry pervushin @ 2005-08-08  9:12 UTC (permalink / raw)
  To: Linux Kernel Mailing List

Hello all, 


Here is the spi core patch (slightly redesigned again). Now it operates
with three abstractions:
a) the spi bus, which is registered in system and is resposible for
general things like registering devices on it, handling PM events for
entire bus, providing bus-wide operations;
b) the spi device, which is responsible for interactions between the
device and the bus (selecting/deselecting device) and PM events for the
specifi device;
c) the driver, which is attached to spi devices and (possibly) provide
interface to the upper level like block device interface. The spi-dev is
the good starting point for people who does not want anything but simple
character device access.
The new abstraction is the spi bus, which functionality was represented
by spi_device structure.

Especially for Greg K-H: yes, I ran this code through sparse :), thank
you.

Signed-off-by: dmitry pervushin <dpervushin@gmail.com>

Kernel-version: 2.6.12

This patch is to provide SPI support on linux

 drivers/Kconfig          |    2
 drivers/Makefile         |    1
 drivers/spi/Kconfig      |   33 ++++
 drivers/spi/Makefile     |   12 +
 drivers/spi/spi-core.c   |  327 +++++++++++++++++++++++++++++++++++++++++++++++ 
 drivers/spi/spi-dev.c    |  303 +++++++++++++++++++++++++++++++++++++++++++
 drivers/spi/spi_locals.h |   10 +
 include/linux/spi.h      |  148 +++++++++++++++++++++
 8 files changed, 836 insertions(+)

Index: linux-2.6.10/drivers/Kconfig
===================================================================
--- linux-2.6.10.orig/drivers/Kconfig	2005-07-15 06:56:55.000000000 +0000
+++ linux-2.6.10/drivers/Kconfig	2005-07-15 06:57:39.000000000 +0000
@@ -42,6 +42,8 @@
 
 source "drivers/i2c/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 source "drivers/w1/Kconfig"
 
 source "drivers/misc/Kconfig"
Index: linux-2.6.10/drivers/Makefile
===================================================================
--- linux-2.6.10.orig/drivers/Makefile	2005-07-15 06:57:21.000000000 +0000
+++ linux-2.6.10/drivers/Makefile	2005-07-15 06:59:04.000000000 +0000
@@ -67,1 +67,2 @@
 obj-$(CONFIG_MMC)		+= mmc/
+obj-$(CONFIG_SPI)               += spi/
Index: linux-2.6.10/drivers/spi/Kconfig
===================================================================
--- linux-2.6.10.orig/drivers/spi/Kconfig	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/drivers/spi/Kconfig	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,33 @@
+#
+# SPI device configuration
+#
+menu "SPI support"
+
+config SPI
+	default Y
+	tristate "SPI support"
+        default false
+	help
+	  Say Y if you need to enable SPI support on your kernel
+
+config SPI_DEBUG
+	bool "SPI debug output" 
+	depends on SPI 
+	default false 
+	help 
+          Say Y there if you'd like to see debug output from SPI drivers.
+	  If unsure, say N
+	
+config SPI_CHARDEV
+	default Y
+	tristate "SPI device interface"
+	depends on SPI
+	help
+	  Say Y here to use spi-* device files, usually found in the /dev
+	  directory on your system.  They make it possible to have user-space
+	  programs use the SPI bus. 
+	  This support is also available as a module.  If so, the module 
+	  will be called spi-dev.
+
+endmenu
+
Index: linux-2.6.10/drivers/spi/Makefile
===================================================================
--- linux-2.6.10.orig/drivers/spi/Makefile	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/drivers/spi/Makefile	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,12 @@
+#
+# Makefile for the kernel spi bus driver.
+#
+
+obj-$(CONFIG_SPI) += spi-core.o helpers.o
+
+obj-$(CONFIG_SPI_CHARDEV) += spi-dev.o
+
+ifeq ($(CONFIG_SPI_DEBUG),y)
+EXTRA_CFLAGS += -DDEBUG
+endif
+
Index: linux-2.6.10/drivers/spi/spi-core.c
===================================================================
--- linux-2.6.10.orig/drivers/spi/spi-core.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/drivers/spi/spi-core.c	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,327 @@
+/*
+ *  linux/drivers/spi/spi-core.c
+ *
+ *  Copyright (C) 2005 MontaVista Software
+ *  Author: dmitry pervushin <dpervushin@ru.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/spi.h>
+
+#include "spi_locals.h"
+
+static LIST_HEAD( spi_busses );
+
+int spi_bus_match(struct device *dev, struct device_driver *driver)
+{
+	struct spi_driver *spidrv = SPI_DRV(driver);
+	struct spi_device *spidev = SPI_DEV(dev);
+	char **id;
+	int found = 0;
+
+	if (NULL == dev || NULL == driver) {
+		printk(KERN_ERR
+		       "%s: error - both dev and driver should not be NULL !\n",
+		       __FUNCTION__);
+		found = 0;
+		goto spi_match_done;
+	}
+
+	if (NULL == spidrv->supported_ids) {
+		printk
+		    ("%s: driver has no ids of devices to support, assuming ALL\n",
+		     __FUNCTION__);
+		found = 1;
+		goto spi_match_done;
+	}
+
+	id = *spidrv->supported_ids;
+	while (*id) {
+		pr_debug
+		    ("Verifying driver's supported id of '%s' against '%s'\n",
+		     *id, spidev->dev.bus_id);
+		if (0 == strncmp(*id, SPI_ID_ANY, strlen(SPI_ID_ANY))) {
+			pr_debug
+			    ("The driver (%p) can be attached to any device (%p)\n",
+			     driver, dev);
+			found = 1;
+			goto spi_match_done;
+		}
+		if (0 == strcmp(*id, spidev->dev.bus_id)) {
+			pr_debug("Done, driver (%p) match the device '%p'\n",
+				 driver, dev);
+			found = 1;
+			goto spi_match_done;
+		}
+		++id;
+	}
+
+	pr_debug("%s: no match\n ", __FUNCTION__);
+	found = 0;
+spi_match_done:	
+	return found;
+}
+
+int spi_bus_register( struct spi_bus* bus, char* name )
+{
+	int err = -EINVAL;
+	static int count = 0;
+	char busname[ BUS_ID_SIZE ];
+	char fullname[ BUS_ID_SIZE ];
+
+	ENTER();	
+	if( bus ) {
+		init_MUTEX( &bus->lock );
+
+		bus->platform_device.name = NULL;
+		bus->the_bus.name = NULL;
+
+		strncpy( busname, name ? name : "SPI", sizeof( busname ) );
+		bus->platform_device.id = count++ % 100;
+		sprintf( fullname, "%s_%02d", busname, bus->platform_device.id);
+		bus->the_bus.name = kmalloc( strlen( fullname )+1, GFP_KERNEL );
+		if( bus->the_bus.name ) {
+			strcpy( bus->the_bus.name, fullname );
+		}
+
+		err = bus_register( &bus->the_bus );
+		if( err ) {
+			goto out;
+		}
+		list_add_tail( &bus->bus_list, &spi_busses );
+		bus->platform_device.name = kmalloc( strlen( busname )+1, GFP_KERNEL );
+		if( bus->platform_device.name ) {
+			strcpy( bus->platform_device.name, busname );
+		}
+		err = platform_device_register( &bus->platform_device );
+	}
+out:
+	if( err ) {
+		if( bus->the_bus.name ) {
+			kfree( bus->the_bus.name );
+		}
+		if( bus->platform_device.name ) {
+			kfree( bus->platform_device.name );
+		}
+		/* TODO: platform_device_unregister */
+	}
+	return err;
+}
+
+void spi_bus_unregister( struct spi_bus* bus )
+{
+	if( bus ) {
+		platform_device_unregister( &bus->platform_device );
+		if( bus->platform_device.name ) {
+			kfree( bus->platform_device.name );
+		}
+		bus_unregister( &bus->the_bus );
+		if( bus->the_bus.name ) {
+			kfree( bus->the_bus.name );
+		}
+		list_del_init( &bus->bus_list );
+	}
+}
+
+/**
+ * spi_add_adapter - register a new SPI bus adapter
+ * @spidev: spi_device structure for the registering adapter
+ *
+ * Make the adapter available for use by clients using name adap->name.
+ * The adap->adapters list is initialised by this function.
+ *
+ * Returns error code ( 0 on success ) ;
+ */
+struct spi_bus* spi_bus_find( char* id )
+{
+	struct bus_type* the_bus = find_bus( id );
+
+	return the_bus ? container_of( the_bus, struct spi_bus, the_bus ) : NULL;
+}
+
+EXPORT_SYMBOL( spi_bus_find );
+int spi_device_add( struct spi_bus* bus, struct spi_device *dev, char* name)
+{
+	int err;
+
+	memset(&dev->dev, 0, sizeof(dev->dev));
+
+	if (dev->dev.parent == NULL)
+		dev->dev.parent = &platform_bus;
+	if( name ) {
+		strncpy( dev->dev.bus_id, name, sizeof( dev->dev.bus_id ) );
+	} else {
+		snprintf( dev->dev.bus_id, sizeof( dev->dev.bus_id ), "DEV%p_%s", dev, bus->the_bus.name );
+	}
+	dev->dev.bus = &bus->the_bus;
+	init_MUTEX( &dev->lock );
+
+	err = device_register(&dev->dev);
+	pr_debug("device_register (%p) status = %d\n", &dev->dev, err);
+	return err;
+}
+
+/**
+ * spi_del_adapter - unregister a SPI bus adapter
+ * @dev: spi_device structure to unregister
+ *
+ * Remove an adapter from the list of available SPI Bus adapters.
+ *
+ * Returns error code (0 on success);
+ */
+
+void spi_device_del(struct spi_device *dev)
+{
+	device_unregister(&dev->dev);
+}
+
+int spi_do_probe( struct device* dev, void* device_driver )
+{
+	struct device_driver* drv = device_driver;
+	
+	if( dev->bus->match && !dev->bus->match( dev, drv ) ) {
+		goto do_probe_out;
+	}
+	if( drv->probe ) {
+		if( 0 == drv->probe( dev ) ) {
+			dev->driver = drv;
+			device_bind_driver( dev );
+		}	
+	}
+do_probe_out:	
+	return 0;
+}
+int spi_driver_add( struct spi_driver* drv )
+{
+	int err;
+	struct list_head* entry;
+       
+	err = driver_register( &drv->driver );
+	if( err ) {
+		goto out;
+	}
+
+	err = kobject_set_name(&drv->driver.kobj, "%s", drv->driver.name);
+	if( err ) {
+		goto out;
+	}
+	err = kobject_register(&drv->driver.kobj);
+	if( err ) {
+		goto out;
+	}
+	list_for_each( entry, &spi_busses ) {
+		struct spi_bus* bus = container_of( entry, struct spi_bus, bus_list );
+		bus_for_each_dev( &bus->the_bus, NULL, &drv->driver, spi_do_probe );
+	}
+	module_add_driver( drv->driver.owner, &drv->driver );
+out:
+	return err;	
+}
+
+void spi_driver_del( struct spi_driver* drv )
+{
+	driver_unregister( &drv->driver );
+}
+/**
+ * spi_transfer - transfer information on an SPI bus
+ * @adap: adapter structure to perform transfer on
+ * @msgs: array of spi_msg structures describing transfer
+ * @num: number of spi_msg structures
+ *
+ * Transfer the specified messages to/from a device on the SPI bus.
+ *
+ * Returns number of messages successfully transferred, otherwise negative
+ * error code.
+ */
+int spi_transfer(struct spi_device *dev, struct spi_msg msgs[], int num)
+{
+	int ret = -ENOSYS;
+	struct spi_bus* bus;
+
+	bus = TO_SPI_BUS( dev->dev.bus );
+
+	if (bus->xfer) {
+		down( &dev->lock );
+		ret = bus->xfer(bus, dev, msgs, num, 0);
+		up(&dev->lock);
+	}
+	return ret;
+}
+
+/**
+ * spi_write - send data to a device on an SPI bus
+ * @client: registered client structure
+ * @addr: SPI bus address
+ * @buf: buffer for bytes to send
+ * @len: number of bytes to send
+ *
+ * Send len bytes pointed to by buf to device address addr on the SPI bus
+ * described by client.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int spi_write(struct spi_device *dev, int addr, const char *buf, int len)
+{
+	struct spi_msg msg;
+	int ret;
+
+	msg.addr = addr;
+	msg.flags = 0;
+	msg.buf = (char *)buf;
+	msg.len = len;
+
+	ret = spi_transfer(dev, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+/**
+ * spi_read - receive data from a device on an SPI bus
+ * @client: registered client structure
+ * @addr: SPI bus address
+ * @buf: buffer for bytes to receive
+ * @len: number of bytes to receive
+ *
+ * Receive len bytes from device address addr on the SPI bus described by
+ * client to a buffer pointed to by buf.
+ *
+ * Returns the number of bytes transferred, or negative error code.
+ */
+int spi_read(struct spi_device *dev, int addr, char *buf, int len)
+{
+	struct spi_msg msg;
+	int ret;
+
+	msg.addr = addr;
+	msg.flags = SPI_M_RD;
+	msg.buf = buf;
+	msg.len = len;
+
+	ret = spi_transfer(dev, &msg, 1);
+	return ret == 1 ? len : ret;
+}
+
+MODULE_LICENSE( "GPL" );
+MODULE_AUTHOR( "dmitry pervushin <dpervushin@ru.mvista.com>" );
+
+EXPORT_SYMBOL_GPL(spi_driver_add);
+EXPORT_SYMBOL_GPL(spi_driver_del);
+EXPORT_SYMBOL_GPL(spi_device_add);
+EXPORT_SYMBOL_GPL(spi_device_del);
+EXPORT_SYMBOL_GPL(spi_bus_match);
+EXPORT_SYMBOL_GPL(spi_transfer);
+EXPORT_SYMBOL_GPL(spi_write);
+EXPORT_SYMBOL_GPL(spi_read);
Index: linux-2.6.10/drivers/spi/spi-dev.c
===================================================================
--- linux-2.6.10.orig/drivers/spi/spi-dev.c	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/drivers/spi/spi-dev.c	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,303 @@
+/*#ifdef CONFIG_DEVFS_FS	
+
+    spi-dev.c - spi-bus driver, char device interface  
+
+    Copyright (C) 2005 MontaVista Software
+    Author: dmitry pervushin <dpervushin@ru.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, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* Adapted from i2c-dev module by Jamey Hicks <jamey.hicks@compaq.com> */
+
+/* Note that this is a complete rewrite of Simon Vogl's i2c-dev module.
+   But I have used so much of his original code and ideas that it seems
+   only fair to recognize him as co-author -- Frodo */
+
+/* The devfs code is contributed by Philipp Matthias Hahn 
+   <pmhahn@titan.lahn.de> */
+
+/* Modifications to allow work with current spi-core by 
+   Andrey Ivolgin <aivolgin@ru.mvista.com>, Sep 2004
+ */
+
+/* devfs code corrected to support automatic device addition/deletion
+   by Vitaly Wool <vwool@ru.mvista.com> (C) 2004 MontaVista Software, Inc. 
+ */
+
+/* $Id: common_spi_core-2.patch,v 1.1.2.6 2005/07/15 07:24:40 tpoynor Exp $ */
+
+#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>
+#include "spi_locals.h"
+
+#define SPI_TRANSFER_MAX	65535
+
+/* struct file_operations changed too often in the 2.1 series for nice code */
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+			   loff_t * offset);
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+			    loff_t * offset);
+
+static int spidev_open(struct inode *inode, struct file *file);
+static int spidev_release(struct inode *inode, struct file *file);
+static int __init spidev_init(void);
+
+static void spidev_cleanup(void);
+
+static int spidev_probe(struct device *dev);
+static int spidev_remove(struct device *dev);
+
+static struct file_operations spidev_fops = {
+	.owner = THIS_MODULE,
+	.llseek = no_llseek,
+	.read = spidev_read,
+	.write = spidev_write,
+	.open = spidev_open,
+	.release = spidev_release,
+};
+
+static struct class_simple *spidev_class;
+
+static struct spi_driver spidev_driver = {
+	.owner = THIS_MODULE,
+	.driver = {
+		   .name = "generic_spi",
+		   .probe = spidev_probe,
+		   .remove = spidev_remove,
+		   },
+	.supported_ids = NULL,
+	.minor = 0,
+};
+
+static int spidev_probe(struct device *dev)
+{
+	struct spidev_driver_data *drvdata;
+
+	if (NULL == dev) {
+		printk(KERN_ERR "%s: probing the NULL device!\n", __FUNCTION__);
+		return -EFAULT;
+	}
+
+	drvdata = kmalloc(sizeof(struct spidev_driver_data), GFP_KERNEL);
+	if (NULL == drvdata) {
+		pr_debug("%s: allocating drvdata failed\n", __FUNCTION__);
+		return -ENOMEM;
+	}
+
+	drvdata->minor = spidev_driver.minor++;
+	pr_debug("%s: setting device's(%p) minor to %d\n",
+		 __FUNCTION__, dev, drvdata->minor);
+	dev_set_drvdata(dev, drvdata);
+
+	class_simple_device_add( spidev_class, 
+			 MKDEV( SPI_MAJOR, drvdata->minor ),
+		         NULL, "spi%d", drvdata->minor );
+
+	pr_debug("%s: Registered as minor %d\n", __FUNCTION__, drvdata->minor);
+	return 0;
+}
+
+static int spidev_remove(struct device *dev)
+{
+	struct spidev_driver_data *drvdata;
+
+	if (NULL == dev) {
+		printk(KERN_ERR "%s: removing the NULL device\n", __FUNCTION__);
+	}
+
+	drvdata = (struct spidev_driver_data *) dev_get_drvdata(dev);
+	if (NULL == drvdata) {
+		pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__);
+		return -ENODEV;
+	}
+	class_simple_device_remove( MKDEV( SPI_MAJOR, drvdata->minor ) );
+	kfree(drvdata);
+	pr_debug("%s: device removed\n", __FUNCTION__);
+	return 0;
+}
+
+static ssize_t spidev_read(struct file *file, char *buf, size_t count,
+			   loff_t * offset)
+{
+	char *tmp;
+	int ret;
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+	unsigned long (*cpy_to_user) (void *to_user, const void *from,
+				      unsigned long len);
+	void *(*alloc) (size_t, int);
+	void (*free) (const void *);
+
+	if (count > SPI_TRANSFER_MAX)
+		count = SPI_TRANSFER_MAX;
+
+	cpy_to_user = dev->copy_to_user ? dev->copy_to_user : copy_to_user;
+	alloc = dev->alloc ? dev->alloc : kmalloc;
+	free = dev->free ? dev->free : kfree;
+
+	/* copy user space data to kernel space. */
+	tmp = alloc(count, GFP_KERNEL);
+	if (tmp == NULL)
+		return -ENOMEM;
+
+	ret = spi_read( dev, 0, tmp, count);
+	if (ret >= 0)
+		ret = cpy_to_user(buf, tmp, count) ? -EFAULT : ret;
+	free(tmp);
+	return ret;
+}
+
+static ssize_t spidev_write(struct file *file, const char *buf, size_t count,
+			    loff_t * offset)
+{
+	int ret;
+	char *tmp;
+	struct spi_device *dev = (struct spi_device *)file->private_data;
+	unsigned long (*cpy_from_user) (void *to, const void *from_user,
+					unsigned long len);
+	void *(*alloc) (size_t, int);
+	void (*free) (const void *);
+
+	if (count > SPI_TRANSFER_MAX)
+		count = SPI_TRANSFER_MAX;
+
+	cpy_from_user =
+	    dev->copy_from_user ? dev->copy_from_user : copy_from_user;
+	alloc = dev->alloc ? dev->alloc : kmalloc;
+	free = dev->free ? dev->free : kfree;
+
+	/* copy user space data to kernel space. */
+	tmp = alloc(count, GFP_KERNEL);
+	if (tmp == NULL)
+		return -ENOMEM;
+
+	if (cpy_from_user(tmp, buf, count)) {
+		free(tmp);
+		return -EFAULT;
+	}
+
+	pr_debug("spi-%d writing %d bytes.\n", MINOR(file->f_dentry->d_inode->i_rdev), count);
+	ret = spi_write( dev, 0, tmp, count);
+	free(tmp);
+	return ret;
+}
+
+struct spidev_openclose {
+	unsigned int minor;
+	struct file *file;
+};
+
+static int spidev_do_open(struct device *the_dev, void *context)
+{
+	struct spidev_openclose *o = (struct spidev_openclose *) context;
+	struct spi_device *dev = SPI_DEV(the_dev);
+	struct spidev_driver_data *drvdata;
+
+	drvdata = (struct spidev_driver_data *) dev_get_drvdata(the_dev);
+	if (NULL == drvdata) {
+		pr_debug("%s: oops, drvdata is NULL !\n", __FUNCTION__);
+		return 0;
+	}
+
+	pr_debug("drvdata->minor = %d vs %d\n", drvdata->minor, o->minor);
+	if (drvdata->minor == o->minor) {
+		get_device(&dev->dev);
+		o->file->private_data = dev;
+		return 1;
+	}
+	return 0;
+}
+
+int spidev_open(struct inode *inode, struct file *file)
+{
+	struct spidev_openclose o;
+	int status;
+
+	o.minor = iminor(inode);
+	o.file = file;
+	status = driver_for_each_dev(&spidev_driver.driver, &o, spidev_do_open);
+	if (status == 0) {
+		status = -ENODEV;
+	}
+	return status < 0 ? status : 0;
+}
+
+static int spidev_release(struct inode *inode, struct file *file)
+{
+	struct spi_device *dev = file->private_data;
+
+	if (dev) {
+		put_device(&dev->dev);
+	}
+	file->private_data = NULL;
+
+	return 0;
+}
+
+static int __init spidev_init(void)
+{
+	int res;
+
+	if (0 != (res = register_chrdev(SPI_MAJOR, "spi", &spidev_fops))) {
+		goto out;
+	}
+
+	spidev_class = class_simple_create(THIS_MODULE, "spi" );
+	if ( IS_ERR( spidev_class)) {
+		printk( KERN_ERR"%s: error creating class\n", __FUNCTION__ );
+		res = -EINVAL;
+		goto out_unreg;
+	}
+
+	if (0 != (res = spi_driver_add(&spidev_driver))) {
+		goto out_unreg;
+	}
+	
+	printk( "SPI /dev entries driver.\n" );
+
+	return 0;
+
+      out_unreg:
+	unregister_chrdev(SPI_MAJOR, "spi");
+      out:
+	printk(KERN_ERR "%s: Driver initialization failed\n", __FILE__);
+	return res;
+}
+
+static void spidev_cleanup(void)
+{
+	spi_driver_del(&spidev_driver);
+	class_simple_destroy( spidev_class );
+	unregister_chrdev(SPI_MAJOR, "spi");
+}
+
+MODULE_AUTHOR( "dmitry pervushin <dpervushin@ru.mvista.com>" );
+MODULE_DESCRIPTION("SPI /dev entries driver");
+MODULE_LICENSE("GPL");
+
+module_init(spidev_init);
+module_exit(spidev_cleanup);
Index: linux-2.6.10/drivers/spi/spi_locals.h
===================================================================
--- linux-2.6.10.orig/drivers/spi/spi_locals.h	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/drivers/spi/spi_locals.h	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,10 @@
+#ifndef __SPI_LOCAL_H
+#define __SPI_LOCAL_H
+
+#define SPI_DRV( n ) container_of( n, struct spi_driver, driver )
+#define SPI_DEV( n ) container_of( n, struct spi_device, dev )
+
+#define ENTER() pr_debug( "%s: ENTERed\n", __FUNCTION__ )
+#define LEAVE() pr_debug( "%s: LEFT OUT\n", __FUNCTION__ )
+
+#endif				/* __SPI_LOCAL_H */
Index: linux-2.6.10/include/linux/spi.h
===================================================================
--- linux-2.6.10.orig/include/linux/spi.h	1970-01-01 00:00:00.000000000 +0000
+++ linux-2.6.10/include/linux/spi.h	2005-07-15 06:57:39.000000000 +0000
@@ -0,0 +1,148 @@
+/*
+ *  linux/include/linux/spi/spi.h
+ *
+ *  Copyright (C) 2005 MontaVista Software
+ *  Author: dmitry pervushin <dpervushin@ru.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>
+
+struct spi_msg {
+	unsigned char addr;	/* slave address        */
+	unsigned char flags;
+#define SPI_M_RD	0x01
+#define SPI_M_WR	0x02	/**< Write mode flag */
+#define SPI_M_CSREL	0x04	/**< CS release level at end of the frame  */
+#define SPI_M_CS	0x08	/**< CS active level at begining of frame ( default low ) */
+#define SPI_M_CPOL	0x10	/**< Clock polarity */
+#define SPI_M_CPHA	0x20	/**< Clock Phase */
+#define SPI_M_NOADDR	0x80
+
+	unsigned short len;	/* msg length           */
+	unsigned char *buf;	/* pointer to msg data  */
+	unsigned long clock;
+};
+
+
+#define SPI_MAJOR	98
+
+struct spi_driver;
+struct spi_device;
+struct spi_bus;
+
+int spi_bus_register( struct spi_bus*, char* name );
+void spi_bus_unregister( struct spi_bus* );
+struct spi_bus* spi_bus_find( char* id );
+
+int spi_device_add( struct spi_bus*, struct spi_device*, char* name );
+void spi_device_del( struct spi_device* );
+
+int spi_driver_add( struct spi_driver* );
+void spi_driver_del( struct spi_driver* );
+
+struct spi_ops {
+	int (*open) (struct spi_driver *);
+	int (*command) (struct spi_driver *, int cmd, void *arg);
+	void (*close) (struct spi_driver *);
+};
+
+#define SPI_ID_ANY "* ANY *"
+
+struct spi_driver {
+	struct spi_ops *ops;
+	struct module *owner;
+	struct device_driver driver;
+	unsigned int minor;
+	char *(*supported_ids)[];
+};
+
+#define TO_SPI_BUS( bus ) container_of( bus, struct spi_bus, the_bus )
+#define TO_SPI_BUS_PLDEV( dev ) container_of( dev, struct spi_bus, platform_device )
+struct spi_bus
+{
+	struct bus_type the_bus;
+	struct platform_device platform_device;
+	struct list_head bus_list;
+	struct semaphore lock;
+	int (*xfer)( struct spi_bus* this, struct spi_device* device, struct spi_msg msgs[], int num, int flags );
+	int (*chip_cs)( int op, void* context );
+	struct resource *rsrc;
+};
+
+#define SPI_DEVICE( dev ) container_of( dev, struct spi_device, dev )
+struct spi_device {
+
+	void* bus_data;
+	void* drv_data;
+
+	struct semaphore lock;
+
+	void (*select)( int op, struct spi_device* this );
+
+	void *(*alloc) (size_t, int);
+	void (*free) (const void *);
+	unsigned long (*copy_from_user) (void *to, const void *from_user,
+					 unsigned long len);
+	unsigned long (*copy_to_user) (void *to_user, const void *from,
+				       unsigned long len);
+	
+	struct device dev;
+};
+
+struct spidev_driver_data {
+	unsigned int minor;
+	void *private_data;
+};
+
+/**
+ * spi_command - send a command to a SPI device driver
+ * @client: registered client structure
+ * @cmd: device driver command
+ * @arg: device driver arguments
+ *
+ * Ask the SPI device driver to perform some function.  Further information
+ * should be sought from the device driver in question.
+ *
+ * Returns negative error code on failure.
+ */
+static inline int spi_command(struct spi_driver *clnt, int cmd, void *arg)
+{
+	struct spi_ops *ops = clnt->ops;
+	int ret = -EINVAL;
+
+	if (ops && ops->command)
+		ret = ops->command(clnt, cmd, arg);
+
+	return ret;
+}
+
+static inline int spi_open(struct spi_driver *clnt)
+{
+	struct spi_ops *ops = clnt->ops;
+	int ret = 0;
+
+	if (ops && ops->open)
+		ret = ops->open(clnt);
+	return ret;
+}
+
+static inline void spi_close(struct spi_driver *clnt)
+{
+	struct spi_ops *ops = clnt->ops;
+	if (ops && ops->close)
+		ops->close(clnt);
+}
+
+extern int spi_write(struct spi_device *dev, int addr, const char *buf, int len);
+extern int spi_read(struct spi_device *dev, int addr, char *buf, int len);
+
+
+#endif				/* SPI_H */



^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2005-10-03 16:26 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2005-09-26 11:12 SPI dmitry pervushin
2005-09-26 12:31 ` SPI Eric Piel
2005-09-26 12:37   ` [spi-devel-general] SPI dmitry pervushin
2005-09-26 16:20 ` SPI Grant Likely
2005-09-27  7:39   ` [spi-devel-general] SPI dmitry pervushin
2005-09-26 16:25 ` SPI Valdis.Kletnieks
2005-09-26 16:46   ` [spi-devel-general] SPI Vitaly Wool
2005-09-26 20:25 ` SPI Jesper Juhl
2005-09-27 12:43 ` SPI Greg KH
2005-09-27 14:27   ` [spi-devel-general] SPI dmitry pervushin
2005-09-27 14:35     ` Greg KH
2005-09-27 14:49       ` dmitry pervushin
2005-09-27 14:54         ` Greg KH
2005-09-27 15:19           ` dmitry pervushin
2005-09-28 13:14           ` [PATCH] SPI dmitry pervushin
  -- strict thread matches above, loose matches on Subject: below --
2005-10-03 16:26 David Brownell
2005-10-03  5:01 David Brownell
2005-10-03  6:20 ` Vitaly Wool
2005-10-03  4:56 David Brownell
2005-09-30 17:59 David Brownell
2005-09-30 18:30 ` Vitaly Wool
2005-09-30 19:20 ` dpervushin
2005-08-08 23:07 [PATCH] spi david-b
2005-08-09  9:38 ` Mark Underwood
2005-07-10 20:01 [PATCH 3/3] kconfig: linux.pot for all arch Egry Gábor
2005-08-08  9:12 ` [PATCH] spi dmitry pervushin
2005-08-08 10:41   ` Jiri Slaby
2005-08-08 13:16   ` Mark Underwood
2005-08-08 16:41     ` dmitry pervushin
2005-08-08 18:51       ` Mark Underwood
2005-08-08 14:55   ` Greg KH
2005-08-08 17:35     ` Marcel Holtmann
2005-08-08 17:47       ` Marc Singer
2005-08-09 17:54         ` Andy Isaacson
2005-08-09 19:05           ` Marc Singer
2005-08-09 19:29             ` Andy Isaacson
2005-08-15  7:51               ` Denis Vlasenko
2005-08-08 22:58   ` Andrew Morton
2005-08-10 13:10   ` Pavel Machek

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).