diff options
author | Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com> | 2018-04-27 14:30:40 +0900 |
---|---|---|
committer | Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com> | 2018-04-27 14:30:40 +0900 |
commit | f8fb4a95d6f0ca9a92d3b9155e54ac1f3695a20d (patch) | |
tree | 7f5a50447e8a940b274c53581dc8af01e2fe0db6 /meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch | |
parent | 2e1a7e39e012ea4436c819f46cfcac05fb161184 (diff) | |
parent | f4fad8b9a0d29946c39bb760b76bc9c16448555a (diff) |
Merge remote-tracking branch 'cogent/v2.23.1' into sandbox/kingfisher_v2.23.1.20180427
Signed-off-by: Harunobu Kurokawa <harunobu.kurokawa.dn@renesas.com>
Conflicts:
meta-rcar-gen3-adas/conf/layer.conf
meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/ulcb.cfg
meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas_4.9.bbappend
meta-rcar-gen3-adas/recipes-multimedia/pulseaudio/pulseaudio_8.0.bbappend
Change-Id: Ib0345634b8bec6a85357152138355d8f932bafc0
Diffstat (limited to 'meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch')
-rw-r--r-- | meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch | 445 |
1 files changed, 445 insertions, 0 deletions
diff --git a/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch new file mode 100644 index 0000000..780459c --- /dev/null +++ b/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0084-spi-core-Add-support-for-registering-SPI-slave-contr.patch @@ -0,0 +1,445 @@ +From 069206bafab71d97d2c427ed664e7cbacbe0678c Mon Sep 17 00:00:00 2001 +From: Geert Uytterhoeven <geert+renesas@glider.be> +Date: Fri, 17 Jun 2016 16:45:37 +0200 +Subject: [PATCH 2/7] 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@glider.be> +--- + drivers/spi/Kconfig | 14 +++- + drivers/spi/Makefile | 2 + + drivers/spi/spi.c | 179 ++++++++++++++++++++++++++++++++++++++++------- + include/linux/spi/spi.h | 35 +++++++-- + 4 files changed, 201 insertions(+), 29 deletions(-) + +diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig +index fb418d5..8c97790 100644 +--- a/drivers/spi/Kconfig ++++ b/drivers/spi/Kconfig +@@ -781,6 +781,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 aa939d9..2428b0f 100644 +--- a/drivers/spi/Makefile ++++ b/drivers/spi/Makefile +@@ -102,3 +102,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 838783c..a403e3a 100644 +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1513,15 +1513,6 @@ static int spi_master_initialize_queue(struct spi_master *master) + goto err_out; + } + +- /* 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); +- goto err_out; +- } +- spi->chip_select = value; +- + /* Mode (clock phase/polarity/etc.) */ + if (of_find_property(nc, "spi-cpha", NULL)) + spi->mode |= SPI_CPHA; +@@ -1571,6 +1562,24 @@ static int spi_master_initialize_queue(struct spi_master *master) + } + } + ++ 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) { +@@ -1603,8 +1612,8 @@ static int spi_master_initialize_queue(struct spi_master *master) + * 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) + { +@@ -1771,28 +1780,129 @@ static void spi_master_release(struct device *dev) + .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; + +@@ -1806,14 +1916,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) +@@ -1889,9 +2003,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 +@@ -1928,8 +2044,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) +@@ -3102,6 +3219,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; + +@@ -3183,6 +3303,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; + +@@ -3255,6 +3378,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)) +@@ -3262,6 +3391,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 4b743ac..f9bbd33 100644 +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -28,8 +28,8 @@ + 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; + +@@ -310,6 +310,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 + * @max_transfer_size: function that returns the max transfer size for + * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used. + * @max_message_size: function that returns the max message size for +@@ -373,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. + * @flash_read_supported: spi device supports flash read +@@ -443,6 +445,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; ++ + /* + * on some hardware transfer / message size may be constrained + * the limit may depend on device transfer settings +@@ -535,6 +540,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); +@@ -589,6 +595,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); +@@ -599,8 +610,23 @@ static inline void spi_master_put(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, +@@ -906,6 +932,7 @@ static inline void spi_message_free(struct spi_message *m) + 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) +-- +1.7.10.4 |