Skip to contentSkip to conversation

This adds support for SPI slave controller drivers

PATCH In review Ken Mills 3 participants 3 messages Jun 12, 2009 – Jul 7, 2009 SPI

lore
Share & tools
lore

Ken Mills

author
#0 lore raw

PATCH 1/1 This adds support for SPI slave controller drivers

Second try sending SPI core patch.

---
 drivers/spi/spi.c       |  368 ++++++++++++++++++++++++++++++++++++++++++-----
 include/linux/spi/spi.h |  100 ++++++++++++-
 2 files changed, 427 insertions(+), 41 deletions(-)
drivers/spi/spi.c +334 -34
index 8eba98c..504f0ae 100644
@@ -26,19 +26,34 @@
 #include <linux/spi/spi.h>
 
 
-/* SPI bustype and spi_master class are registered after board init code
- * provides the SPI device tables, ensuring that both are present by the
- * time controller driver registration causes spi_devices to "enumerate".
- */
+/* SPI bustype, spi_master and spi_slave class are registered after board
+* init code provides the SPI device tables, ensuring that both are present
+* by the time controller driver registration causes spi_devices
+* to "enumerate".
+*/
+
+/* SPI Slave Support is added for new spi slave devices: It uses common APIs,
+* apart from few new APIs and a spi_slave structure.
+*/
+
 static void spidev_release(struct device *dev)
 {
 	struct spi_device	*spi = to_spi_device(dev);
 
-	/* spi masters may cleanup for released devices */
-	if (spi->master->cleanup)
-		spi->master->cleanup(spi);
+	if (spi->master) {
+		/* spi masters may cleanup for released devices */
+		if (spi->master->cleanup)
+			spi->master->cleanup(spi);
+
+		spi_master_put(spi->master);
+	} else {
+		/* spi slave controller */
+		if (spi->slave->cleanup)
+			spi->slave->cleanup(spi);
+
+		spi_slave_put(spi->slave);
+	}
 
-	spi_master_put(spi->master);
 	kfree(dev);
 }
 
@@ -46,7 +61,6 @@ static ssize_t
 modalias_show(struct device *dev, struct device_attribute *a, char *buf)
 {
 	const struct spi_device	*spi = to_spi_device(dev);
-
 	return sprintf(buf, "%s\n", spi->modalias);
 }
 
@@ -62,14 +76,12 @@ static struct device_attribute spi_dev_attrs[] = {
 static int spi_match_device(struct device *dev, struct device_driver *drv)
 {
 	const struct spi_device	*spi = to_spi_device(dev);
-
 	return strcmp(spi->modalias, drv->name) == 0;
 }
 
 static int spi_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
 	const struct spi_device		*spi = to_spi_device(dev);
-
 	add_uevent_var(env, "MODALIAS=%s", spi->modalias);
 	return 0;
 }
@@ -153,10 +165,13 @@ int spi_register_driver(struct spi_driver *sdrv)
 	sdrv->driver.bus = &spi_bus_type;
 	if (sdrv->probe)
 		sdrv->driver.probe = spi_drv_probe;
+
 	if (sdrv->remove)
 		sdrv->driver.remove = spi_drv_remove;
+
 	if (sdrv->shutdown)
 		sdrv->driver.shutdown = spi_drv_shutdown;
+
 	return driver_register(&sdrv->driver);
 }
 EXPORT_SYMBOL_GPL(spi_register_driver);
