From 83ca0efb3e1e5b765d8b8a6bcb2e681f51b7a944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= Date: Tue, 26 Apr 2016 09:23:12 +0000 Subject: [PATCH] kernel: backport support for accelerated SPI flash read MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rafał Miłecki SVN-Revision: 49233 --- ...80-add-support-for-mmap-read-request.patch | 46 +++++ ...celerated-read-support-for-spi-flash.patch | 179 ++++++++++++++++++ ...spi_flash_read-callback-for-MMIO-bas.patch | 157 +++++++++++++++ 3 files changed, 382 insertions(+) create mode 100644 target/linux/generic/patches-4.4/045-mtd-devices-m25p80-add-support-for-mmap-read-request.patch create mode 100644 target/linux/generic/patches-4.4/080-spi-introduce-accelerated-read-support-for-spi-flash.patch create mode 100644 target/linux/generic/patches-4.4/081-spi-bcm53xx-add-spi_flash_read-callback-for-MMIO-bas.patch diff --git a/target/linux/generic/patches-4.4/045-mtd-devices-m25p80-add-support-for-mmap-read-request.patch b/target/linux/generic/patches-4.4/045-mtd-devices-m25p80-add-support-for-mmap-read-request.patch new file mode 100644 index 0000000000..ee85f448ff --- /dev/null +++ b/target/linux/generic/patches-4.4/045-mtd-devices-m25p80-add-support-for-mmap-read-request.patch @@ -0,0 +1,46 @@ +From 08922f644878c9163ada8df3ef9def89be1d5e90 Mon Sep 17 00:00:00 2001 +From: Vignesh R +Date: Tue, 29 Mar 2016 11:16:17 +0530 +Subject: [PATCH] mtd: devices: m25p80: add support for mmap read request + +Certain SPI controllers may provide accelerated hardware interface to +read from m25p80 type flash devices in order to provide better read +performance. SPI core supports such devices with spi_flash_read() API. +Call spi_flash_read(), if supported, to make use of such interface. + +Signed-off-by: Vignesh R +[Brian: add memset()] +Signed-off-by: Brian Norris +--- + +--- a/drivers/mtd/devices/m25p80.c ++++ b/drivers/mtd/devices/m25p80.c +@@ -131,6 +131,28 @@ static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len, + /* convert the dummy cycles to the number of bytes */ + dummy /= 8; + ++ if (spi_flash_read_supported(spi)) { ++ struct spi_flash_read_message msg; ++ int ret; ++ ++ memset(&msg, 0, sizeof(msg)); ++ ++ msg.buf = buf; ++ msg.from = from; ++ msg.len = len; ++ msg.read_opcode = nor->read_opcode; ++ msg.addr_width = nor->addr_width; ++ msg.dummy_bytes = dummy; ++ /* TODO: Support other combinations */ ++ msg.opcode_nbits = SPI_NBITS_SINGLE; ++ msg.addr_nbits = SPI_NBITS_SINGLE; ++ msg.data_nbits = m25p80_rx_nbits(nor); ++ ++ ret = spi_flash_read(spi, &msg); ++ *retlen = msg.retlen; ++ return ret; ++ } ++ + spi_message_init(&m); + memset(t, 0, (sizeof t)); + diff --git a/target/linux/generic/patches-4.4/080-spi-introduce-accelerated-read-support-for-spi-flash.patch b/target/linux/generic/patches-4.4/080-spi-introduce-accelerated-read-support-for-spi-flash.patch new file mode 100644 index 0000000000..5e116b0bbf --- /dev/null +++ b/target/linux/generic/patches-4.4/080-spi-introduce-accelerated-read-support-for-spi-flash.patch @@ -0,0 +1,179 @@ +From 556351f14e74db4cd3ddde386457edce7bf0b27f Mon Sep 17 00:00:00 2001 +From: Vignesh R +Date: Fri, 11 Dec 2015 09:39:56 +0530 +Subject: [PATCH] spi: introduce accelerated read support for spi flash devices + +In addition to providing direct access to SPI bus, some spi controller +hardwares (like ti-qspi) provide special port (like memory mapped port) +that are optimized to improve SPI flash read performance. +This means the controller can automatically send the SPI signals +required to read data from the SPI flash device. +For this, SPI controller needs to know flash specific information like +read command to use, dummy bytes and address width. + +Introduce spi_flash_read() interface to support accelerated read +over SPI flash devices. SPI master drivers can implement this callback to +support interfaces such as memory mapped read etc. m25p80 flash driver +and other flash drivers can call this make use of such interfaces. The +interface should only be used with SPI flashes and cannot be used with +other SPI devices. + +Signed-off-by: Vignesh R +Signed-off-by: Mark Brown +--- + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1135,6 +1135,7 @@ static void __spi_pump_messages(struct s + } + } + ++ mutex_lock(&master->bus_lock_mutex); + trace_spi_message_start(master->cur_msg); + + if (master->prepare_message) { +@@ -1144,6 +1145,7 @@ static void __spi_pump_messages(struct s + "failed to prepare message: %d\n", ret); + master->cur_msg->status = ret; + spi_finalize_current_message(master); ++ mutex_unlock(&master->bus_lock_mutex); + return; + } + master->cur_msg_prepared = true; +@@ -1153,6 +1155,7 @@ static void __spi_pump_messages(struct s + if (ret) { + master->cur_msg->status = ret; + spi_finalize_current_message(master); ++ mutex_unlock(&master->bus_lock_mutex); + return; + } + +@@ -1160,8 +1163,10 @@ static void __spi_pump_messages(struct s + if (ret) { + dev_err(&master->dev, + "failed to transfer one message from queue\n"); ++ mutex_unlock(&master->bus_lock_mutex); + return; + } ++ mutex_unlock(&master->bus_lock_mutex); + } + + /** +@@ -2329,6 +2334,46 @@ int spi_async_locked(struct spi_device * + EXPORT_SYMBOL_GPL(spi_async_locked); + + ++int spi_flash_read(struct spi_device *spi, ++ struct spi_flash_read_message *msg) ++ ++{ ++ struct spi_master *master = spi->master; ++ int ret; ++ ++ if ((msg->opcode_nbits == SPI_NBITS_DUAL || ++ msg->addr_nbits == SPI_NBITS_DUAL) && ++ !(spi->mode & (SPI_TX_DUAL | SPI_TX_QUAD))) ++ return -EINVAL; ++ if ((msg->opcode_nbits == SPI_NBITS_QUAD || ++ msg->addr_nbits == SPI_NBITS_QUAD) && ++ !(spi->mode & SPI_TX_QUAD)) ++ return -EINVAL; ++ if (msg->data_nbits == SPI_NBITS_DUAL && ++ !(spi->mode & (SPI_RX_DUAL | SPI_RX_QUAD))) ++ return -EINVAL; ++ if (msg->data_nbits == SPI_NBITS_QUAD && ++ !(spi->mode & SPI_RX_QUAD)) ++ return -EINVAL; ++ ++ if (master->auto_runtime_pm) { ++ ret = pm_runtime_get_sync(master->dev.parent); ++ if (ret < 0) { ++ dev_err(&master->dev, "Failed to power device: %d\n", ++ ret); ++ return ret; ++ } ++ } ++ mutex_lock(&master->bus_lock_mutex); ++ ret = master->spi_flash_read(spi, msg); ++ mutex_unlock(&master->bus_lock_mutex); ++ if (master->auto_runtime_pm) ++ pm_runtime_put(master->dev.parent); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(spi_flash_read); ++ + /*-------------------------------------------------------------------------*/ + + /* Utility methods for SPI master protocol drivers, layered on +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -25,6 +25,7 @@ + struct dma_chan; + struct spi_master; + struct spi_transfer; ++struct spi_flash_read_message; + + /* + * INTERFACES between SPI master-side drivers and SPI infrastructure. +@@ -361,6 +362,8 @@ static inline void spi_unregister_driver + * @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(). ++ * @spi_flash_read: to support spi-controller hardwares that provide ++ * accelerated interface to read from flash devices. + * @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). +@@ -507,6 +510,8 @@ struct spi_master { + struct spi_message *message); + int (*unprepare_message)(struct spi_master *master, + struct spi_message *message); ++ int (*spi_flash_read)(struct spi_device *spi, ++ struct spi_flash_read_message *msg); + + /* + * These hooks are for drivers that use a generic implementation +@@ -999,6 +1004,42 @@ static inline ssize_t spi_w8r16be(struct + return be16_to_cpu(result); + } + ++/** ++ * struct spi_flash_read_message - flash specific information for ++ * spi-masters that provide accelerated flash read interfaces ++ * @buf: buffer to read data ++ * @from: offset within the flash from where data is to be read ++ * @len: length of data to be read ++ * @retlen: actual length of data read ++ * @read_opcode: read_opcode to be used to communicate with flash ++ * @addr_width: number of address bytes ++ * @dummy_bytes: number of dummy bytes ++ * @opcode_nbits: number of lines to send opcode ++ * @addr_nbits: number of lines to send address ++ * @data_nbits: number of lines for data ++ */ ++struct spi_flash_read_message { ++ void *buf; ++ loff_t from; ++ size_t len; ++ size_t retlen; ++ u8 read_opcode; ++ u8 addr_width; ++ u8 dummy_bytes; ++ u8 opcode_nbits; ++ u8 addr_nbits; ++ u8 data_nbits; ++}; ++ ++/* SPI core interface for flash read support */ ++static inline bool spi_flash_read_supported(struct spi_device *spi) ++{ ++ return spi->master->spi_flash_read ? true : false; ++} ++ ++int spi_flash_read(struct spi_device *spi, ++ struct spi_flash_read_message *msg); ++ + /*---------------------------------------------------------------------------*/ + + /* diff --git a/target/linux/generic/patches-4.4/081-spi-bcm53xx-add-spi_flash_read-callback-for-MMIO-bas.patch b/target/linux/generic/patches-4.4/081-spi-bcm53xx-add-spi_flash_read-callback-for-MMIO-bas.patch new file mode 100644 index 0000000000..5f131e76d3 --- /dev/null +++ b/target/linux/generic/patches-4.4/081-spi-bcm53xx-add-spi_flash_read-callback-for-MMIO-bas.patch @@ -0,0 +1,157 @@ +From a7b221d8f0d75511c5f959584712a5dd35f88a86 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= +Date: Mon, 18 Apr 2016 14:39:30 +0200 +Subject: [PATCH] spi: bcm53xx: add spi_flash_read callback for MMIO-based + reads +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This implements more efficient reads of SPI-attached flash content. + +Signed-off-by: Rafał Miłecki +Signed-off-by: Mark Brown +--- + +--- a/drivers/spi/spi-bcm53xx.c ++++ b/drivers/spi/spi-bcm53xx.c +@@ -10,6 +10,7 @@ + #include "spi-bcm53xx.h" + + #define BCM53XXSPI_MAX_SPI_BAUD 13500000 /* 216 MHz? */ ++#define BCM53XXSPI_FLASH_WINDOW SZ_32M + + /* The longest observed required wait was 19 ms */ + #define BCM53XXSPI_SPE_TIMEOUT_MS 80 +@@ -17,8 +18,10 @@ + struct bcm53xxspi { + struct bcma_device *core; + struct spi_master *master; ++ void __iomem *mmio_base; + + size_t read_offset; ++ bool bspi; /* Boot SPI mode with memory mapping */ + }; + + static inline u32 bcm53xxspi_read(struct bcm53xxspi *b53spi, u16 offset) +@@ -32,6 +35,50 @@ static inline void bcm53xxspi_write(struct bcm53xxspi *b53spi, u16 offset, + bcma_write32(b53spi->core, offset, value); + } + ++static void bcm53xxspi_disable_bspi(struct bcm53xxspi *b53spi) ++{ ++ struct device *dev = &b53spi->core->dev; ++ unsigned long deadline; ++ u32 tmp; ++ ++ if (!b53spi->bspi) ++ return; ++ ++ tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL); ++ if (tmp & 0x1) ++ return; ++ ++ deadline = jiffies + usecs_to_jiffies(200); ++ do { ++ tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_BUSY_STATUS); ++ if (!(tmp & 0x1)) { ++ bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL, ++ 0x1); ++ ndelay(200); ++ b53spi->bspi = false; ++ return; ++ } ++ udelay(1); ++ } while (!time_after_eq(jiffies, deadline)); ++ ++ dev_warn(dev, "Timeout disabling BSPI\n"); ++} ++ ++static void bcm53xxspi_enable_bspi(struct bcm53xxspi *b53spi) ++{ ++ u32 tmp; ++ ++ if (b53spi->bspi) ++ return; ++ ++ tmp = bcm53xxspi_read(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL); ++ if (!(tmp & 0x1)) ++ return; ++ ++ bcm53xxspi_write(b53spi, B53SPI_BSPI_MAST_N_BOOT_CTRL, 0x0); ++ b53spi->bspi = true; ++} ++ + static inline unsigned int bcm53xxspi_calc_timeout(size_t len) + { + /* Do some magic calculation based on length and buad. Add 10% and 1. */ +@@ -176,6 +223,8 @@ static int bcm53xxspi_transfer_one(struct spi_master *master, + u8 *buf; + size_t left; + ++ bcm53xxspi_disable_bspi(b53spi); ++ + if (t->tx_buf) { + buf = (u8 *)t->tx_buf; + left = t->len; +@@ -206,6 +255,22 @@ static int bcm53xxspi_transfer_one(struct spi_master *master, + return 0; + } + ++static int bcm53xxspi_flash_read(struct spi_device *spi, ++ struct spi_flash_read_message *msg) ++{ ++ struct bcm53xxspi *b53spi = spi_master_get_devdata(spi->master); ++ int ret = 0; ++ ++ if (msg->from + msg->len > BCM53XXSPI_FLASH_WINDOW) ++ return -EINVAL; ++ ++ bcm53xxspi_enable_bspi(b53spi); ++ memcpy_fromio(msg->buf, b53spi->mmio_base + msg->from, msg->len); ++ msg->retlen = msg->len; ++ ++ return ret; ++} ++ + /************************************************** + * BCMA + **************************************************/ +@@ -222,6 +287,7 @@ MODULE_DEVICE_TABLE(bcma, bcm53xxspi_bcma_tbl); + + static int bcm53xxspi_bcma_probe(struct bcma_device *core) + { ++ struct device *dev = &core->dev; + struct bcm53xxspi *b53spi; + struct spi_master *master; + int err; +@@ -231,7 +297,7 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core) + return -ENOTSUPP; + } + +- master = spi_alloc_master(&core->dev, sizeof(*b53spi)); ++ master = spi_alloc_master(dev, sizeof(*b53spi)); + if (!master) + return -ENOMEM; + +@@ -239,11 +305,19 @@ static int bcm53xxspi_bcma_probe(struct bcma_device *core) + b53spi->master = master; + b53spi->core = core; + ++ if (core->addr_s[0]) ++ b53spi->mmio_base = devm_ioremap(dev, core->addr_s[0], ++ BCM53XXSPI_FLASH_WINDOW); ++ b53spi->bspi = true; ++ bcm53xxspi_disable_bspi(b53spi); ++ + master->transfer_one = bcm53xxspi_transfer_one; ++ if (b53spi->mmio_base) ++ master->spi_flash_read = bcm53xxspi_flash_read; + + bcma_set_drvdata(core, b53spi); + +- err = devm_spi_register_master(&core->dev, master); ++ err = devm_spi_register_master(dev, master); + if (err) { + spi_master_put(master); + bcma_set_drvdata(core, NULL);