[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1493919951-32122-3-git-send-email-geert+renesas@glider.be>
Date: Thu, 4 May 2017 19:45:47 +0200
From: Geert Uytterhoeven <geert+renesas@...der.be>
To: Mark Brown <broonie@...nel.org>, Rob Herring <robh+dt@...nel.org>,
Mark Rutland <mark.rutland@....com>
Cc: Magnus Damm <magnus.damm@...il.com>,
Wolfram Sang <wsa+renesas@...g-engineering.com>,
Hiromitsu Yamasaki <hiromitsu.yamasaki.ym@...esas.com>,
Jiada Wang <jiada_wang@...tor.com>,
Matt Porter <mporter@...sulko.com>, linux-spi@...r.kernel.org,
devicetree@...r.kernel.org, linux-renesas-soc@...r.kernel.org,
linux-kernel@...r.kernel.org,
Geert Uytterhoeven <geert+renesas@...der.be>
Subject: [PATCH v3 2/6] spi: core: Add support for registering SPI slave controllers
Add support for registering SPI slave controllers using the existing SPI
master framework:
- SPI slave controllers must use spi_alloc_slave() instead of
spi_alloc_master(), 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,
- SPI slave handlers can be bound to the SPI slave device represented
by an SPI slave controller using a DT child node named "slave",
- Alternatively, (un)binding an SPI slave handler to the SPI slave
device represented by an SPI slave controller can be done by
(un)registering the slave device through a sysfs virtual file named
"slave".
>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@...der.be>
---
v3:
- Introduce a separate spi_alloc_slave() function, which sets up the
spi_slave_class instead of the spi_master class, to avoid the WARN()
in device_release(),
- Wrap spi_alloc_{master,slave}() around __spi_alloc_controller(),
- Replace the SPI_CONTROLLER_IS_SLAVE flag in spi_master.flags (set by
the SPI controller driver) by a bool in spi_master (set by
__spi_alloc_controller()),
- Revert parsing of slave-specific mode properties in the SPI slave
controller DT node for the (single) slave device,
- Register again children from DT or ACPI for SPI slave controllers;
the single optional slave child node must be called "slave",
- Let {acpi,of}_spi_notify() also search spi_slave_class devices,
- Use octal instead of symbolic permissions,
- s/hander/handler/,
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.
---
drivers/spi/Kconfig | 14 +++-
drivers/spi/Makefile | 2 +
drivers/spi/spi.c | 179 +++++++++++++++++++++++++++++++++++++++++-------
include/linux/spi/spi.h | 33 +++++++--
4 files changed, 199 insertions(+), 29 deletions(-)
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 1761c9004fc1149e..df8ddec24b5d7e88 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -784,6 +784,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 b375a7a892160b76..e50852c6fcb87d8b 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -105,3 +105,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 6f87fec409b5f6fd..c3f6b524b3ceb6c1 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1535,15 +1535,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;
@@ -1593,6 +1584,24 @@ static int of_spi_parse_dt(struct spi_master *master, struct spi_device *spi,
}
}
+ if (spi_controller_is_slave(master)) {
+ if (strcmp(nc->name, "slave")) {
+ dev_err(&master->dev, "%s is not called 'slave'\n",
+ nc->full_name);
+ return -EINVAL;
+ }
+ 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) {
@@ -1658,8 +1667,8 @@ of_register_spi_device(struct spi_master *master, struct device_node *nc)
* of_register_spi_devices() - Register child devices onto the SPI bus
* @master: Pointer to spi_master device
*
- * Registers an spi_device for each child node of master node which has a 'reg'
- * property.
+ * Registers an spi_device for each child node of controller node which
+ * represents a valid SPI slave.
*/
static void of_register_spi_devices(struct spi_master *master)
{
@@ -1828,28 +1837,129 @@ 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 = spi_add_device(spi);
+ if (rc) {
+ spi_dev_put(spi);
+ return rc;
+ }
+ }
+
+ return count;
+}
+
+static DEVICE_ATTR(slave, 0644, 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
+ * __spi_alloc_controller - allocate an SPI master or slave controller
* @dev: the controller, possibly using the platform_bus
* @size: how much zeroed driver-private data to allocate; the pointer to this
* memory is in the driver_data field of the returned device,
* accessible with spi_master_get_devdata().
+ * @slave: flag indicating whether to allocate an SPI master (false) or SPI
+ * slave (true) controller
* Context: can sleep
*
- * This call is used only by SPI master controller drivers, which are the
+ * This call is used only by SPI controller drivers, which are the
* only ones directly touching chip registers. It's how they allocate
* an spi_master structure, prior to calling spi_register_master().
*
* This must be called from context that can sleep.
*
- * The caller is responsible for assigning the bus number and initializing
- * the master's methods before calling spi_register_master(); and (after errors
+ * The caller is responsible for assigning the bus number and initializing the
+ * controller's methods before calling spi_register_master(); and (after errors
* adding the device) calling spi_master_put() to prevent a memory leak.
*
- * Return: the SPI master structure on success, else NULL.
+ * Return: the SPI controller structure on success, else NULL.
*/
-struct spi_master *spi_alloc_master(struct device *dev, unsigned size)
+struct spi_master *__spi_alloc_controller(struct device *dev,
+ unsigned int size, bool slave)
{
struct spi_master *master;
@@ -1863,14 +1973,18 @@ 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->slave = slave;
+ if (IS_ENABLED(CONFIG_SPI_SLAVE) && slave)
+ master->dev.class = &spi_slave_class;
+ else
+ master->dev.class = &spi_master_class;
master->dev.parent = dev;
pm_suspend_ignore_children(&master->dev, true);
spi_master_set_devdata(master, &master[1]);
return master;
}
-EXPORT_SYMBOL_GPL(spi_alloc_master);
+EXPORT_SYMBOL_GPL(__spi_alloc_controller);
#ifdef CONFIG_OF
static int of_spi_register_master(struct spi_master *master)
@@ -1946,9 +2060,11 @@ int spi_register_master(struct spi_master *master)
if (!dev)
return -ENODEV;
- status = of_spi_register_master(master);
- if (status)
- return status;
+ if (!spi_controller_is_slave(master)) {
+ 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
@@ -1985,8 +2101,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)
@@ -3159,6 +3276,9 @@ static struct spi_master *of_find_spi_master_by_node(struct device_node *node)
dev = class_find_device(&spi_master_class, NULL, node,
__spi_of_master_match);
+ if (!dev && IS_ENABLED(CONFIG_SPI_SLAVE))
+ dev = class_find_device(&spi_slave_class, NULL, node,
+ __spi_of_master_match);
if (!dev)
return NULL;
@@ -3240,6 +3360,9 @@ static struct spi_master *acpi_spi_find_master_by_adev(struct acpi_device *adev)
dev = class_find_device(&spi_master_class, NULL, adev,
spi_acpi_master_match);
+ if (!dev && IS_ENABLED(CONFIG_SPI_SLAVE))
+ dev = class_find_device(&spi_slave_class, NULL, adev,
+ spi_acpi_master_match);
if (!dev)
return NULL;
@@ -3312,6 +3435,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))
@@ -3319,6 +3448,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 935bd2854ff19b2a..2c73165a441fa001 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -29,8 +29,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 handlers,
+ * and SPI infrastructure.
*/
extern struct bus_type spi_bus_type;
@@ -374,6 +374,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.
* @spi_flash_can_dma: analogous to can_dma() interface, but for
@@ -447,6 +448,8 @@ struct spi_master {
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
#define SPI_MASTER_GPIO_SS BIT(5) /* GPIO CS must select slave */
+ bool slave; /* SPI slave controller */
+
/*
* on some hardware transfer / message size may be constrained
* the limit may depend on device transfer settings
@@ -539,6 +542,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 (*spi_flash_can_dma)(struct spi_device *spi,
@@ -595,6 +599,11 @@ 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)
+{
+ return IS_ENABLED(CONFIG_SPI_SLAVE) && ctlr->slave;
+}
+
/* 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);
@@ -605,8 +614,23 @@ extern void spi_finalize_current_message(struct spi_master *master);
extern void spi_finalize_current_transfer(struct spi_master *master);
/* the spi driver core manages memory for the spi_master classdev */
-extern struct spi_master *
-spi_alloc_master(struct device *host, unsigned size);
+extern struct spi_master *__spi_alloc_controller(struct device *host,
+ unsigned int size, bool slave);
+
+static inline struct spi_master *spi_alloc_master(struct device *host,
+ unsigned int size)
+{
+ return __spi_alloc_controller(host, size, false);
+}
+
+static inline struct spi_master *spi_alloc_slave(struct device *host,
+ unsigned int size)
+{
+ if (!IS_ENABLED(CONFIG_SPI_SLAVE))
+ return NULL;
+
+ return __spi_alloc_controller(host, size, true);
+}
extern int spi_register_master(struct spi_master *master);
extern int devm_spi_register_master(struct device *dev,
@@ -912,6 +936,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_message_size(struct spi_device *spi)
--
2.7.4
Powered by blists - more mailing lists