lkml.org 
[lkml]   [2016]   [Sep]   [12]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH/RFC v2 3/7] spi: core: Add support for registering SPI slave controllers
Date
Add support for registering SPI slave controllers using the existing SPI
master framework:
- SPI slave controllers must set the SPI_CONTROLLER_IS_SLAVE flag in
spi_master.flags, and should provide an additional callback
"slave_abort" to abort an ongoing SPI transfer request.
- SPI slave controllers are added to a new "spi_slave" device class,
- (Un)binding an SPI slave handler to the SPI slave device represented
by an SPI slave controller is done by (un)registering the slave
device through a sysfs virtual file named "slave",
- Initial slave-specific mode properties are parsed from the SPI slave
controller DT node.

From the point of view of an SPI slave protocol handler, an SPI slave
controller looks almost like an ordinary SPI master controller. The only
exception is that a transfer request will block on the remote SPI
master, and may be cancelled using spi_slave_abort().

Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
---
v2:
- Attach SPI slave controllers to a new "spi_slave" device class,
- Don't call of_spi_register_master() instead of letting it return
early for SPI slave controllers,
- Skip registration of children from DT or ACPI for SPI slave
controllers,
- Use a "slave" virtual file in sysfs to (un)register the (single)
slave device for an SPI slave controller, incl. specifying the slave
protocol handler,
- Parse slave-specific mode properties in the SPI slave controller DT
node for the (single) slave device using of_spi_parse_dt(),
- Add cancellation support using spi_master.slave_abort() and
spi_slave_abort(),
- Rename flag SPI_MASTER_IS_SLAVE to SPI_CONTROLLER_IS_SLAVE,
- Introduce helper function spi_controller_is_slave(), making it easy
to leave out SPI slave support where appropriate.

TBD:
- s/spi_master/spi_controller/ where appropriate,
- Provide wrappers (e.g. "#define spi_master spi_controller" until all
SPI drivers have been converted),
- Do we want a separate spi_register_slave() instead?
---
drivers/spi/Kconfig | 14 ++++-
drivers/spi/Makefile | 2 +
drivers/spi/spi.c | 162 +++++++++++++++++++++++++++++++++++++++++++-----
include/linux/spi/spi.h | 17 ++++-
4 files changed, 175 insertions(+), 20 deletions(-)

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index d6fb8d4b778672fd..fa13a3663de5bde0 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -737,6 +737,18 @@ config SPI_TLE62X0

endif # SPI_MASTER

-# (slave support would go here)
+#
+# SLAVE side ... listening to other SPI masters
+#
+
+config SPI_SLAVE
+ bool "SPI slave protocol handlers"
+ help
+ If your system has a slave-capable SPI controller, you can enable
+ slave protocol handlers.
+
+if SPI_SLAVE
+
+endif # SPI_SLAVE

endif # SPI
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 185367ef65761a74..96e5c0d6e5bc6151 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -98,3 +98,5 @@ obj-$(CONFIG_SPI_XILINX) += spi-xilinx.o
obj-$(CONFIG_SPI_XLP) += spi-xlp.o
obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
+
+# SPI slave protocol handlers
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 79e5c1ba23c81dc1..cfb0d573fc4dedba 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1477,15 +1477,6 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
u32 value;
int rc;

- /* Device address */
- rc = of_property_read_u32(nc, "reg", &value);
- if (rc) {
- dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
- nc->full_name, rc);
- return rc;
- }
- spi->chip_select = value;
-
/* Mode (clock phase/polarity/etc.) */
if (of_find_property(nc, "spi-cpha", NULL))
spi->mode |= SPI_CPHA;
@@ -1535,6 +1526,18 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
}
}

+ if (spi_controller_is_slave(master))
+ return 0;
+
+ /* Device address */
+ rc = of_property_read_u32(nc, "reg", &value);
+ if (rc) {
+ dev_err(&master->dev, "%s has no valid 'reg' property (%d)\n",
+ nc->full_name, rc);
+ return rc;
+ }
+ spi->chip_select = value;
+
/* Device speed */
rc = of_property_read_u32(nc, "spi-max-frequency", &value);
if (rc) {
@@ -1619,6 +1622,8 @@ static void of_register_spi_devices(struct spi_master *master)
}
}
#else
+static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
+ struct device_node *nc) { return 0; }
static void of_register_spi_devices(struct spi_master *master) { }
#endif

