The I2C core provides a means to instantiate devices from userspace
using sysfs attributes. Provide the same mechanism for SPI devices.
Signed-off-by: Guenter Roeck
---
This helped me tremendously for testing new SPI master and client drivers.
Maybe it is useful for others as well.
ping: Any interest ?
Documentation/spi/spi-summary | 48 +
drivers/spi/spi.c | 159 +
include/linux/spi/spi.h |3 +
3 files changed, 210 insertions(+)
diff --git a/Documentation/spi/spi-summary b/Documentation/spi/spi-summary
index 7312ec1..5b59992 100644
--- a/Documentation/spi/spi-summary
+++ b/Documentation/spi/spi-summary
@@ -331,6 +331,54 @@ configurations will also be dynamic. Fortunately, such
devices all support
basic device identification probes, so they should hotplug normally.
+DECLARE SLAVE DEVICES FROM USER-SPACE
+
+In general, the kernel should know which SPI devices are connected and
+what addresses they live at. However, in certain cases, it does not, so a
+sysfs interface was added to let the user provide the information. This
+interface is made of 2 attribute files which are created in every SPI master
+directory: new_device and delete_device. Both files are write only and you
+must write the right parameters to them in order to properly instantiate,
+respectively delete, a SPI device.
+
+File new_device takes several parameters:
+
+Primary parameters are the name of the SPI device (a string), the SPI chip
+select (a number, expressed in decimal or hexadecimal), the SPI bus speed
+(a number, expressed in decimal or hexadecimal), and the SPI mode (a number,
+expressed in decimal or hexadecimal). SPI device name, chip select, and speed
+are mandatory. The mode parameter is optional and will be initialized with 0 if
+not provided.
+
+For at25 type EEPROMs, additional parameters must be provided in < >. Those are
+the EEPROM name (a string), the EEPROM capacity in bytes (a number, expressed
in
+decimal or hexadecimal), the EEPROM write page size (a number, expressed in
+decimal or hexadecimal), and the EEPROM flags (a number, expressed in decimal
or
+hexadecimal).
+
+File delete_device takes a single parameter: the chip select of the SPI
+device. As no two devices can live at the same chip select on a given SPI
+master, the chip select is sufficient to uniquely identify the device to be
+deleted.
+
+Examples:
+ cd /sys/class/spi_master/spi0
+ Instantiate devices on SPI master 0
+
+ echo "lm70 0 40" > new_device
+ Instantiate LM70 on CS0
+
+ echo "at25 1 100 0 < at25160b 2048 32 0x02 >" > new_device
+ Instantiate at25 driver with AT25160B on CS1
+
+ echo "sst25vf080b 2 100 3" > new_device
+ Instantiate SST25VF080B on CS2, using SPI mode 3
+
+While this interface should only be used when in-kernel device declaration
+can't be done, it can be helpful if you are developing a driver on a test
board,
+where you soldered the SPI device yourself, or in hot-plug situations.
+
+
How do I write an "SPI Protocol Driver"?
Most SPI drivers are currently kernel drivers, but there's also support
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 84c2861..da9ea04 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -30,11 +30,13 @@
#include
#include
#include
+#include
#include
#include
#include
#include
#include
+#include
static void spidev_release(struct device *dev)
{
@@ -229,6 +231,161 @@ struct bus_type spi_bus_type = {
};
EXPORT_SYMBOL_GPL(spi_bus_type);
+/*
+ * Let users instantiate SPI devices through sysfs. This can be used when
+ * platform initialization code doesn't contain the proper data for
+ * whatever reason, or for testing.
+ *
+ * Parameter checking may look overzealous, but we really don't want
+ * the user to provide incorrect parameters.
+ */
+static ssize_t
+spi_sysfs_new_device(struct device *dev, struct device_attribute *attr,
+const char *buf, size_t count)
+{
+ struct spi_master *master = container_of(dev, struct spi_master, dev);
+ struct spi_board_info info;
+ struct spi_eeprom eeprom;
+ struct spi_device *spi;
+ char end, d1, d2;
+ unsigned int cs, speed, mode = 0;
+ unsigned int len, pagesize, flags;
+ int res;
+
+ dev_warn(dev,
+"The new_device interface is still experimental and may change
in a near future\n");
+
+ memset(&info, 0, sizeof(struct spi_board_info));
+ memset(&eeprom, 0, sizeof(struct spi_eeprom));
+
+ /* Parse parameters, reject extra parameters */
+ res = sscanf(buf, " %32s %u %u %x %c %10s %u %u %x %c%c",
+info.modalias, &cs, &speed, &mode,
+&d1, eeprom.name, &len, &pagesize, &flags, &d2,
+&end);
+ /* Must have at least name (modalias), chip sele