@@ -197,28 +212,70 @@ static DEFINE_MUTEX(board_lock);
  */
 struct spi_device *spi_alloc_device(struct spi_master *master)
 {
-	struct spi_device	*spi;
+	struct spi_device	*spi_m_dev;
 	struct device		*dev = master->dev.parent;
 
 	if (!spi_master_get(master))
 		return NULL;
 
-	spi = kzalloc(sizeof *spi, GFP_KERNEL);
-	if (!spi) {
+	spi_m_dev = kzalloc(sizeof *spi_m_dev, GFP_KERNEL);
+	if (!spi_m_dev) {
 		dev_err(dev, "cannot alloc spi_device\n");
 		spi_master_put(master);
 		return NULL;
 	}
 
-	spi->master = master;
-	spi->dev.parent = dev;
-	spi->dev.bus = &spi_bus_type;
-	spi->dev.release = spidev_release;
-	device_initialize(&spi->dev);
-	return spi;
+	spi_m_dev->master = master;
+	spi_m_dev->slave = NULL;
+	spi_m_dev->dev.parent = dev;
+	spi_m_dev->dev.bus = &spi_bus_type;
+	spi_m_dev->dev.release = spidev_release;
+	device_initialize(&spi_m_dev->dev);
+	return spi_m_dev;
 }
 EXPORT_SYMBOL_GPL(spi_alloc_device);
 
+/*
+* spi_alloc_slave_device - Allocate a new SPI device
+* @slave: Controller to which device is connected
+* Context: can sleep
+*
+* Allows a driver to allocate and initialize a spi_device without
+* registering it immediately.  This allows a driver to directly
+* fill the spi_device with device parameters before calling
+* spi_add_slave_device() on it.
+*
+* Caller is responsible to call spi_add_slave_device() on the returned
+* spi_device structure to add it to the SPI slave.  If the caller
+* needs to discard the spi_device without adding it, then it should
+* call spi_dev_slave_put() on it.
+* Returns a pointer to the new device, or NULL.
+*/
+struct spi_device *spi_alloc_slave_device(struct spi_slave *slave)
+{
+	struct spi_device	*spi_s;
+	struct device		*dev = slave->dev.parent;
+
+	if (!spi_slave_get(slave))
+		return NULL;
+
+	spi_s = kzalloc(sizeof *spi_s, GFP_KERNEL);
+	if (!spi_s) {
+		dev_err(dev, "cannot alloc spi_slave_device\n");
+		spi_slave_put(slave);
+		return NULL;
+	}
+
+	spi_s->slave = slave;
+	spi_s->master = NULL;
+	spi_s->dev.parent = dev;
+	spi_s->dev.bus = &spi_bus_type;
+	spi_s->dev.release = spidev_release;
+	device_initialize(&spi_s->dev);
+	return spi_s;
+}
+EXPORT_SYMBOL_GPL(spi_alloc_slave_device);
+
 /**
  * spi_add_device - Add spi_device allocated with spi_alloc_device
  * @spi: spi_device to register
@@ -231,21 +288,29 @@ EXPORT_SYMBOL_GPL(spi_alloc_device);
 int spi_add_device(struct spi_device *spi)
 {
 	static DEFINE_MUTEX(spi_add_lock);
-	struct device *dev = spi->master->dev.parent;
 	int status;
-
-	/* Chipselects are numbered 0..max; validate. */
-	if (spi->chip_select >= spi->master->num_chipselect) {
-		dev_err(dev, "cs%d >= max %d\n",
-			spi->chip_select,
-			spi->master->num_chipselect);
-		return -EINVAL;
-	}
-
+	struct device *dev;
+
+	if (spi->master) {
+		/* Master Controller */
+		dev = spi->master->dev.parent;
+		/* Chipselects are numbered 0..max; validate. */
+		if (spi->chip_select >= spi->master->num_chipselect) {
+			dev_err(dev, "cs%d >= max %d\n",
+				spi->chip_select,
+				spi->master->num_chipselect);
+			return -EINVAL;
+		}
 	/* Set the bus ID string */
 	dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
 			spi->chip_select);
-
+	} else {
+		/* Slave Controller */
+		dev = spi->slave->dev.parent;
+		/* Set the bus ID string */
+		dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->slave->dev),
+				spi->chip_select);
+	}
 
 	/* We need to make sure there's no other device with this
 	 * chipselect **BEFORE** we call setup(), else we'll trash
@@ -265,7 +330,11 @@ int spi_add_device(struct spi_device *spi)
 	 * normally rely on the device being setup.  Devices
 	 * using SPI_CS_HIGH can't coexist well otherwise...
 	 */
-	status = spi->master->setup(spi);
+	if (spi->master)
+		status = spi->master->setup(spi);
+	else
+		status = spi->slave->setup(spi);	/* Slave Controller */
+
 	if (status < 0) {
 		dev_err(dev, "can't %s %s, status %d\n",
 				"setup", dev_name(&spi->dev), status);
@@ -286,6 +355,7 @@ done:
 }
 EXPORT_SYMBOL_GPL(spi_add_device);
 