@@ -1764,6 +1769,110 @@ static struct class spi_master_class = {
.dev_groups = spi_master_groups,
};

+#ifdef CONFIG_SPI_SLAVE
+/**
+ * spi_slave_abort - abort the ongoing transfer request on an SPI slave
+ * controller
+ * @spi: device used for the current transfer
+ */
+int spi_slave_abort(struct spi_device *spi)
+{
+ struct spi_master *master = spi->master;
+
+ if (spi_controller_is_slave(master) && master->slave_abort)
+ return master->slave_abort(master);
+
+ return -ENOTSUPP;
+}
+EXPORT_SYMBOL_GPL(spi_slave_abort);
+
+static int match_true(struct device *dev, void *data)
+{
+ return 1;
+}
+
+static ssize_t spi_slave_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct spi_master *ctlr = container_of(dev, struct spi_master, dev);
+ struct device *child;
+
+ child = device_find_child(&ctlr->dev, NULL, match_true);
+ return sprintf(buf, "%s\n",
+ child ? to_spi_device(child)->modalias : NULL);
+}
+
+static ssize_t spi_slave_store(struct device *dev,
+ struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct spi_master *ctlr = container_of(dev, struct spi_master, dev);
+ struct spi_device *spi;
+ struct device *child;
+ char name[32];
+ int rc;
+
+ rc = sscanf(buf, "%31s", name);
+ if (rc != 1 || !name[0])
+ return -EINVAL;
+
+ child = device_find_child(&ctlr->dev, NULL, match_true);
+ if (child) {
+ /* Remove registered slave */
+ device_unregister(child);
+ put_device(child);
+ }
+
+ if (strcmp(name, "(null)")) {
+ /* Register new slave */
+ spi = spi_alloc_device(ctlr);
+ if (!spi)
+ return -ENOMEM;
+
+ strlcpy(spi->modalias, name, sizeof(spi->modalias));
+
+ rc = of_spi_parse_dt(ctlr, spi, ctlr->dev.of_node);
+ if (rc)
+ goto err_out;
+
+ rc = spi_add_device(spi);
+ if (rc)
+ goto err_out;
+ }
+
+ return count;
+
+err_out:
+ spi_dev_put(spi);
+ return rc;
+}
+
+static DEVICE_ATTR(slave, S_IWUSR|S_IRUGO, spi_slave_show, spi_slave_store);
+
+static struct attribute *spi_slave_attrs[] = {
+ &dev_attr_slave.attr,
+ NULL,
+};
+
+static const struct attribute_group spi_slave_group = {
+ .attrs = spi_slave_attrs,
+};
+
+static const struct attribute_group *spi_slave_groups[] = {
+ &spi_master_statistics_group,
+ &spi_slave_group,
+ NULL,
+};
+
+static struct class spi_slave_class = {
+ .name = "spi_slave",
+ .owner = THIS_MODULE,
+ .dev_release = spi_master_release,
+ .dev_groups = spi_slave_groups,
+};
+#else
+extern struct class spi_slave_class; /* dummy */
+#endif

/**
* spi_alloc_master - allocate SPI master controller
@@ -1799,7 +1908,6 @@ struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
device_initialize(&master->dev);
master->bus_num = -1;
master->num_chipselect = 1;
- master->dev.class = &spi_master_class;
master->dev.parent = dev;
pm_suspend_ignore_children(&master->dev, true);
spi_master_set_devdata(master, &master[1]);
@@ -1882,9 +1990,18 @@ int spi_register_master(struct spi_master *master)
if (!dev)
return -ENODEV;

- status = of_spi_register_master(master);
- if (status)
- return status;
+ if (master->flags & SPI_CONTROLLER_IS_SLAVE) {
+ if (!IS_ENABLED(CONFIG_SPI_SLAVE))
+ return -ENOSYS;
+
+ master->dev.class = &spi_slave_class;
+ } else {
+ master->dev.class = &spi_master_class;
+
+ status = of_spi_register_master(master);
+ if (status)
+ return status;
+ }

/* even if it's just one always-selected device, there must
* be at least one chipselect
@@ -1921,8 +2038,9 @@ 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),
- dynamic ? " (dynamic)" : "");
+ dev_dbg(dev, "registered %s %s%s\n",
+ spi_controller_is_slave(master) ? "slave" : "master",
+ dev_name(&master->dev), dynamic ? " (dynamic)" : "");

/* If we're using a queued driver, start the queue */
if (master->transfer)
@@ -1944,8 +2062,10 @@ int spi_register_master(struct spi_master *master)
mutex_unlock(&board_lock);

