summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGeert Uytterhoeven <geert+renesas@glider.be>2017-05-22 15:11:41 +0200
committerAlok Chauhan <alokc@codeaurora.org>2017-11-08 11:26:36 +0530
commit7e351fc4ea00d1319b798ac5df44a91b36c4a40c (patch)
tree7e46ada1882c926513b765a044eff009584ecc75
parentc64b5ccbba33214094f8eed3f1117c7a8d9720f0 (diff)
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(). Change-Id: I251c5b7247ee7088285e42dd3b3cdce9c56cb9f7 Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be> Signed-off-by: Mark Brown <broonie@kernel.org> Git-commit: 6c364062bfed3c34490e85bea52ff6e2d4f0f281 Git-repo: git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git Signed-off-by: Alok Chauhan <alokc@codeaurora.org>
-rw-r--r--drivers/spi/Kconfig14
-rw-r--r--drivers/spi/Makefile2
-rw-r--r--drivers/spi/spi.c176
-rw-r--r--include/linux/spi/spi.h35
4 files changed, 198 insertions, 29 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 8b9c2a38d1cc..1f4a1f02a2cd 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -704,6 +704,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 a7dade652e1b..9e7f8f97c908 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -95,3 +95,5 @@ obj-$(CONFIG_SPI_XTENSA_XTFPGA) += spi-xtensa-xtfpga.o
obj-$(CONFIG_SPI_ZYNQMP_GQSPI) += spi-zynqmp-gqspi.o
obj-$(CONFIG_SPI_QSD) += spi_qsd.o
obj-$(CONFIG_SPI_QUP) += spi_qsd.o
+
+# SPI slave protocol handlers
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 6030a3fdf50d..f273de948a78 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -1427,15 +1427,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;
@@ -1485,6 +1476,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) {
@@ -1548,8 +1557,8 @@ err_out:
* 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)
{
@@ -1681,28 +1690,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;
@@ -1716,13 +1826,17 @@ 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;
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)
@@ -1798,9 +1912,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
@@ -1836,8 +1952,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)
@@ -2625,6 +2742,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;
@@ -2697,11 +2817,19 @@ 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));
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 cce80e6dc7d1..01cf8b6ac61a 100644
--- a/include/linux/spi/spi.h
+++ b/include/linux/spi/spi.h
@@ -27,8 +27,8 @@ struct spi_master;
struct spi_transfer;
/*
- * 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;
@@ -303,6 +303,7 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv)
* @min_speed_hz: Lowest supported transfer speed
* @max_speed_hz: Highest supported transfer speed
* @flags: other constraints relevant to this driver
+ * @slave: indicates that this is an SPI slave controller
* @bus_lock_spinlock: spinlock for SPI bus locking
* @bus_lock_mutex: mutex for SPI bus locking
* @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
@@ -361,6 +362,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
* @cs_gpios: Array of GPIOs to use as chip select lines; one per CS
* number. Any individual value may be -ENOENT for CS lines that
* are not GPIOs (driven by the SPI controller itself).
@@ -425,6 +427,9 @@ struct spi_master {
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
+ /* flag indicating this is an SPI slave controller */
+ bool slave;
+
/* lock and mutex for SPI bus locking */
spinlock_t bus_lock_spinlock;
struct mutex bus_lock_mutex;
@@ -507,6 +512,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);
/*
* These hooks are for drivers that use a generic implementation
@@ -556,6 +562,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);
@@ -566,8 +577,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,
@@ -831,6 +857,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);
/*---------------------------------------------------------------------------*/