+
 /**
  * spi_new_device - instantiate one new SPI device
  * @master: Controller to which device is connected
@@ -317,6 +387,8 @@ struct spi_device *spi_new_device(struct spi_master *master,
 	if (!proxy)
 		return NULL;
 
+
+
 	WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));
 
 	proxy->chip_select = chip->chip_select;
@@ -339,6 +411,54 @@ struct spi_device *spi_new_device(struct spi_master *master,
 EXPORT_SYMBOL_GPL(spi_new_device);
 
 /**
+* spi_slave_new_device - instantiate one new SPI device
+* @slave: Controller to which device is connected
+* @chip: Describes the SPI device
+* Context: can sleep
+*
+* On typical mainboards, this is purely internal; and it's not needed
+* after board init creates the hard-wired devices.  Some development
+* platforms may not be able to use spi_register_board_info though, and
+* this is exported so that for example a USB or parport based adapter
+* driver could add devices (which it would learn about out-of-band).
+*
+* Returns the new device, or NULL.
+*/
+struct spi_device *spi_slave_new_device(struct spi_slave *slave,
+				  struct spi_board_info *chip)
+{
+	struct spi_device	*proxy_slave;
+	int			status;
+
+	proxy_slave = spi_alloc_slave_device(slave);
+
+	if (!proxy_slave)
+		return NULL;
+
+	WARN_ON(strlen(chip->modalias) >= sizeof(proxy_slave->modalias));
+
+	proxy_slave->chip_select = chip->chip_select;
+	proxy_slave->max_speed_hz = chip->max_speed_hz;
+	proxy_slave->mode = chip->mode;
+	proxy_slave->irq = chip->irq;
+	strlcpy(proxy_slave->modalias, chip->modalias,
+					sizeof(proxy_slave->modalias));
+	proxy_slave->dev.platform_data = (void *) chip->platform_data;
+	proxy_slave->controller_data = chip->controller_data;
+	proxy_slave->controller_state = NULL;
+
+	status = spi_add_device(proxy_slave);
+	if (status < 0) {
+		spi_dev_put(proxy_slave);
+		return NULL;
+	}
+
+	return proxy_slave;
+}
+EXPORT_SYMBOL_GPL(spi_slave_new_device);
+
+
+/**
  * spi_register_board_info - register SPI devices for a given board
  * @info: array of chip descriptors
  * @n: how many descriptors are provided
@@ -365,6 +485,7 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
 	bi = kmalloc(sizeof(*bi) + n * sizeof *info, GFP_KERNEL);
 	if (!bi)
 		return -ENOMEM;
+
 	bi->n_board_info = n;
 	memcpy(bi->board_info, info, n * sizeof *info);
 
@@ -374,6 +495,7 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
 	return 0;
 }
 
+
 /* FIXME someone should add support for a __setup("spi", ...) that
  * creates board info from kernel command lines
  */
@@ -399,6 +521,28 @@ static void scan_boardinfo(struct spi_master *master)
 	mutex_unlock(&board_lock);
 }
 
+static void spi_slave_scan_boardinfo(struct spi_slave *slave)
+{
+	struct boardinfo	*bi;
+
+	mutex_lock(&board_lock);
+	list_for_each_entry(bi, &board_list, list) {
+		struct spi_board_info	*chip = bi->board_info;
+		unsigned		n;
+
+		for (n = bi->n_board_info; n > 0; n--, chip++) {
+			if (chip->bus_num != slave->bus_num)
+				continue;
+			/* NOTE: this relies on spi_new_device to
+			 * issue diagnostics when given bogus inputs
+			 */
+			(void) spi_slave_new_device(slave, chip);
+
+		}
+	}
+	mutex_unlock(&board_lock);
+}
+
 /*-------------------------------------------------------------------------*/
 
 static void spi_master_release(struct device *dev)
@@ -415,6 +559,19 @@ static struct class spi_master_class = {
 	.dev_release	= spi_master_release,
 };
 
