[2/5] spi: spi_register_lock_bus and spi_unregister_lock_bus
diff mbox

Message ID 20100216205728.86960a9e.eschwab@online.de
State Changes Requested
Headers show

Commit Message

Ernst Schwab Feb. 16, 2010, 7:57 p.m. UTC
None

Patch
diff mbox

diff -upr a/drivers/spi/spi.c b/drivers/spi/spi.c
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -285,6 +285,18 @@  int spi_add_device(struct spi_device *sp
 		goto done;
 	}
 
+	/* If exclusive SPI bus access was granted to an SPI client, all
+	 * further spi_add_device calls are abandoned.
+	 */
+	if (spi->master->exclusive &&
+	    spi->chip_select!=spi->master->bus_lock_chip_select) {
+		dev_err(dev, "SPI bus is locked for exclusive use "
+			"by chipselect %d; chipselect %d cannot be used\n", 
+			spi->master->bus_lock_chip_select, spi->chip_select);
+		status = -EBUSY;
+		goto done;
+	}		
+
 	/* Drivers may modify this initial i/o setup, but will
 	 * normally rely on the device being setup.  Devices
 	 * using SPI_CS_HIGH can't coexist well otherwise...
@@ -524,6 +536,10 @@  int spi_register_master(struct spi_maste
 		dynamic = 1;
 	}
 
+	/* no device has registered the master for exclusive use yet */
+	master->bus_lock_chip_select = SPI_MASTER_BUS_UNLOCKED;
+	master->exclusive = 0;
+	
 	/* register the device, then userspace will see it.
 	 * registration fails if the bus ID is in use.
 	 */
@@ -783,6 +799,96 @@  int spi_unlock_bus(struct spi_device *sp
 }
 EXPORT_SYMBOL_GPL(spi_unlock_bus);
 
+/* REVISIT:
+ * Temporary solution: 
+ * Helper functions to check if exclusive access possible.
+ * If all masters support the spi_lock_bus callback, these can be removed.
+ */
+struct count_children {
+	unsigned	n;
+	struct bus_type	*bus;
+};
+
+static int maybe_count_child(struct device *dev, void *c)
+{
+	struct count_children *ccp = c;
+
+	if (dev->bus == ccp->bus) {
+		if (ccp->n)
+			return -EBUSY;
+		ccp->n++;
+	}
+	return 0;
+}
+
+static int spi_lock_bus_check (struct spi_device *spi) 
+{
+	int status;
+	
+	if (spi->master->num_chipselect > 1) {
+		struct count_children cc;
+
+		cc.n = 0;
+		cc.bus = spi->dev.bus;
+		status = device_for_each_child(spi->dev.parent, &cc,
+				maybe_count_child);
+		if (status < 0) {
+			return status;
+		}
+	}
+	return 0;
+}
+
+/**
+ * spi_register_lock_bus - register SPI device for use of spi_lock_bus
+ */
+int spi_register_lock_bus(struct spi_device *spi)
+{
+	if (spi->master->bus_lock_chip_select != SPI_MASTER_BUS_UNLOCKED) {
+		/* currently, only one device can use bus locking */
+		dev_err(&spi->dev, "only one SPI device can "
+				   "get exclusive SPI bus access\n");
+		return -EBUSY;
+	}
+
+	if (!spi->master->lock_bus) {
+		/* REVISIT:
+		 * If all masters support the spi_lock_bus entry, 
+		 * this can be removed.
+		 * A check for exclusive SPI bus use is done if the SPI master
+		 * does not support bus locking.
+		 */
+		if (spi_lock_bus_check(spi) < 0) {
+			return -EBUSY;
+		}
+		
+		/* lock out all future spi_add_device calls */
+		spi->master->exclusive = 1;
+		dev_warn(&spi->dev, "ENFORCING SPI bus stays unshared!\n");
+	}
+
+	/* Remember the chip_select for which the bus is locked. */
+	spi->master->bus_lock_chip_select = spi->chip_select;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_register_lock_bus);
+
+/**
+ * spi_unregister_lock_bus - unregister SPI device for use of spi_lock_bus
+ */
+int spi_unregister_lock_bus(struct spi_device *spi)
+{
+	if (spi->master->bus_lock_chip_select != spi->chip_select) {
+		/* unregister without a matching register */
+		return -EINVAL;
+	}
+
+	spi->master->bus_lock_chip_select = SPI_MASTER_BUS_UNLOCKED;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(spi_unregister_lock_bus);
+
 /**
  * spi_sync - blocking/synchronous SPI data transfers
  * @spi: device with which data will be exchanged
diff -upr a/include/linux/spi/spi.h b/include/linux/spi/spi.h
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -263,6 +263,14 @@  struct spi_master {
 #define SPI_MASTER_NO_RX	BIT(1)		/* can't do buffer read */
 #define SPI_MASTER_NO_TX	BIT(2)		/* can't do buffer write */
 
+	/* chipselect of the (one) spi device that may lock the bus for 
+	 * exclusive use */
+	u8			bus_lock_chip_select;
+#define SPI_MASTER_BUS_UNLOCKED 0xFF		/* value to indicate unlocked */
+
+	/* enforce that the spi bus remains unshared */
+	u8			exclusive;
+				
 	/* Setup mode and clock, etc (spi driver may call many times).
 	 *
 	 * IMPORTANT:  this may be called when transfers to another
@@ -555,6 +563,10 @@  extern int spi_async(struct spi_device *
  */
 
 extern int spi_sync(struct spi_device *spi, struct spi_message *message);
+
+/* Functions to ensure exclusive access to the spi bus */
+extern int spi_register_lock_bus(struct spi_device *spi);
+extern int spi_unregister_lock_bus(struct spi_device *spi);
 extern int spi_lock_bus(struct spi_device *spi);
 extern int spi_unlock_bus(struct spi_device *spi);