From f4502dd1da9b060a49d539eb754ff86cb97b89f0 Mon Sep 17 00:00:00 2001 From: Vignesh R Date: Wed, 8 Jun 2016 12:18:31 +0530 Subject: spi: Add DMA support for spi_flash_read() Few SPI devices provide accelerated read interfaces to read from SPI-NOR flash devices. These hardwares also support DMA to transfer data from flash to memory either via mem-to-mem DMA or dedicated slave DMA channels. Hence, add support for DMA in order to improve throughput and reduce CPU load. Use spi_map_buf() to get sg table for the buffer and pass it to SPI driver. Signed-off-by: Vignesh R Signed-off-by: Mark Brown --- include/linux/spi/spi.h | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'include') diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 1f03483f61e5..7b53af4ba5f8 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -1143,6 +1143,8 @@ static inline ssize_t spi_w8r16be(struct spi_device *spi, u8 cmd) * @opcode_nbits: number of lines to send opcode * @addr_nbits: number of lines to send address * @data_nbits: number of lines for data + * @rx_sg: Scatterlist for receive data read from flash + * @cur_msg_mapped: message has been mapped for DMA */ struct spi_flash_read_message { void *buf; @@ -1155,6 +1157,8 @@ struct spi_flash_read_message { u8 opcode_nbits; u8 addr_nbits; u8 data_nbits; + struct sg_table rx_sg; + bool cur_msg_mapped; }; /* SPI core interface for flash read support */ -- cgit v1.2.3 From a92e7c3d82a1313ab1954e5cdfd8f04efdb4ca78 Mon Sep 17 00:00:00 2001 From: Andi Shyti Date: Tue, 28 Jun 2016 11:41:12 +0900 Subject: spi: s3c64xx: consider the case when the CS line is not connected When the CS line is not connected, it is not needed to enable or disable the chip selection functionality from the s3c64xx devices in order to perform a transfer. Set the CS controller logically always enabled already during initialization (by writing '0' in the S3C64XX_SPI_SLAVE_SEL register) and never disable it. Signed-off-by: Andi Shyti Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/spi/spi-samsung.txt | 3 +++ drivers/spi/spi-s3c64xx.c | 9 ++++++++- include/linux/platform_data/spi-s3c64xx.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/Documentation/devicetree/bindings/spi/spi-samsung.txt b/Documentation/devicetree/bindings/spi/spi-samsung.txt index 6dbdeb3c361a..930bc7349271 100644 --- a/Documentation/devicetree/bindings/spi/spi-samsung.txt +++ b/Documentation/devicetree/bindings/spi/spi-samsung.txt @@ -40,6 +40,9 @@ Optional Board Specific Properties: - cs-gpios: should specify GPIOs used for chipselects (see spi-bus.txt) +- no-cs-readback: the CS line is disconnected, therefore the device should not + operate based on CS signalling. + SPI Controller specific data in SPI slave nodes: - The spi slave nodes should provide the following information which is required diff --git a/drivers/spi/spi-s3c64xx.c b/drivers/spi/spi-s3c64xx.c index 972367d5c7f2..14269b07804c 100644 --- a/drivers/spi/spi-s3c64xx.c +++ b/drivers/spi/spi-s3c64xx.c @@ -315,6 +315,9 @@ static void s3c64xx_spi_set_cs(struct spi_device *spi, bool enable) struct s3c64xx_spi_driver_data *sdd = spi_master_get_devdata(spi->master); + if (sdd->cntrlr_info->no_cs) + return; + if (enable) { if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) { writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); @@ -960,7 +963,9 @@ static void s3c64xx_spi_hwinit(struct s3c64xx_spi_driver_data *sdd, int channel) sdd->cur_speed = 0; - if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) + if (sci->no_cs) + writel(0, sdd->regs + S3C64XX_SPI_SLAVE_SEL); + else if (!(sdd->port_conf->quirks & S3C64XX_SPI_QUIRK_CS_AUTO)) writel(S3C64XX_SPI_SLAVE_SIG_INACT, sdd->regs + S3C64XX_SPI_SLAVE_SEL); /* Disable Interrupts - we use Polling if not DMA mode */ @@ -1015,6 +1020,8 @@ static struct s3c64xx_spi_info *s3c64xx_spi_parse_dt(struct device *dev) sci->num_cs = temp; } + sci->no_cs = of_property_read_bool(dev->of_node, "broken-cs"); + return sci; } #else diff --git a/include/linux/platform_data/spi-s3c64xx.h b/include/linux/platform_data/spi-s3c64xx.h index fb5625bcca9a..5c1e21c87270 100644 --- a/include/linux/platform_data/spi-s3c64xx.h +++ b/include/linux/platform_data/spi-s3c64xx.h @@ -38,6 +38,7 @@ struct s3c64xx_spi_csinfo { struct s3c64xx_spi_info { int src_clk_nr; int num_cs; + bool no_cs; int (*cfg_gpio)(void); dma_filter_fn filter; void *dma_tx; -- cgit v1.2.3 From ef4d96ec4ad947360f48677b6007a4c77953b090 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 21 Jul 2016 23:53:31 +0100 Subject: spi: Split bus and I/O locking The current SPI code attempts to use bus_lock_mutex for two purposes. One is to implement spi_bus_lock() which grants exclusive access to the bus. The other is to serialize access to the physical hardware. This duplicate purpose causes confusion which leads to cases where access is not locked when a caller holds the bus lock mutex. Fix this by splitting out the I/O functionality into a new io_mutex. This means taking both mutexes in the DMA path, replacing the existing mutex with the new I/O one in the message pump (the mutex now always being taken in the message pump) and taking the bus lock mutex in spi_sync(), allowing __spi_sync() to have no mutex handling. While we're at it hoist the mutex further up the message pump before we power up the device so that all power up/down of the block is covered by it and there are no races with in-line pumping of messages. Reported-by: Rich Felker Tested-by: Rich Felker Signed-off-by: Mark Brown --- drivers/spi/spi.c | 38 ++++++++++++++++++-------------------- include/linux/spi/spi.h | 6 +++++- 2 files changed, 23 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index c9a8d544e467..d2e7f1350ef6 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -1069,7 +1069,6 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * __spi_pump_messages - function which processes spi message queue * @master: master to process queue for * @in_kthread: true if we are in the context of the message pump thread - * @bus_locked: true if the bus mutex is held when calling this function * * This function checks if there is any spi message in the queue that * needs processing and if so call out to the driver to initialize hardware @@ -1079,8 +1078,7 @@ EXPORT_SYMBOL_GPL(spi_finalize_current_transfer); * inside spi_sync(); the queue extraction handling at the top of the * function should deal with this safely. */ -static void __spi_pump_messages(struct spi_master *master, bool in_kthread, - bool bus_locked) +static void __spi_pump_messages(struct spi_master *master, bool in_kthread) { unsigned long flags; bool was_busy = false; @@ -1152,6 +1150,8 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, master->busy = true; spin_unlock_irqrestore(&master->queue_lock, flags); + mutex_lock(&master->io_mutex); + if (!was_busy && master->auto_runtime_pm) { ret = pm_runtime_get_sync(master->dev.parent); if (ret < 0) { @@ -1176,9 +1176,6 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, } } - if (!bus_locked) - mutex_lock(&master->bus_lock_mutex); - trace_spi_message_start(master->cur_msg); if (master->prepare_message) { @@ -1208,8 +1205,7 @@ static void __spi_pump_messages(struct spi_master *master, bool in_kthread, } out: - if (!bus_locked) - mutex_unlock(&master->bus_lock_mutex); + mutex_unlock(&master->io_mutex); /* Prod the scheduler in case transfer_one() was busy waiting */ if (!ret) @@ -1225,7 +1221,7 @@ static void spi_pump_messages(struct kthread_work *work) struct spi_master *master = container_of(work, struct spi_master, pump_messages); - __spi_pump_messages(master, true, master->bus_lock_flag); + __spi_pump_messages(master, true); } static int spi_init_queue(struct spi_master *master) @@ -1887,6 +1883,7 @@ int spi_register_master(struct spi_master *master) spin_lock_init(&master->queue_lock); spin_lock_init(&master->bus_lock_spinlock); mutex_init(&master->bus_lock_mutex); + mutex_init(&master->io_mutex); master->bus_lock_flag = 0; init_completion(&master->xfer_completion); if (!master->max_dma_len) @@ -2767,6 +2764,7 @@ int spi_flash_read(struct spi_device *spi, } mutex_lock(&master->bus_lock_mutex); + mutex_lock(&master->io_mutex); if (master->dma_rx) { rx_dev = master->dma_rx->device->dev; ret = spi_map_buf(master, rx_dev, &msg->rx_sg, @@ -2779,6 +2777,7 @@ int spi_flash_read(struct spi_device *spi, if (msg->cur_msg_mapped) spi_unmap_buf(master, rx_dev, &msg->rx_sg, DMA_FROM_DEVICE); + mutex_unlock(&master->io_mutex); mutex_unlock(&master->bus_lock_mutex); if (master->auto_runtime_pm) @@ -2800,8 +2799,7 @@ static void spi_complete(void *arg) complete(arg); } -static int __spi_sync(struct spi_device *spi, struct spi_message *message, - int bus_locked) +static int __spi_sync(struct spi_device *spi, struct spi_message *message) { DECLARE_COMPLETION_ONSTACK(done); int status; @@ -2819,9 +2817,6 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, SPI_STATISTICS_INCREMENT_FIELD(&master->statistics, spi_sync); SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync); - if (!bus_locked) - mutex_lock(&master->bus_lock_mutex); - /* If we're not using the legacy transfer method then we will * try to transfer in the calling context so special case. * This code would be less tricky if we could remove the @@ -2839,9 +2834,6 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, status = spi_async_locked(spi, message); } - if (!bus_locked) - mutex_unlock(&master->bus_lock_mutex); - if (status == 0) { /* Push out the messages in the calling context if we * can. @@ -2851,7 +2843,7 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, spi_sync_immediate); SPI_STATISTICS_INCREMENT_FIELD(&spi->statistics, spi_sync_immediate); - __spi_pump_messages(master, false, bus_locked); + __spi_pump_messages(master, false); } wait_for_completion(&done); @@ -2884,7 +2876,13 @@ static int __spi_sync(struct spi_device *spi, struct spi_message *message, */ int spi_sync(struct spi_device *spi, struct spi_message *message) { - return __spi_sync(spi, message, spi->master->bus_lock_flag); + int ret; + + mutex_lock(&spi->master->bus_lock_mutex); + ret = __spi_sync(spi, message); + mutex_unlock(&spi->master->bus_lock_mutex); + + return ret; } EXPORT_SYMBOL_GPL(spi_sync); @@ -2906,7 +2904,7 @@ EXPORT_SYMBOL_GPL(spi_sync); */ int spi_sync_locked(struct spi_device *spi, struct spi_message *message) { - return __spi_sync(spi, message, 1); + return __spi_sync(spi, message); } EXPORT_SYMBOL_GPL(spi_sync_locked); diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h index 7b53af4ba5f8..072cb2aa2413 100644 --- a/include/linux/spi/spi.h +++ b/include/linux/spi/spi.h @@ -312,8 +312,9 @@ static inline void spi_unregister_driver(struct spi_driver *sdrv) * @flags: other constraints relevant to this driver * @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. + * @io_mutex: mutex for physical bus access * @bus_lock_spinlock: spinlock for SPI bus locking - * @bus_lock_mutex: mutex for SPI bus locking + * @bus_lock_mutex: mutex for exclusion of multiple callers * @bus_lock_flag: indicates that the SPI bus is locked for exclusive use * @setup: updates the device mode and clocking records used by a * device's SPI controller; protocol code may call this. This @@ -446,6 +447,9 @@ struct spi_master { */ size_t (*max_transfer_size)(struct spi_device *spi); + /* I/O mutex */ + struct mutex io_mutex; + /* lock and mutex for SPI bus locking */ spinlock_t bus_lock_spinlock; struct mutex bus_lock_mutex; -- cgit v1.2.3