+static void spi_slave_release(struct device *dev)
+{
+	struct spi_slave *slave;
+
+	slave = container_of(dev, struct spi_slave, dev);
+	kfree(slave);
+}
+
+static struct class spi_slave_class = {
+	.name		= "spi_slave",
+	.owner		= THIS_MODULE,
+	.dev_release	= spi_slave_release,
+};
 
 /**
  * spi_alloc_master - allocate SPI master controller
@@ -456,6 +613,47 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
 EXPORT_SYMBOL_GPL(spi_alloc_master);
 
 /**
+* spi_alloc_slave - allocate SPI slave controller
+* @dev: the controller, possibly using the platform_bus
+* @size: how much zeroed driver-private data to allocate; the pointer to this
+*	memory is in the driver_data field of the returned device,
+*	accessible with spi_slave_get_devdata().
+* Context: can sleep
+*
+* This call is used only by SPI master controller drivers, which are the
+* only ones directly touching chip registers.  It's how they allocate
+* an spi_master structure, prior to calling spi_register_slave().
+*
+* This must be called from context that can sleep.  It returns the SPI
+* master structure on success, else NULL.
+*
+* The caller is responsible for assigning the bus number and initializing
+* the master's methods before calling spi_register_slave(); and (after errors
+* adding the device) calling spi_slave_put() to prevent a memory leak.
+*/
+struct spi_slave *spi_alloc_slave(struct device *dev, unsigned size)
+{
+	struct spi_slave	*slave;
+
+	if (!dev)
+		return NULL;
+
+	slave = kzalloc(size + sizeof *slave, GFP_KERNEL);
+	if (!slave)
+		return NULL;
+
+	device_initialize(&slave->dev);
+	slave->dev.class = &spi_slave_class;
+	slave->dev.parent = get_device(dev);
+	spi_slave_set_devdata(slave, &slave[1]);
+
+	return slave;
+}
+EXPORT_SYMBOL_GPL(spi_alloc_slave);
+
+
+
+/**
  * spi_register_master - register SPI master controller
  * @master: initialized master, originally from spi_alloc_master()
  * Context: can sleep
@@ -507,7 +705,8 @@ int spi_register_master(struct spi_master *master)
 	status = device_add(&master->dev);
 	if (status < 0)
 		goto done;
-	dev_dbg(dev, "registered master %s%s\n", dev_name(&master->dev),
+
+	dev_dbg(dev, "spi_register_master() : %s%s\n", dev_name(&master->dev),
 			dynamic ? " (dynamic)" : "");
 
 	/* populate children from any spi device tables */
@@ -518,6 +717,69 @@ done:
 }
 EXPORT_SYMBOL_GPL(spi_register_master);
 
+/**
+* spi_register_slave - register SPI slave controller
+* @master: initialized master, originally from spi_alloc_slave()
+* Context: can sleep
+*
+* SPI slave controllers connect to their drivers using some non-SPI bus,
+* such as the platform bus.  The final stage of probe() in that code
+* includes calling spi_register_slave() to hook up to this SPI bus glue.
+*
+* SPI controllers use board specific (often SOC specific) bus numbers,
+* and board-specific addressing for SPI devices combines those numbers
+* with chip select numbers.  Since SPI does not directly support dynamic
+* device identification, boards need configuration tables telling which
+* chip is at which address.
+*
+* This must be called from context that can sleep.  It returns zero on
+* success, else a negative error code (dropping the slave's refcount).
+* After a successful return, the caller is responsible for calling
+* spi_unregister_slave().
+*/
+int spi_register_slave(struct spi_slave *slave)
+{
+	static atomic_t		dyn_bus_id = ATOMIC_INIT((1<<15) - 1);
+	struct device		*dev = slave->dev.parent;
+	int			status = -ENODEV;
+	int			dynamic = 0;
+
+	if (!dev)
+		return -ENODEV;
+
+	/* even if it's just one always-selected device, there must
+	 * be at least one chipselect
+	 */
+	if (slave->num_chipselect == 0)
+		return -EINVAL;
+
+	/* convention:  dynamically assigned bus IDs count down from the max */
+	if (slave->bus_num < 0) {
+		/* FIXME switch to an IDR based scheme, something like
+		 * I2C now uses, so we can't run out of "dynamic" IDs
+		 */
+		slave->bus_num = atomic_dec_return(&dyn_bus_id);
+		dynamic = 1;
+	}
+
+	/* register the device, then userspace will see it.
+	 * registration fails if the bus ID is in use.
+	 */
+	dev_set_name(&slave->dev, "spi%u", slave->bus_num);
+	status = device_add(&slave->dev);
+	if (status < 0)
+		goto done;
+
+	dev_dbg(dev, "registered slave %s%s\n", dev_name(&slave->dev),
+			dynamic ? " (dynamic)" : "");
+
+	/* populate children from any spi device tables */
+	spi_slave_scan_boardinfo(slave);
+	status = 0;
+done:
+	return status;
+}
+EXPORT_SYMBOL_GPL(spi_register_slave);
 
 static int __unregister(struct device *dev, void *master_dev)
 {
@@ -547,6 +809,27 @@ void spi_unregister_master(struct spi_master *master)
 }
 EXPORT_SYMBOL_GPL(spi_unregister_master);
 
