Subject: [PATCH 1/3] SPI bus core infrastructure

From: Grant Likely <[email-masked]>
Date: Sat, 23 Jul 2005 10:07:38 -0400

Patch to add support for SPI busses.  SPI bus master drivers and SPI
slave drivers register with the SPI infrastructure.

Signed-off-by: Grant Likely <[email-masked]>

diff -ruNp linux/drivers/Kconfig linux-spi/drivers/Kconfig
--- linux/drivers/Kconfig	2005-06-17 15:48:29.000000000 -0400
+++ linux-spi/drivers/Kconfig	2005-07-22 17:08:00.000000000 -0400
@@ -58,4 +58,6 @@ source "drivers/mmc/Kconfig"
 
 source "drivers/infiniband/Kconfig"
 
+source "drivers/spi/Kconfig"
+
 endmenu
diff -ruNp linux/drivers/Makefile linux-spi/drivers/Makefile
--- linux/drivers/Makefile	2005-06-17 15:48:29.000000000 -0400
+++ linux-spi/drivers/Makefile	2005-07-22 17:08:00.000000000 -0400
@@ -61,6 +61,7 @@ obj-$(CONFIG_EISA)		+= eisa/
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq/
 obj-$(CONFIG_MMC)		+= mmc/
 obj-$(CONFIG_INFINIBAND)	+= infiniband/
+obj-$(CONFIG_SPI)		+= spi/
 obj-$(CONFIG_BLK_DEV_SGIIOC4)	+= sn/
 obj-y				+= firmware/
 obj-$(CONFIG_CRYPTO)		+= crypto/