/* Register devices from the device tree and ACPI */
- of_register_spi_devices(master);
- acpi_register_spi_devices(master);
+ if (!spi_controller_is_slave(master)) {
+ of_register_spi_devices(master);
+ acpi_register_spi_devices(master);
+ }
done:
return status;
}
@@ -3247,6 +3367,12 @@ static int __init spi_init(void)
if (status < 0)
goto err2;

+ if (IS_ENABLED(CONFIG_SPI_SLAVE)) {
+ status = class_register(&spi_slave_class);
+ if (status < 0)
+ goto err3;
+ }
+
if (IS_ENABLED(CONFIG_OF_DYNAMIC))
WARN_ON(of_reconfig_notifier_register(&spi_of_notifier));
if (IS_ENABLED(CONFIG_ACPI))
@@ -3254,6 +3380,8 @@ static int __init spi_init(void)

return 0;

+err3:
+ class_unregister(&spi_master_class);
err2:
bus_unregister(&spi_bus_type);
err1:
diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
index 072cb2aa24139981..9a278e92b5b70e07 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -28,8 +28,8 @@ struct spi_transfer;
struct spi_flash_read_message;

/*
- * INTERFACES between SPI master-side drivers and SPI infrastructure.
- * (There's no SPI slave support for Linux yet...)
+ * INTERFACES between SPI master-side drivers and SPI slave protocol handers,
+ * and SPI infrastructure.
*/
extern struct bus_type spi_bus_type;

@@ -371,6 +371,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @handle_err: the subsystem calls the driver to handle an error that occurs
* in the generic implementation of transfer_one_message().
* @unprepare_message: undo any work done by prepare_message().
+ * @slave_abort: abort the ongoing transfer request on an SPI slave controller
* @spi_flash_read: to support spi-controller hardwares that provide
* accelerated interface to read from flash devices.
* @flash_read_supported: spi device supports flash read
@@ -440,6 +441,7 @@ struct spi_master {
#define SPI_MASTER_NO_TX BIT(2) /* can't do buffer write */
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
+#define SPI_CONTROLLER_IS_SLAVE BIT(5) /* SPI slave controller */

/*
* on some hardware transfer size may be constrained
@@ -532,6 +534,7 @@ struct spi_master {
struct spi_message *message);
int (*unprepare_message)(struct spi_master *master,
struct spi_message *message);
+ int (*slave_abort)(struct spi_master *spi);
int (*spi_flash_read)(struct spi_device *spi,
struct spi_flash_read_message *msg);
bool (*flash_read_supported)(struct spi_device *spi);
@@ -586,6 +589,15 @@ static inline void spi_master_put(struct spi_master *master)
put_device(&master->dev);
}

+static inline bool spi_controller_is_slave(struct spi_master *ctlr)
+{
+#ifdef CONFIG_SPI_SLAVE
+ return ctlr->flags & SPI_CONTROLLER_IS_SLAVE;
+#else
+ return false;
+#endif
+}
+
/* PM calls that need to be issued by the driver */
extern int spi_master_suspend(struct spi_master *master);
extern int spi_master_resume(struct spi_master *master);
@@ -903,6 +915,7 @@ extern int spi_setup(struct spi_device *spi);
extern int spi_async(struct spi_device *spi, struct spi_message *message);
extern int spi_async_locked(struct spi_device *spi,
struct spi_message *message);
+extern int spi_slave_abort(struct spi_device *spi);

static inline size_t
spi_max_transfer_size(struct spi_device *spi)
--
1.9.1
\
 
 \ /
  Last update: 2016-09-17 09:59    [W:0.139 / U:0.104 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site