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