kernel: backport support for accelerated SPI flash read
Signed-off-by: Rafał Miłecki <zajec5@gmail.com> SVN-Revision: 49233
This commit is contained in:
parent
e53d533a24
commit
83ca0efb3e
3 changed files with 382 additions and 0 deletions
|
@ -0,0 +1,46 @@
|
|||
From 08922f644878c9163ada8df3ef9def89be1d5e90 Mon Sep 17 00:00:00 2001
|
||||
From: Vignesh R <vigneshr@ti.com>
|
||||
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 <vigneshr@ti.com>
|
||||
[Brian: add memset()]
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
---
|
||||
|
||||
--- 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));
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
From 556351f14e74db4cd3ddde386457edce7bf0b27f Mon Sep 17 00:00:00 2001
|
||||
From: Vignesh R <vigneshr@ti.com>
|
||||
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 <vigneshr@ti.com>
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
|
||||
--- 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);
|
||||
+
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/*
|
|
@ -0,0 +1,157 @@
|
|||
From a7b221d8f0d75511c5f959584712a5dd35f88a86 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <zajec5@gmail.com>
|
||||
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 <zajec5@gmail.com>
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
|
||||
--- 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);
|
Loading…
Reference in a new issue