Browse Source

kernel: backport support for accelerated SPI flash read

Signed-off-by: Rafał Miłecki <[email protected]>

SVN-Revision: 49233
Rafał Miłecki 9 years ago
parent
commit
83ca0efb3e

+ 46 - 0
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 <[email protected]>
+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 <[email protected]>
+[Brian: add memset()]
+Signed-off-by: Brian Norris <[email protected]>
+---
+
+--- 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));
+ 

+ 179 - 0
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 <[email protected]>
+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 <[email protected]>
+Signed-off-by: Mark Brown <[email protected]>
+---
+
+--- 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);
++
+ /*---------------------------------------------------------------------------*/
+ 
+ /*

+ 157 - 0
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?= <[email protected]>
+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 <[email protected]>
+Signed-off-by: Mark Brown <[email protected]>
+---
+
+--- 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);