diff -ruNp linux/drivers/spi/Kconfig linux-spi/drivers/spi/Kconfig
--- linux/drivers/spi/Kconfig	1969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/Kconfig	2005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,27 @@
+#
+# SPI bus configuration
+#
+
+menu "SPI support"
+
+config SPI
+	tristate "SPI support"
+	---help---
+	  SPI is a serial bus protocol for connecting between ICs.  SPI
+	  support requires two components; a bus driver plus drivers for
+	  each SPI slave device.
+
+	  Enabling SPI support will only cause SPI device drivers to be
compiled	  into the kernel.  You also need to make sure the platform
setup code
+	  takes care of mapping SPI slaves onto each SPI bus.
+
+	  Typically you will only need this for embedded systems.
+	  Say N if unsure.
+
+comment "SPI Bus Drivers"
+	depends on SPI
+
+comment "SPI slaves"
+	depends on SPI
+
+endmenu
+
diff -ruNp linux/drivers/spi/Makefile linux-spi/drivers/spi/Makefile
--- linux/drivers/spi/Makefile	1969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/Makefile	2005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,5 @@
+#
+# Makefile for the spi core.
+#
+
+obj-$(CONFIG_SPI)			+= spi-core.o
diff -ruNp linux/drivers/spi/spi-core.c linux-spi/drivers/spi/spi-core.c
--- linux/drivers/spi/spi-core.c	1969-12-31 19:00:00.000000000 -0500
+++ linux-spi/drivers/spi/spi-core.c	2005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,156 @@
+/*
+ * drivers/spi/spi-core.c
+ *
+ * Core Serial Peripheral Interface (SPI) bus infrastructure
+ *
+ * Adds support for SPI busses.  SPI master devices and SPI device drivers
+ * register with spi core.  SPI slave devices get attached to SPI masters
+ * and are matched with an SPI device driver using the facillities of the
+ * linux device model.
+ *
+ * SPI is oriented around 'transfers' initiated by the SPI master.  A transfer
+ * begins when the master asserts the 'slave select (SS)' signal.  It then
+ * clocks out data one bit at a time on the "Master Out/Slave In (MOSI)" line
+ * and samples one bit at a time on the "Master In/Slave Out (MISO)" line.
+ * Clocking out and sampling are syncronized with the clock signal which is
+ * driven by the master.  Exactly the same amount of data is transmitted as
+ * is received for each transfer.  The transfer if finished when the master
+ * deasserts the SS signal.
+ *
+ * SPI bus drivers provide a 'transfer' method which accepts an input buffer,
+ * an output buffer and a count.  Slave device drivers use the transfer method
+ * to communicate with the slave device.  It is up to the slave device driver
+ * to decide what to do with the transfered data.
+ *
+ * This infrastructure is for SPI master devices only.  Linux cannot be an
+ * SPI slave with this module.  Theoretically multi-master busses will work
+ * as long as the other master doesn't try to use us as a slave
+ *
+ * Maintainer : Grant Likely <[email-masked]>
+ * 
+ * This file is in the public domain.
+ *
+ */
+ 
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/spi.h>
+#include <asm/uaccess.h>
+
+MODULE_AUTHOR("Grant Likely <[email-masked]>");
+MODULE_DESCRIPTION("Freescale MPC52xx PSC in SPI mode");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* Simple enumeration scheme for assigning bus ids */
+static int spi_next_bus_id = 0;
+
+/* Forward Declarations */
+static int spi_match (struct device *dev, struct device_driver *drv);
+static void spi_bus_release(struct device *dev);
+
+/* Linux device model stuff (for registering a new bus type) */
+struct bus_type spi_bus_type = {
+	.name = "spi",
+	.match = spi_match,
+};
+
+static struct sysfs_ops spi_driver_sysfs_ops = {
+};
+
+static struct kobj_type spi_driver_kobj_type = {
+	.sysfs_ops = &spi_driver_sysfs_ops,
+};
+
+/* Called by device model to match devices to device drivers
+ *
+ * Devices are matched to devices based on the name fields in the spi_dev
+ * and device_driver structures.  If the names match, then the device and
+ * driver can possibly be connected
+ */
+static int spi_match (struct device *dev, struct device_driver *drv)
+{
+	struct spi_device *spi_device = to_spi_dev(dev);
+	return !strncmp(spi_device->name, drv->name, strlen(drv->name));
+}
+
+static void spi_bus_release(struct device *dev)
+{
+}
+
+/* Initialize common SPI bus fields before registering the bus device with the
+ * device model
+ */
+int spi_bus_register(struct spi_bus *spi)
+{
+	snprintf(spi->dev.bus_id, BUS_ID_SIZE, "spi%i", spi_next_bus_id++);
+	spi->dev.release = spi_bus_release;
+
+	return device_register(&spi->dev);
+}
+
+void spi_bus_unregister(struct spi_bus *spi)
+{
+	device_unregister(&spi->dev);
+}
+
+/* Helper function for attaching SPI slave devices to an SPI bus.  Slave
+ * devices should get matched to slave device drivers
+ */
+int spi_device_create(struct spi_bus *bus, char *name, int id)
+{
+	struct spi_device *dev;
+
+	dev = kmalloc(sizeof (struct spi_device), GFP_KERNEL);
+	if (!dev) {
+		return -ENOMEM;
+	}
+	memset(dev, 0, sizeof(struct spi_device));
+
+	strlcpy(dev->name, name, SPI_NAME_SIZE);
+	dev->id = id;
+	snprintf(dev->dev.bus_id, BUS_ID_SIZE, "%s.%i", name, id);
+	dev->dev.parent = &bus->dev;
+	dev->dev.bus = &spi_bus_type;
+
+	return device_register(&dev->dev);
+}
+
+/* Register an SPI slave device driver with the SPI infrastructure */
+int spi_driver_register(struct spi_driver *drv)
+{
+	int err;
+
+	drv->driver.name = drv->name;
+	drv->driver.bus = &spi_bus_type;
+	drv->driver.kobj.ktype = &spi_driver_kobj_type;
+	err = driver_register(&drv->driver);
+	return err;
+}
+
+void spi_driver_unregister(struct spi_driver *drv)
+{
+	driver_unregister(&drv->driver);
+}
+
+static int __init spi_init(void)
+{
+	printk(KERN_INFO "spi-core: initializing\n");
+
+	return bus_register(&spi_bus_type);
+}
+
+static void __exit spi_exit(void)
+{
+	bus_unregister(&spi_bus_type);
+}
+
+module_init(spi_init);
+module_exit(spi_exit);
+EXPORT_SYMBOL(spi_bus_register);
+EXPORT_SYMBOL(spi_bus_unregister);
+EXPORT_SYMBOL(spi_device_create);
+EXPORT_SYMBOL(spi_driver_register);
+EXPORT_SYMBOL(spi_driver_unregister);
+
diff -ruNp linux/include/linux/spi.h linux-spi/include/linux/spi.h
--- linux/include/linux/spi.h	1969-12-31 19:00:00.000000000 -0500
+++ linux-spi/include/linux/spi.h	2005-07-22 17:08:00.000000000 -0400
@@ -0,0 +1,72 @@
+/*
+ * drivers/spi/spi.h
+ *
+ * Core Serial Peripheral Interface (SPI) bus infrastructure
+ *
+ * Adds support for SPI busses.  SPI master devices and SPI device drivers
+ * register with spi core.  SPI slave devices get attached to SPI masters
+ * and are matched with an SPI device driver using the facillities of the
+ * linux device model.
+ *
+ * SPI is oriented around 'transfers' initiated by the SPI master.  A transfer
+ * begins when the master asserts the 'slave select (SS)' signal.  It then
+ * clocks out data one bit at a time on the "Master Out/Slave In (MOSI)" line
+ * and samples one bit at a time on the "Master In/Slave Out (MISO)" line.
+ * Clocking out and sampling are syncronized with the clock signal which is
+ * driven by the master.  Exactly the same amount of data is transmitted as
+ * is received for each transfer.  The transfer if finished when the master
+ * deasserts the SS signal.
+ *
+ * SPI bus drivers provide a 'transfer' method which accepts an input buffer,
+ * an output buffer and a count.  Slave device drivers use the transfer method
+ * to communicate with the slave device.  It is up to the slave device driver
+ * to decide what to do with the transfered data.
+ *
+ * This infrastructure is for SPI master devices only.  Linux cannot be an
+ * SPI slave with this module.  Multi-master SPI busses aren't supported
+ * either.
+ *
+ * Maintainer : Grant Likely <[email-masked]>
+ * 
+ * This file is in the public domain
+ */
+
+struct spi_bus {
+	struct spi_bus_ops *ops;
+	struct device dev;
+	void *input_buff;
+	int   input_len;
+};
+#define to_spi_bus(d) container_of(d, struct spi_bus, dev);
+
+#define SPI_CLKEDGE_RISING	0
+#define SPI_CLKEDGE_FALLING	1
+struct spi_bus_ops {
+	int (*transfer) (struct spi_bus *bus, int id, int clkedge,
+	                 uint8_t *in, const uint8_t *out, size_t count);
+};
+
+struct spi_driver {
+	char *name;
+	struct module *module;
+	struct device_driver driver;
+};
+
+#define SPI_NAME_SIZE	KOBJ_NAME_LEN
+struct spi_device {
+	char name[SPI_NAME_SIZE];
+	int id;
+	struct spi_driver *driver;
+	struct device dev;
+};
+#define to_spi_dev(d) container_of(d, struct spi_device, dev);
+
+
+extern int spi_device_create(struct spi_bus *bus, char *name, int id);
+
+/* Interface functions for registering and unregistering SPI busses */
+extern int spi_bus_register(struct spi_bus *bus);
+extern void spi_bus_unregister(struct spi_bus *bus);
+
+extern int spi_driver_register(struct spi_driver *driver);
+extern void spi_driver_unregister(struct spi_driver *driver);