+/**
+* spi_unregister_slave - unregister SPI slave controller
+* @master: the slave being unregistered
+* Context: can sleep
+*
+* This call is used only by SPI slave controller drivers, which are the
+* only ones directly touching chip registers.
+*
+* This must be called from context that can sleep.
+*/
+void spi_unregister_slave(struct spi_slave *slave)
+{
+	int dummy;
+
+	dummy = device_for_each_child(slave->dev.parent, &slave->dev,
+					__unregister);
+	device_unregister(&slave->dev);
+}
+EXPORT_SYMBOL_GPL(spi_unregister_slave);
+
+
 static int __spi_master_match(struct device *dev, void *data)
 {
 	struct spi_master *m;
@@ -626,6 +909,18 @@ int spi_sync(struct spi_device *spi, struct spi_message *message)
 }
 EXPORT_SYMBOL_GPL(spi_sync);
 
+/* spi_transfer_async - Wraper function to allow spi_async to expose to
+* user protocol drivers for modem handshaking
+*/
+
+int spi_transfer_async(struct spi_device *spi, struct spi_message *message)
+{
+	int status;
+	status = spi_async(spi, message);
+	return status;
+}
+EXPORT_SYMBOL_GPL(spi_transfer_async);
+
 /* portable code must never pass more than 32 bytes */
 #define	SPI_BUFSIZ	max(32,SMP_CACHE_BYTES)
 
@@ -724,6 +1019,12 @@ static int __init spi_init(void)
 	status = class_register(&spi_master_class);
 	if (status < 0)
 		goto err2;
+
+	status = class_register(&spi_slave_class);
+
+	if (status < 0)
+		goto err2;
+
 	return 0;
 
 err2:
@@ -743,4 +1044,3 @@ err0:
  * include needing to have boardinfo data structures be much more public.
  */
 postcore_initcall(spi_init);
-
include/linux/spi/spi.h +93 -7

192 lines not on this page · raw message

Signature · 11 lines
1.5.4.3




------------------------------------------------------------------------------
Crystal Reports - New Free Runtime and 30 Day Trial
Check out the new simplified licensing option that enables unlimited
royalty-free distribution of the report engine for externally facing 
server and web deployment.
http://p.sf.net/sfu/businessobjects
#1 lore raw
Quoted 5 lines · Ken Mills

---
 drivers/spi/spi.c       |  368 ++++++++++++++++++++++++++++++++++++++++++----- include/linux/spi/spi.h |  100 ++++++++++++-

 2 files changed, 427 insertions(+), 41 deletions(-)

Comments from anyone?

I'm going to stick this into my review queue, but it may sit there for a while yet while I catch up...

------------------------------------------------------------------------------

#2 lore raw
In reply to Ken Mills

2009/6/12 Ken Mills <[email protected]>:

Quoted 1 line

Second try sending SPI core patch.

I reviewed this slave driver last time you sent it, do you have a ChangeLog from v1->v2?

I made a lot of remarks, I'd like you to either address them or answer my concerns. I'll repost the comments from last version...

Linus

------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge
This is your chance to win up to $100,000 in prizes! For a limited time, vendors submitting new applications to BlackBerry App World(TM) will have the opportunity to enter the BlackBerry Developer Challenge. See full prize details at: http://p.sf.net/sfu/blackberry