[PATCH] spi: Add option to bind spidev to all chipselects

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Bypass the check if CS is in use for spidev devices if CONFIG_SPIDEV_SHADOW is
set.  Rename spidev devices to avoid sysfs conflict.

This allows dynamically loading SPI device overlays or communicating
with SPI devices configured by a kernel driver from userspace.

Signed-off-by: Michal Suchanek <hramrach@xxxxxxxxx>
---
 drivers/spi/Kconfig     | 13 +++++++++
 drivers/spi/spi.c       | 74 ++++++++++++++++++++++++++++++++++---------------
 include/linux/spi/spi.h |  1 +
 3 files changed, 65 insertions(+), 23 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 198f96b..b477828 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -651,6 +651,19 @@ config SPI_SPIDEV
 	  Note that this application programming interface is EXPERIMENTAL
 	  and hence SUBJECT TO CHANGE WITHOUT NOTICE while it stabilizes.
 
+config SPIDEV_SHADOW
+	depends on SPI_SPIDEV
+	bool "Allow spidev access to configured SPI devices (DANGEROUS)"
+	help
+	  This creates a spidev device node for every chipselect.
+
+	  It is possible to access even SPI devices which are in use by a
+	  kernel driver. This allows invoking features not in use by the kernel
+	  or checking device state from userspace when the kernel driver fails.
+
+	  Sending out-of-order messages to the device or reconfiguring the
+	  device might cause driver failure. DANGEROUS
+
 config SPI_TLE62X0
 	tristate "Infineon TLE62X0 (for power switching)"
 	depends on SYSFS
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index e6ca46e..b48a0dc 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -254,16 +254,23 @@ struct spi_device *spi_alloc_device(struct spi_master *master)
 }
 EXPORT_SYMBOL_GPL(spi_alloc_device);
 
-static void spi_dev_set_name(struct spi_device *spi)
+static void spi_dev_set_name(struct spi_device *spi, const char * alias)
 {
 	struct acpi_device *adev = ACPI_COMPANION(&spi->dev);
 
 	if (adev) {
-		dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
+		if (alias)
+			dev_set_name(&spi->dev, "%s-%s", alias, acpi_dev_name(adev));
+		else
+			dev_set_name(&spi->dev, "spi-%s", acpi_dev_name(adev));
 		return;
 	}
 
-	dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
+	if (alias)
+		dev_set_name(&spi->dev, "%s%u.%u", alias, spi->master->bus_num,
+		     spi->chip_select);
+	else
+		dev_set_name(&spi->dev, "%s.%u", dev_name(&spi->master->dev),
 		     spi->chip_select);
 }
 
@@ -272,22 +279,25 @@ static int spi_dev_check(struct device *dev, void *data)
 	struct spi_device *spi = to_spi_device(dev);
 	struct spi_device *new_spi = data;
 
-	if (spi->master == new_spi->master &&
-	    spi->chip_select == new_spi->chip_select)
+	if (spi->master == new_spi->master
+	    && spi->chip_select == new_spi->chip_select
+#ifdef CONFIG_SPIDEV_SHADOW
+	    && !spi->shadow && !new_spi->shadow
+#endif
+	    )
 		return -EBUSY;
 	return 0;
 }
 
 /**
- * spi_add_device - Add spi_device allocated with spi_alloc_device
+ * spi_add_device_alias - Add spi_device allocated with spi_alloc_device
+ * possibly even when device for the CS exists.
  * @spi: spi_device to register
+ * @alias: string used as device name prefix or NULL
  *
- * Companion function to spi_alloc_device.  Devices allocated with
- * spi_alloc_device can be added onto the spi bus with this function.
- *
- * Returns 0 on success; negative errno on failure
+ * See spi_add_device
  */
-int spi_add_device(struct spi_device *spi)
+static inline int spi_add_device_alias(struct spi_device *spi, const char * alias)
 {
 	static DEFINE_MUTEX(spi_add_lock);
 	struct spi_master *master = spi->master;
@@ -303,7 +313,8 @@ int spi_add_device(struct spi_device *spi)
 	}
 
 	/* Set the bus ID string */
-	spi_dev_set_name(spi);
+	spi_dev_set_name(spi, alias);
+	spi->shadow = !!alias;
 
 	/* We need to make sure there's no other device with this
 	 * chipselect **BEFORE** we call setup(), else we'll trash
@@ -321,15 +332,17 @@ int spi_add_device(struct spi_device *spi)
 	if (master->cs_gpios)
 		spi->cs_gpio = master->cs_gpios[spi->chip_select];
 
-	/* 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...
-	 */
-	status = spi_setup(spi);
-	if (status < 0) {
-		dev_err(dev, "can't setup %s, status %d\n",
-				dev_name(&spi->dev), status);
-		goto done;
+	if (!alias) {
+			/* 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...
+			 */
+			status = spi_setup(spi);
+			if (status < 0) {
+					dev_err(dev, "can't setup %s, status %d\n",
+						dev_name(&spi->dev), status);
+					goto done;
+			}
 	}
 
 	/* Device may be bound to an active driver when this returns */
@@ -344,6 +357,20 @@ done:
 	mutex_unlock(&spi_add_lock);
 	return status;
 }
+
+/**
+ * spi_add_device - Add spi_device allocated with spi_alloc_device
+ * @spi: spi_device to register
+ *
+ * Companion function to spi_alloc_device.  Devices allocated with
+ * spi_alloc_device can be added onto the spi bus with this function.
+ *
+ * Returns 0 on success; negative errno on failure
+ */
+int spi_add_device(struct spi_device *spi)
+{
+	return spi_add_device_alias(spi, NULL);
+}
 EXPORT_SYMBOL_GPL(spi_add_device);
 
 /**
@@ -1400,6 +1427,7 @@ static void spidev_register_devices(struct spi_master *master)
 		spi->chip_select = i;
 		strlcpy(spi->modalias, "spidev", sizeof(spi->modalias));
 
+#ifndef CONFIG_SPIDEV_SHADOW
 		/*
 		 * This is far from perfect since an addition might be
 		 * done between here and the call to spi_add_device,
@@ -1418,8 +1446,8 @@ static void spidev_register_devices(struct spi_master *master)
 			spi_dev_put(spi);
 			continue;
 		}
-
-		if (spi_add_device(spi)) {
+#endif /* CONFIG_SPIDEV_SHADOW */
+		if (spi_add_device_alias(spi, "spidev")) {
 			dev_err(&master->dev, "Couldn't add spidev device\n");
 			spi_dev_put(spi);
 		}
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index d673072..8368714 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -97,6 +97,7 @@ struct spi_device {
 	void			*controller_data;
 	char			modalias[SPI_NAME_SIZE];
 	int			cs_gpio;	/* chip select gpio */
+	int			shadow; /* ignore when determining if CS is in use */
 
 	/*
 	 * likely need more hooks for more protocol options affecting how
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux