Backport upstream solution for working around SPI controller maximum message sizes. Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>master
parent
f62e02cf20
commit
2a2b16210b
@ -0,0 +1,51 @@ |
||||
From 4acad4aae10d1fa79a075b38b5c73772c44f576c Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Wed, 2 Dec 2015 10:38:21 +0000
|
||||
Subject: [PATCH] spi: expose master transfer size limitation.
|
||||
|
||||
On some SPI controllers it is not feasible to transfer arbitrary amount
|
||||
of data at once.
|
||||
|
||||
When the limit on transfer size is a few kilobytes at least it makes
|
||||
sense to use the SPI hardware rather than reverting to gpio driver.
|
||||
|
||||
The protocol drivers need a way to check that they do not sent overly
|
||||
long messages, though.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
include/linux/spi/spi.h | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/include/linux/spi/spi.h
|
||||
+++ b/include/linux/spi/spi.h
|
||||
@@ -428,6 +428,12 @@ struct spi_master {
|
||||
#define SPI_MASTER_MUST_RX BIT(3) /* requires rx */
|
||||
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
|
||||
|
||||
+ /*
|
||||
+ * on some hardware transfer size may be constrained
|
||||
+ * the limit may depend on device transfer settings
|
||||
+ */
|
||||
+ size_t (*max_transfer_size)(struct spi_device *spi);
|
||||
+
|
||||
/* lock and mutex for SPI bus locking */
|
||||
spinlock_t bus_lock_spinlock;
|
||||
struct mutex bus_lock_mutex;
|
||||
@@ -837,6 +843,15 @@ extern int spi_async(struct spi_device *
|
||||
extern int spi_async_locked(struct spi_device *spi,
|
||||
struct spi_message *message);
|
||||
|
||||
+static inline size_t
|
||||
+spi_max_transfer_size(struct spi_device *spi)
|
||||
+{
|
||||
+ struct spi_master *master = spi->master;
|
||||
+ if (!master->max_transfer_size)
|
||||
+ return SIZE_MAX;
|
||||
+ return master->max_transfer_size(spi);
|
||||
+}
|
||||
+
|
||||
/*---------------------------------------------------------------------------*/
|
||||
|
||||
/* All these synchronous SPI transfer routines are utilities layered
|
@ -0,0 +1,149 @@ |
||||
From 59451e1233bd315c5379a631838a03d80e689581 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:47 -0700
|
||||
Subject: [PATCH 01/10] mtd: spi-nor: change return value of read/write
|
||||
|
||||
Change the return value of spi-nor device read and write methods to
|
||||
allow returning amount of data transferred and errors as
|
||||
read(2)/write(2) does.
|
||||
|
||||
Also, start handling positive returns in spi_nor_read(), since we want
|
||||
to convert drivers to start returning the read-length both via *retlen
|
||||
and the return code. (We don't need to do the same transition process
|
||||
for spi_nor_write(), since ->write() didn't used to have a return code
|
||||
at all.)
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 5 +++--
|
||||
drivers/mtd/spi-nor/fsl-quadspi.c | 5 +++--
|
||||
drivers/mtd/spi-nor/nxp-spifi.c | 12 ++++++------
|
||||
drivers/mtd/spi-nor/spi-nor.c | 5 ++++-
|
||||
include/linux/mtd/spi-nor.h | 4 ++--
|
||||
6 files changed, 36 insertions(+), 21 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -73,7 +73,7 @@ static int m25p80_write_reg(struct spi_n
|
||||
return spi_write(spi, flash->command, len + 1);
|
||||
}
|
||||
|
||||
-static void m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
+static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
@@ -101,6 +101,7 @@ static void m25p80_write(struct spi_nor
|
||||
spi_sync(spi, &m);
|
||||
|
||||
*retlen += m.actual_length - cmd_sz;
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
|
||||
@@ -119,7 +120,7 @@ static inline unsigned int m25p80_rx_nbi
|
||||
* Read an address range from the nor chip. The address range
|
||||
* may be any size provided it is within the physical boundaries.
|
||||
*/
|
||||
-static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
+static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
@@ -822,7 +822,7 @@ static int fsl_qspi_write_reg(struct spi
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static void fsl_qspi_write(struct spi_nor *nor, loff_t to,
|
||||
+static ssize_t fsl_qspi_write(struct spi_nor *nor, loff_t to,
|
||||
size_t len, size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
@@ -832,9 +832,10 @@ static void fsl_qspi_write(struct spi_no
|
||||
|
||||
/* invalid the data in the AHB buffer. */
|
||||
fsl_qspi_invalid(q);
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
-static int fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||
+static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
--- a/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
@@ -172,8 +172,8 @@ static int nxp_spifi_write_reg(struct sp
|
||||
return nxp_spifi_wait_for_cmd(spifi);
|
||||
}
|
||||
|
||||
-static int nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
- size_t *retlen, u_char *buf)
|
||||
+static ssize_t nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
+ size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct nxp_spifi *spifi = nor->priv;
|
||||
int ret;
|
||||
@@ -188,8 +188,8 @@ static int nxp_spifi_read(struct spi_nor
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
- size_t *retlen, const u_char *buf)
|
||||
+static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
+ size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct nxp_spifi *spifi = nor->priv;
|
||||
u32 cmd;
|
||||
@@ -197,7 +197,7 @@ static void nxp_spifi_write(struct spi_n
|
||||
|
||||
ret = nxp_spifi_set_memory_mode_off(spifi);
|
||||
if (ret)
|
||||
- return;
|
||||
+ return ret;
|
||||
|
||||
writel(to, spifi->io_base + SPIFI_ADDR);
|
||||
*retlen += len;
|
||||
@@ -212,7 +212,7 @@ static void nxp_spifi_write(struct spi_n
|
||||
while (len--)
|
||||
writeb(*buf++, spifi->io_base + SPIFI_DATA);
|
||||
|
||||
- nxp_spifi_wait_for_cmd(spifi);
|
||||
+ return nxp_spifi_wait_for_cmd(spifi);
|
||||
}
|
||||
|
||||
static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -893,7 +893,10 @@ static int spi_nor_read(struct mtd_info
|
||||
ret = nor->read(nor, from, len, retlen, buf);
|
||||
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
|
||||
- return ret;
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
||||
--- a/include/linux/mtd/spi-nor.h
|
||||
+++ b/include/linux/mtd/spi-nor.h
|
||||
@@ -169,9 +169,9 @@ struct spi_nor {
|
||||
int (*read_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
|
||||
- int (*read)(struct spi_nor *nor, loff_t from,
|
||||
+ ssize_t (*read)(struct spi_nor *nor, loff_t from,
|
||||
size_t len, size_t *retlen, u_char *read_buf);
|
||||
- void (*write)(struct spi_nor *nor, loff_t to,
|
||||
+ ssize_t (*write)(struct spi_nor *nor, loff_t to,
|
||||
size_t len, size_t *retlen, const u_char *write_buf);
|
||||
int (*erase)(struct spi_nor *nor, loff_t offs);
|
||||
|
@ -0,0 +1,91 @@ |
||||
From 1992297b0810a42d78ec7b4de15304eb0489fd97 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:48 -0700
|
||||
Subject: [PATCH 02/10] mtd: m25p80: return amount of data transferred or error
|
||||
in read/write
|
||||
|
||||
Add checking of SPI transfer errors and return them from read/write
|
||||
functions. Also return the amount of data transferred.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 29 +++++++++++++++++++++--------
|
||||
1 file changed, 21 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -81,6 +81,7 @@ static ssize_t m25p80_write(struct spi_n
|
||||
struct spi_transfer t[2] = {};
|
||||
struct spi_message m;
|
||||
int cmd_sz = m25p_cmdsz(nor);
|
||||
+ ssize_t ret;
|
||||
|
||||
spi_message_init(&m);
|
||||
|
||||
@@ -98,10 +99,15 @@ static ssize_t m25p80_write(struct spi_n
|
||||
t[1].len = len;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
- spi_sync(spi, &m);
|
||||
+ ret = spi_sync(spi, &m);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- *retlen += m.actual_length - cmd_sz;
|
||||
- return 0;
|
||||
+ ret = m.actual_length - cmd_sz;
|
||||
+ if (ret < 0)
|
||||
+ return -EIO;
|
||||
+ *retlen += ret;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static inline unsigned int m25p80_rx_nbits(struct spi_nor *nor)
|
||||
@@ -128,13 +134,13 @@ static ssize_t m25p80_read(struct spi_no
|
||||
struct spi_transfer t[2];
|
||||
struct spi_message m;
|
||||
unsigned int dummy = nor->read_dummy;
|
||||
+ ssize_t ret;
|
||||
|
||||
/* 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));
|
||||
|
||||
@@ -151,7 +157,9 @@ static ssize_t m25p80_read(struct spi_no
|
||||
|
||||
ret = spi_flash_read(spi, &msg);
|
||||
*retlen = msg.retlen;
|
||||
- return ret;
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+ return msg.retlen;
|
||||
}
|
||||
|
||||
spi_message_init(&m);
|
||||
@@ -169,10 +177,15 @@ static ssize_t m25p80_read(struct spi_no
|
||||
t[1].len = len;
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
- spi_sync(spi, &m);
|
||||
+ ret = spi_sync(spi, &m);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- *retlen = m.actual_length - m25p_cmdsz(nor) - dummy;
|
||||
- return 0;
|
||||
+ ret = m.actual_length - m25p_cmdsz(nor) - dummy;
|
||||
+ if (ret < 0)
|
||||
+ return -EIO;
|
||||
+ *retlen += ret;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int m25p80_erase(struct spi_nor *nor, loff_t offset)
|
@ -0,0 +1,72 @@ |
||||
From fc0d7e542a0d4193521899d15f8f4999dc295323 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:49 -0700
|
||||
Subject: [PATCH 03/10] mtd: fsl-quadspi: return amount of data read/written or
|
||||
error
|
||||
|
||||
Return amount of data read/written or error as read(2)/write(2) does.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
---
|
||||
drivers/mtd/spi-nor/fsl-quadspi.c | 17 +++++++++++------
|
||||
1 file changed, 11 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
@@ -575,7 +575,7 @@ static inline void fsl_qspi_invalid(stru
|
||||
writel(reg, q->iobase + QUADSPI_MCR);
|
||||
}
|
||||
|
||||
-static int fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
||||
+static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
||||
u8 opcode, unsigned int to, u32 *txbuf,
|
||||
unsigned count, size_t *retlen)
|
||||
{
|
||||
@@ -604,8 +604,11 @@ static int fsl_qspi_nor_write(struct fsl
|
||||
/* Trigger it */
|
||||
ret = fsl_qspi_runcmd(q, opcode, to, count);
|
||||
|
||||
- if (ret == 0 && retlen)
|
||||
- *retlen += count;
|
||||
+ if (ret == 0) {
|
||||
+ if (retlen)
|
||||
+ *retlen += count;
|
||||
+ return count;
|
||||
+ }
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -814,6 +817,8 @@ static int fsl_qspi_write_reg(struct spi
|
||||
} else if (len > 0) {
|
||||
ret = fsl_qspi_nor_write(q, nor, opcode, 0,
|
||||
(u32 *)buf, len, NULL);
|
||||
+ if (ret > 0)
|
||||
+ return 0;
|
||||
} else {
|
||||
dev_err(q->dev, "invalid cmd %d\n", opcode);
|
||||
ret = -EINVAL;
|
||||
@@ -827,12 +832,12 @@ static ssize_t fsl_qspi_write(struct spi
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
|
||||
- fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
|
||||
+ ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
|
||||
(u32 *)buf, len, retlen);
|
||||
|
||||
/* invalid the data in the AHB buffer. */
|
||||
fsl_qspi_invalid(q);
|
||||
- return 0;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||
@@ -879,7 +884,7 @@ static ssize_t fsl_qspi_read(struct spi_
|
||||
len);
|
||||
|
||||
*retlen += len;
|
||||
- return 0;
|
||||
+ return len;
|
||||
}
|
||||
|
||||
static int fsl_qspi_erase(struct spi_nor *nor, loff_t offs)
|
@ -0,0 +1,51 @@ |
||||
From bc418cd2652f47a327e27f978caa3d85f9558b09 Mon Sep 17 00:00:00 2001
|
||||
From: Brian Norris <computersforpeace@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:51 -0700
|
||||
Subject: [PATCH 05/10] mtd: nxp-spifi: return amount of data transferred or
|
||||
error in read/write
|
||||
|
||||
Add checking of SPI transfer errors and return them from read/write
|
||||
functions. Also return the amount of data transferred.
|
||||
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
---
|
||||
drivers/mtd/spi-nor/nxp-spifi.c | 13 +++++++++----
|
||||
1 file changed, 9 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
@@ -185,7 +185,7 @@ static ssize_t nxp_spifi_read(struct spi
|
||||
memcpy_fromio(buf, spifi->flash_base + from, len);
|
||||
*retlen += len;
|
||||
|
||||
- return 0;
|
||||
+ return len;
|
||||
}
|
||||
|
||||
static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
@@ -194,6 +194,7 @@ static ssize_t nxp_spifi_write(struct sp
|
||||
struct nxp_spifi *spifi = nor->priv;
|
||||
u32 cmd;
|
||||
int ret;
|
||||
+ size_t i;
|
||||
|
||||
ret = nxp_spifi_set_memory_mode_off(spifi);
|
||||
if (ret)
|
||||
@@ -209,10 +210,14 @@ static ssize_t nxp_spifi_write(struct sp
|
||||
SPIFI_CMD_FRAMEFORM(spifi->nor.addr_width + 1);
|
||||
writel(cmd, spifi->io_base + SPIFI_CMD);
|
||||
|
||||
- while (len--)
|
||||
- writeb(*buf++, spifi->io_base + SPIFI_DATA);
|
||||
+ for (i = 0; i < len; i++)
|
||||
+ writeb(buf[i], spifi->io_base + SPIFI_DATA);
|
||||
|
||||
- return nxp_spifi_wait_for_cmd(spifi);
|
||||
+ ret = nxp_spifi_wait_for_cmd(spifi);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return len;
|
||||
}
|
||||
|
||||
static int nxp_spifi_erase(struct spi_nor *nor, loff_t offs)
|
@ -0,0 +1,118 @@ |
||||
From 0bad7b9304d543dd7627f4cd564aea5d7338b950 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:52 -0700
|
||||
Subject: [PATCH 06/10] mtd: spi-nor: check return value from write
|
||||
|
||||
SPI NOR hardware drivers now return useful value from their write
|
||||
functions so check them.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/spi-nor/spi-nor.c | 45 ++++++++++++++++++++++++++++++-------------
|
||||
1 file changed, 32 insertions(+), 13 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -922,10 +922,14 @@ static int sst_write(struct mtd_info *mt
|
||||
nor->program_opcode = SPINOR_OP_BP;
|
||||
|
||||
/* write one byte. */
|
||||
- nor->write(nor, to, 1, retlen, buf);
|
||||
+ ret = nor->write(nor, to, 1, retlen, buf);
|
||||
+ if (ret < 0)
|
||||
+ goto sst_write_err;
|
||||
+ WARN(ret != 1, "While writing 1 byte written %i bytes\n",
|
||||
+ (int)ret);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
- goto time_out;
|
||||
+ goto sst_write_err;
|
||||
}
|
||||
to += actual;
|
||||
|
||||
@@ -934,10 +938,14 @@ static int sst_write(struct mtd_info *mt
|
||||
nor->program_opcode = SPINOR_OP_AAI_WP;
|
||||
|
||||
/* write two bytes. */
|
||||
- nor->write(nor, to, 2, retlen, buf + actual);
|
||||
+ ret = nor->write(nor, to, 2, retlen, buf + actual);
|
||||
+ if (ret < 0)
|
||||
+ goto sst_write_err;
|
||||
+ WARN(ret != 2, "While writing 2 bytes written %i bytes\n",
|
||||
+ (int)ret);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
- goto time_out;
|
||||
+ goto sst_write_err;
|
||||
to += 2;
|
||||
nor->sst_write_second = true;
|
||||
}
|
||||
@@ -946,21 +954,24 @@ static int sst_write(struct mtd_info *mt
|
||||
write_disable(nor);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
- goto time_out;
|
||||
+ goto sst_write_err;
|
||||
|
||||
/* Write out trailing byte if it exists. */
|
||||
if (actual != len) {
|
||||
write_enable(nor);
|
||||
|
||||
nor->program_opcode = SPINOR_OP_BP;
|
||||
- nor->write(nor, to, 1, retlen, buf + actual);
|
||||
-
|
||||
+ ret = nor->write(nor, to, 1, retlen, buf + actual);
|
||||
+ if (ret < 0)
|
||||
+ goto sst_write_err;
|
||||
+ WARN(ret != 1, "While writing 1 byte written %i bytes\n",
|
||||
+ (int)ret);
|
||||
ret = spi_nor_wait_till_ready(nor);
|
||||
if (ret)
|
||||
- goto time_out;
|
||||
+ goto sst_write_err;
|
||||
write_disable(nor);
|
||||
}
|
||||
-time_out:
|
||||
+sst_write_err:
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
|
||||
return ret;
|
||||
}
|
||||
@@ -989,14 +1000,18 @@ static int spi_nor_write(struct mtd_info
|
||||
|
||||
/* do all the bytes fit onto one page? */
|
||||
if (page_offset + len <= nor->page_size) {
|
||||
- nor->write(nor, to, len, retlen, buf);
|
||||
+ ret = nor->write(nor, to, len, retlen, buf);
|
||||
+ if (ret < 0)
|
||||
+ goto write_err;
|
||||
} else {
|
||||
/* the size of data remaining on the first page */
|
||||
page_size = nor->page_size - page_offset;
|
||||
- nor->write(nor, to, page_size, retlen, buf);
|
||||
+ ret = nor->write(nor, to, page_size, retlen, buf);
|
||||
+ if (ret < 0)
|
||||
+ goto write_err;
|
||||
|
||||
/* write everything in nor->page_size chunks */
|
||||
- for (i = page_size; i < len; i += page_size) {
|
||||
+ for (i = ret; i < len; ) {
|
||||
page_size = len - i;
|
||||
if (page_size > nor->page_size)
|
||||
page_size = nor->page_size;
|
||||
@@ -1007,7 +1022,11 @@ static int spi_nor_write(struct mtd_info
|
||||
|
||||
write_enable(nor);
|
||||
|
||||
- nor->write(nor, to + i, page_size, retlen, buf + i);
|
||||
+ ret = nor->write(nor, to + i, page_size, retlen,
|
||||
+ buf + i);
|
||||
+ if (ret < 0)
|
||||
+ goto write_err;
|
||||
+ i += ret;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,266 @@ |
||||
From 2dd087b16946cf168f401526adf26afa771bb740 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:53 -0700
|
||||
Subject: [PATCH 07/10] mtd: spi-nor: stop passing around retlen
|
||||
|
||||
Do not pass retlen to hardware driver read/write functions. Update it in
|
||||
spi-nor generic driver instead.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 7 ++-----
|
||||
drivers/mtd/spi-nor/fsl-quadspi.c | 17 ++++++-----------
|
||||
drivers/mtd/spi-nor/nxp-spifi.c | 6 ++----
|
||||
drivers/mtd/spi-nor/spi-nor.c | 21 +++++++++++++--------
|
||||
include/linux/mtd/spi-nor.h | 4 ++--
|
||||
6 files changed, 28 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -74,7 +74,7 @@ static int m25p80_write_reg(struct spi_n
|
||||
}
|
||||
|
||||
static ssize_t m25p80_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
- size_t *retlen, const u_char *buf)
|
||||
+ const u_char *buf)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
struct spi_device *spi = flash->spi;
|
||||
@@ -106,7 +106,6 @@ static ssize_t m25p80_write(struct spi_n
|
||||
ret = m.actual_length - cmd_sz;
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
- *retlen += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -127,7 +126,7 @@ static inline unsigned int m25p80_rx_nbi
|
||||
* may be any size provided it is within the physical boundaries.
|
||||
*/
|
||||
static ssize_t m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
- size_t *retlen, u_char *buf)
|
||||
+ u_char *buf)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
struct spi_device *spi = flash->spi;
|
||||
@@ -156,7 +155,6 @@ static ssize_t m25p80_read(struct spi_no
|
||||
msg.data_nbits = m25p80_rx_nbits(nor);
|
||||
|
||||
ret = spi_flash_read(spi, &msg);
|
||||
- *retlen = msg.retlen;
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
return msg.retlen;
|
||||
@@ -184,7 +182,6 @@ static ssize_t m25p80_read(struct spi_no
|
||||
ret = m.actual_length - m25p_cmdsz(nor) - dummy;
|
||||
if (ret < 0)
|
||||
return -EIO;
|
||||
- *retlen += ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
--- a/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
+++ b/drivers/mtd/spi-nor/fsl-quadspi.c
|
||||
@@ -577,7 +577,7 @@ static inline void fsl_qspi_invalid(stru
|
||||
|
||||
static ssize_t fsl_qspi_nor_write(struct fsl_qspi *q, struct spi_nor *nor,
|
||||
u8 opcode, unsigned int to, u32 *txbuf,
|
||||
- unsigned count, size_t *retlen)
|
||||
+ unsigned count)
|
||||
{
|
||||
int ret, i, j;
|
||||
u32 tmp;
|
||||
@@ -604,11 +604,8 @@ static ssize_t fsl_qspi_nor_write(struct
|
||||
/* Trigger it */
|
||||
ret = fsl_qspi_runcmd(q, opcode, to, count);
|
||||
|
||||
- if (ret == 0) {
|
||||
- if (retlen)
|
||||
- *retlen += count;
|
||||
+ if (ret == 0)
|
||||
return count;
|
||||
- }
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -816,7 +813,7 @@ static int fsl_qspi_write_reg(struct spi
|
||||
|
||||
} else if (len > 0) {
|
||||
ret = fsl_qspi_nor_write(q, nor, opcode, 0,
|
||||
- (u32 *)buf, len, NULL);
|
||||
+ (u32 *)buf, len);
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
} else {
|
||||
@@ -828,12 +825,11 @@ static int fsl_qspi_write_reg(struct spi
|
||||
}
|
||||
|
||||
static ssize_t fsl_qspi_write(struct spi_nor *nor, loff_t to,
|
||||
- size_t len, size_t *retlen, const u_char *buf)
|
||||
+ size_t len, const u_char *buf)
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
-
|
||||
ssize_t ret = fsl_qspi_nor_write(q, nor, nor->program_opcode, to,
|
||||
- (u32 *)buf, len, retlen);
|
||||
+ (u32 *)buf, len);
|
||||
|
||||
/* invalid the data in the AHB buffer. */
|
||||
fsl_qspi_invalid(q);
|
||||
@@ -841,7 +837,7 @@ static ssize_t fsl_qspi_write(struct spi
|
||||
}
|
||||
|
||||
static ssize_t fsl_qspi_read(struct spi_nor *nor, loff_t from,
|
||||
- size_t len, size_t *retlen, u_char *buf)
|
||||
+ size_t len, u_char *buf)
|
||||
{
|
||||
struct fsl_qspi *q = nor->priv;
|
||||
u8 cmd = nor->read_opcode;
|
||||
@@ -883,7 +879,6 @@ static ssize_t fsl_qspi_read(struct spi_
|
||||
memcpy(buf, q->ahb_addr + q->chip_base_addr + from - q->memmap_offs,
|
||||
len);
|
||||
|
||||
- *retlen += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
--- a/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
+++ b/drivers/mtd/spi-nor/nxp-spifi.c
|
||||
@@ -173,7 +173,7 @@ static int nxp_spifi_write_reg(struct sp
|
||||
}
|
||||
|
||||
static ssize_t nxp_spifi_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
- size_t *retlen, u_char *buf)
|
||||
+ u_char *buf)
|
||||
{
|
||||
struct nxp_spifi *spifi = nor->priv;
|
||||
int ret;
|
||||
@@ -183,13 +183,12 @@ static ssize_t nxp_spifi_read(struct spi
|
||||
return ret;
|
||||
|
||||
memcpy_fromio(buf, spifi->flash_base + from, len);
|
||||
- *retlen += len;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t nxp_spifi_write(struct spi_nor *nor, loff_t to, size_t len,
|
||||
- size_t *retlen, const u_char *buf)
|
||||
+ const u_char *buf)
|
||||
{
|
||||
struct nxp_spifi *spifi = nor->priv;
|
||||
u32 cmd;
|
||||
@@ -201,7 +200,6 @@ static ssize_t nxp_spifi_write(struct sp
|
||||
return ret;
|
||||
|
||||
writel(to, spifi->io_base + SPIFI_ADDR);
|
||||
- *retlen += len;
|
||||
|
||||
cmd = SPIFI_CMD_DOUT |
|
||||
SPIFI_CMD_DATALEN(len) |
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -890,12 +890,13 @@ static int spi_nor_read(struct mtd_info
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = nor->read(nor, from, len, retlen, buf);
|
||||
+ ret = nor->read(nor, from, len, buf);
|
||||
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+ *retlen += ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -922,7 +923,7 @@ static int sst_write(struct mtd_info *mt
|
||||
nor->program_opcode = SPINOR_OP_BP;
|
||||
|
||||
/* write one byte. */
|
||||
- ret = nor->write(nor, to, 1, retlen, buf);
|
||||
+ ret = nor->write(nor, to, 1, buf);
|
||||
if (ret < 0)
|
||||
goto sst_write_err;
|
||||
WARN(ret != 1, "While writing 1 byte written %i bytes\n",
|
||||
@@ -938,7 +939,7 @@ static int sst_write(struct mtd_info *mt
|
||||
nor->program_opcode = SPINOR_OP_AAI_WP;
|
||||
|
||||
/* write two bytes. */
|
||||
- ret = nor->write(nor, to, 2, retlen, buf + actual);
|
||||
+ ret = nor->write(nor, to, 2, buf + actual);
|
||||
if (ret < 0)
|
||||
goto sst_write_err;
|
||||
WARN(ret != 2, "While writing 2 bytes written %i bytes\n",
|
||||
@@ -961,7 +962,7 @@ static int sst_write(struct mtd_info *mt
|
||||
write_enable(nor);
|
||||
|
||||
nor->program_opcode = SPINOR_OP_BP;
|
||||
- ret = nor->write(nor, to, 1, retlen, buf + actual);
|
||||
+ ret = nor->write(nor, to, 1, buf + actual);
|
||||
if (ret < 0)
|
||||
goto sst_write_err;
|
||||
WARN(ret != 1, "While writing 1 byte written %i bytes\n",
|
||||
@@ -970,8 +971,10 @@ static int sst_write(struct mtd_info *mt
|
||||
if (ret)
|
||||
goto sst_write_err;
|
||||
write_disable(nor);
|
||||
+ actual += 1;
|
||||
}
|
||||
sst_write_err:
|
||||
+ *retlen += actual;
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
|
||||
return ret;
|
||||
}
|
||||
@@ -1000,15 +1003,17 @@ static int spi_nor_write(struct mtd_info
|
||||
|
||||
/* do all the bytes fit onto one page? */
|
||||
if (page_offset + len <= nor->page_size) {
|
||||
- ret = nor->write(nor, to, len, retlen, buf);
|
||||
+ ret = nor->write(nor, to, len, buf);
|
||||
if (ret < 0)
|
||||
goto write_err;
|
||||
+ *retlen += ret;
|
||||
} else {
|
||||
/* the size of data remaining on the first page */
|
||||
page_size = nor->page_size - page_offset;
|
||||
- ret = nor->write(nor, to, page_size, retlen, buf);
|
||||
+ ret = nor->write(nor, to, page_size, buf);
|
||||
if (ret < 0)
|
||||
goto write_err;
|
||||
+ *retlen += ret;
|
||||
|
||||
/* write everything in nor->page_size chunks */
|
||||
for (i = ret; i < len; ) {
|
||||
@@ -1022,10 +1027,10 @@ static int spi_nor_write(struct mtd_info
|
||||
|
||||
write_enable(nor);
|
||||
|
||||
- ret = nor->write(nor, to + i, page_size, retlen,
|
||||
- buf + i);
|
||||
+ ret = nor->write(nor, to + i, page_size, buf + i);
|
||||
if (ret < 0)
|
||||
goto write_err;
|
||||
+ *retlen += ret;
|
||||
i += ret;
|
||||
}
|
||||
}
|
||||
--- a/include/linux/mtd/spi-nor.h
|
||||
+++ b/include/linux/mtd/spi-nor.h
|
||||
@@ -170,9 +170,9 @@ struct spi_nor {
|
||||
int (*write_reg)(struct spi_nor *nor, u8 opcode, u8 *buf, int len);
|
||||
|
||||
ssize_t (*read)(struct spi_nor *nor, loff_t from,
|
||||
- size_t len, size_t *retlen, u_char *read_buf);
|
||||
+ size_t len, u_char *read_buf);
|
||||
ssize_t (*write)(struct spi_nor *nor, loff_t to,
|
||||
- size_t len, size_t *retlen, const u_char *write_buf);
|
||||
+ size_t len, const u_char *write_buf);
|
||||
int (*erase)(struct spi_nor *nor, loff_t offs);
|
||||
|
||||
int (*flash_lock)(struct spi_nor *nor, loff_t ofs, uint64_t len);
|
@ -0,0 +1,103 @@ |
||||
From e5d05cbd6d8b01f08c95c427a36c66aac769af4f Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:54 -0700
|
||||
Subject: [PATCH 08/10] mtd: spi-nor: simplify write loop
|
||||
|
||||
The spi-nor write loop assumes that what is passed to the hardware
|
||||
driver write() is what gets written.
|
||||
|
||||
When write() writes less than page size at once data is dropped on the
|
||||
floor. Check the amount of data writen and exit if it does not match
|
||||
requested amount.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/spi-nor/spi-nor.c | 58 +++++++++++++++++++------------------------
|
||||
1 file changed, 25 insertions(+), 33 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -988,8 +988,8 @@ static int spi_nor_write(struct mtd_info
|
||||
size_t *retlen, const u_char *buf)
|
||||
{
|
||||
struct spi_nor *nor = mtd_to_spi_nor(mtd);
|
||||
- u32 page_offset, page_size, i;
|
||||
- int ret;
|
||||
+ size_t page_offset, page_remain, i;
|
||||
+ ssize_t ret;
|
||||
|
||||
dev_dbg(nor->dev, "to 0x%08x, len %zd\n", (u32)to, len);
|
||||
|
||||
@@ -997,45 +997,37 @@ static int spi_nor_write(struct mtd_info
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- write_enable(nor);
|
||||
+ for (i = 0; i < len; ) {
|
||||
+ ssize_t written;
|
||||
|
||||
- page_offset = to & (nor->page_size - 1);
|
||||
-
|
||||
- /* do all the bytes fit onto one page? */
|
||||
- if (page_offset + len <= nor->page_size) {
|
||||
- ret = nor->write(nor, to, len, buf);
|
||||
- if (ret < 0)
|
||||
- goto write_err;
|
||||
- *retlen += ret;
|
||||
- } else {
|
||||
+ page_offset = (to + i) & (nor->page_size - 1);
|
||||
+ WARN_ONCE(page_offset,
|
||||
+ "Writing at offset %zu into a NOR page. Writing partial pages may decrease reliability and increase wear of NOR flash.",
|
||||
+ page_offset);
|
||||
/* the size of data remaining on the first page */
|
||||
- page_size = nor->page_size - page_offset;
|
||||
- ret = nor->write(nor, to, page_size, buf);
|
||||
+ page_remain = min_t(size_t,
|
||||
+ nor->page_size - page_offset, len - i);
|
||||
+
|
||||
+ write_enable(nor);
|
||||
+ ret = nor->write(nor, to + i, page_remain, buf + i);
|
||||
if (ret < 0)
|
||||
goto write_err;
|
||||
- *retlen += ret;
|
||||
+ written = ret;
|
||||
|
||||
- /* write everything in nor->page_size chunks */
|
||||
- for (i = ret; i < len; ) {
|
||||
- page_size = len - i;
|
||||
- if (page_size > nor->page_size)
|
||||
- page_size = nor->page_size;
|
||||
-
|
||||
- ret = spi_nor_wait_till_ready(nor);
|
||||
- if (ret)
|
||||
- goto write_err;
|
||||
-
|
||||
- write_enable(nor);
|
||||
-
|
||||
- ret = nor->write(nor, to + i, page_size, buf + i);
|
||||
- if (ret < 0)
|
||||
- goto write_err;
|
||||
- *retlen += ret;
|
||||
- i += ret;
|
||||
+ ret = spi_nor_wait_till_ready(nor);
|
||||
+ if (ret)
|
||||
+ goto write_err;
|
||||
+ *retlen += written;
|
||||
+ i += written;
|
||||
+ if (written != page_remain) {
|
||||
+ dev_err(nor->dev,
|
||||
+ "While writing %zu bytes written %zd bytes\n",
|
||||
+ page_remain, written);
|
||||
+ ret = -EIO;
|
||||
+ goto write_err;
|
||||
}
|
||||
}
|
||||
|
||||
- ret = spi_nor_wait_till_ready(nor);
|
||||
write_err:
|
||||
spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
|
||||
return ret;
|
@ -0,0 +1,54 @@ |
||||
From 26f9bcad29a6c240881bd4efc90f16a9990dd6c2 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:55 -0700
|
||||
Subject: [PATCH 09/10] mtd: spi-nor: add read loop
|
||||
|
||||
mtdblock and ubi do not handle the situation when read returns less data
|
||||
than requested. Loop in spi-nor until buffer is filled or an error is
|
||||
returned.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Tested-by Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/spi-nor/spi-nor.c | 25 +++++++++++++++++++------
|
||||
1 file changed, 19 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/spi-nor.c
|
||||
+++ b/drivers/mtd/spi-nor/spi-nor.c
|
||||
@@ -890,14 +890,27 @@ static int spi_nor_read(struct mtd_info
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = nor->read(nor, from, len, buf);
|
||||
+ while (len) {
|
||||
+ ret = nor->read(nor, from, len, buf);
|
||||
+ if (ret == 0) {
|
||||
+ /* We shouldn't see 0-length reads */
|
||||
+ ret = -EIO;
|
||||
+ goto read_err;
|
||||
+ }
|
||||
+ if (ret < 0)
|
||||
+ goto read_err;
|
||||
|
||||
- spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
+ WARN_ON(ret > len);
|
||||
+ *retlen += ret;
|
||||
+ buf += ret;
|
||||
+ from += ret;
|
||||
+ len -= ret;
|
||||
+ }
|
||||
+ ret = 0;
|
||||
|
||||
- *retlen += ret;
|
||||
- return 0;
|
||||
+read_err:
|
||||
+ spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static int sst_write(struct mtd_info *mtd, loff_t to, size_t len,
|
@ -0,0 +1,26 @@ |
||||
From 95193796256cfce16e5d881318e15b6b04062c15 Mon Sep 17 00:00:00 2001
|
||||
From: Michal Suchanek <hramrach@gmail.com>
|
||||
Date: Thu, 5 May 2016 17:31:56 -0700
|
||||
Subject: [PATCH 10/10] mtd: m25p80: read in spi_max_transfer_size chunks
|
||||
|
||||
Take into account transfer size limitation of SPI master.
|
||||
|
||||
Signed-off-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
|
||||
Acked-by: Michal Suchanek <hramrach@gmail.com>
|
||||
Tested-by: Michal Suchanek <hramrach@gmail.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -172,7 +172,7 @@ static ssize_t m25p80_read(struct spi_no
|
||||
|
||||
t[1].rx_buf = buf;
|
||||
t[1].rx_nbits = m25p80_rx_nbits(nor);
|
||||
- t[1].len = len;
|
||||
+ t[1].len = min(len, spi_max_transfer_size(spi));
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
ret = spi_sync(spi, &m);
|
@ -0,0 +1,73 @@ |
||||
From 5090cc6ae2f79ee779e5faf7c8a28edf42b7d738 Mon Sep 17 00:00:00 2001
|
||||
From: Heiner Kallweit <hkallweit1@gmail.com>
|
||||
Date: Wed, 17 Aug 2016 21:08:01 +0200
|
||||
Subject: [PATCH] spi: introduce max_message_size hook in spi_master
|
||||
|
||||
Recently a maximum transfer size was was introduced in struct spi_master.
|
||||
However there are also spi controllers with a maximum message size, e.g.
|
||||
fsl-espi has a max message size of 64KB.
|
||||
Introduce a hook max_message_size to deal with such limitations.
|
||||
|
||||
Also make sure that spi_max_transfer_size doesn't return greater values
|
||||
than spi_max_message_size, even if hook max_transfer_size is not set.
|
||||
|
||||
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
include/linux/spi/spi.h | 25 +++++++++++++++++++++----
|
||||
1 file changed, 21 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/include/linux/spi/spi.h
|
||||
+++ b/include/linux/spi/spi.h
|
||||
@@ -304,6 +304,8 @@ static inline void spi_unregister_driver
|
||||
* @min_speed_hz: Lowest supported transfer speed
|
||||
* @max_speed_hz: Highest supported transfer speed
|
||||
* @flags: other constraints relevant to this driver
|
||||
+ * @max_message_size: function that returns the max message size for
|
||||
+ * a &spi_device; may be %NULL, so the default %SIZE_MAX will be used.
|
||||
* @bus_lock_spinlock: spinlock for SPI bus locking
|
||||
* @bus_lock_mutex: mutex for SPI bus locking
|
||||
* @bus_lock_flag: indicates that the SPI bus is locked for exclusive use
|
||||
@@ -429,10 +431,11 @@ struct spi_master {
|
||||
#define SPI_MASTER_MUST_TX BIT(4) /* requires tx */
|
||||
|
||||
/*
|
||||
- * on some hardware transfer size may be constrained
|
||||
+ * on some hardware transfer / message size may be constrained
|
||||
* the limit may depend on device transfer settings
|
||||
*/
|
||||
size_t (*max_transfer_size)(struct spi_device *spi);
|
||||
+ size_t (*max_message_size)(struct spi_device *spi);
|
||||
|
||||
/* lock and mutex for SPI bus locking */
|
||||
spinlock_t bus_lock_spinlock;
|
||||
@@ -844,12 +847,26 @@ extern int spi_async_locked(struct spi_d
|
||||
struct spi_message *message);
|
||||
|
||||
static inline size_t
|
||||
-spi_max_transfer_size(struct spi_device *spi)
|
||||
+spi_max_message_size(struct spi_device *spi)
|
||||
{
|
||||
struct spi_master *master = spi->master;
|
||||
- if (!master->max_transfer_size)
|
||||
+ if (!master->max_message_size)
|
||||
return SIZE_MAX;
|
||||
- return master->max_transfer_size(spi);
|
||||
+ return master->max_message_size(spi);
|
||||
+}
|
||||
+
|
||||
+static inline size_t
|
||||
+spi_max_transfer_size(struct spi_device *spi)
|
||||
+{
|
||||
+ struct spi_master *master = spi->master;
|
||||
+ size_t tr_max = SIZE_MAX;
|
||||
+ size_t msg_max = spi_max_message_size(spi);
|
||||
+
|
||||
+ if (master->max_transfer_size)
|
||||
+ tr_max = master->max_transfer_size(spi);
|
||||
+
|
||||
+ /* transfer size limit must not be greater than messsage size limit */
|
||||
+ return min(tr_max, msg_max);
|
||||
}
|
||||
|
||||
/*---------------------------------------------------------------------------*/
|
@ -0,0 +1,30 @@ |
||||
From 80a79a889ce5df16c5261ab2f1e8e63b94b78102 Mon Sep 17 00:00:00 2001
|
||||
From: Heiner Kallweit <hkallweit1@gmail.com>
|
||||
Date: Fri, 28 Oct 2016 07:58:46 +0200
|
||||
Subject: [PATCH 1/8] mtd: m25p80: consider max message size in m25p80_read
|
||||
|
||||
Consider a message size limit when calculating the maximum amount
|
||||
of data that can be read.
|
||||
|
||||
The message size limit has been introduced with 4.9, so cc it
|
||||
to stable.
|
||||
|
||||
Signed-off-by: Heiner Kallweit <hkallweit1@gmail.com>
|
||||
Cc: <stable@vger.kernel.org> # 4.9.x
|
||||
Signed-off-by: Cyrille Pitchen <cyrille.pitchen@atmel.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -172,7 +172,8 @@ static ssize_t m25p80_read(struct spi_no
|
||||
|
||||
t[1].rx_buf = buf;
|
||||
t[1].rx_nbits = m25p80_rx_nbits(nor);
|
||||
- t[1].len = min(len, spi_max_transfer_size(spi));
|
||||
+ t[1].len = min3(len, spi_max_transfer_size(spi),
|
||||
+ spi_max_message_size(spi) - t[0].len);
|
||||
spi_message_add_tail(&t[1], &m);
|
||||
|
||||
ret = spi_sync(spi, &m);
|
@ -0,0 +1,42 @@ |
||||
From 3fcc36962c32ad0af2d5904103e2b2b824b6b1aa Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Sat, 4 Feb 2017 12:32:59 +0100
|
||||
Subject: [PATCH 2/8] spi/bcm63xx: make spi subsystem aware of message size
|
||||
limits
|
||||
|
||||
The bcm63xx LS SPI controller does not allow manual control of the CS
|
||||
lines and will toggle it automatically before after sending data, so we
|
||||
are limited to messages that fit in the FIFO buffer. Since the CS lines
|
||||
aren't available as GPIOs either, we will need to make slave drivers
|
||||
aware of this limitation and handle it accordingly.
|
||||
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
drivers/spi/spi-bcm63xx.c | 9 +++++++++
|
||||
1 file changed, 9 insertions(+)
|
||||
|
||||
--- a/drivers/spi/spi-bcm63xx.c
|
||||
+++ b/drivers/spi/spi-bcm63xx.c
|
||||
@@ -429,6 +429,13 @@ static irqreturn_t bcm63xx_spi_interrupt
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
+static size_t bcm63xx_spi_max_length(struct spi_device *spi)
|
||||
+{
|
||||
+ struct bcm63xx_spi *bs = spi_master_get_devdata(spi->master);
|
||||
+
|
||||
+ return bs->fifo_size;
|
||||
+}
|
||||
+
|
||||
static const unsigned long bcm6348_spi_reg_offsets[] = {
|
||||
[SPI_CMD] = SPI_6348_CMD,
|
||||
[SPI_INT_STATUS] = SPI_6348_INT_STATUS,
|
||||
@@ -542,6 +549,8 @@ static int bcm63xx_spi_probe(struct plat
|
||||
master->transfer_one_message = bcm63xx_spi_transfer_one;
|
||||
master->mode_bits = MODEBITS;
|
||||
master->bits_per_word_mask = SPI_BPW_MASK(8);
|
||||
+ master->max_transfer_size = bcm63xx_spi_max_length;
|
||||
+ master->max_message_size = bcm63xx_spi_max_length;
|
||||
master->auto_runtime_pm = true;
|
||||
bs->msg_type_shift = bs->reg_offsets[SPI_MSG_TYPE_SHIFT];
|
||||
bs->msg_ctl_width = bs->reg_offsets[SPI_MSG_CTL_WIDTH];
|
@ -1,90 +0,0 @@ |
||||
From 5fb4e8d7287ac8fcb33aae8b1e9e22c5a3c392bd Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
Date: Thu, 10 Nov 2011 17:33:40 +0100
|
||||
Subject: [PATCH 51/79] MTD: DEVICES: m25p80: add support for limiting reads
|
||||
|
||||
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
|
||||
---
|
||||
drivers/mtd/devices/m25p80.c | 29 +++++++++++++++++++++++++++--
|
||||
include/linux/spi/flash.h | 4 ++++
|
||||
2 files changed, 31 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/devices/m25p80.c
|
||||
+++ b/drivers/mtd/devices/m25p80.c
|
||||
@@ -31,6 +31,7 @@
|
||||
struct m25p {
|
||||
struct spi_device *spi;
|
||||
struct spi_nor spi_nor;
|
||||
+ int max_transfer_len;
|
||||
u8 command[MAX_CMD_SIZE];
|
||||
};
|
||||
|
||||
@@ -119,7 +120,7 @@ static inline unsigned int m25p80_rx_nbi
|
||||
* Read an address range from the nor chip. The address range
|
||||
* may be any size provided it is within the physical boundaries.
|
||||
*/
|
||||
-static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
+static int __m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
size_t *retlen, u_char *buf)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
@@ -174,6 +175,29 @@ static int m25p80_read(struct spi_nor *n
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int m25p80_read(struct spi_nor *nor, loff_t from, size_t len,
|
||||
+ size_t *retlen, u_char *buf)
|
||||
+{
|
||||
+ struct m25p *flash = nor->priv;
|
||||
+ size_t off;
|
||||
+ size_t read_len = flash->max_transfer_len;
|
||||
+ size_t part_len;
|
||||
+ int ret = 0;
|
||||
+
|
||||
+ if (!read_len)
|
||||
+ return __m25p80_read(nor, from, len, retlen, buf);
|
||||
+
|
||||
+ *retlen = 0;
|
||||
+
|
||||
+ for (off = 0; off < len && !ret; off += read_len) {
|
||||
+ ret = __m25p80_read(nor, from + off, min(len - off, read_len),
|
||||
+ &part_len, buf + off);
|
||||
+ *retlen += part_len;
|
||||
+ }
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int m25p80_erase(struct spi_nor *nor, loff_t offset)
|
||||
{
|
||||
struct m25p *flash = nor->priv;
|
||||
@@ -244,6 +268,9 @@ static int m25p_probe(struct spi_device
|
||||
else
|
||||
flash_name = spi->modalias;
|
||||
|
||||
+ if (data)
|
||||
+ flash->max_transfer_len = data->max_transfer_len;
|
||||
+
|
||||
ret = spi_nor_scan(nor, flash_name, mode);
|
||||
if (ret)
|
||||
return ret;
|
||||
--- a/include/linux/spi/flash.h
|
||||
+++ b/include/linux/spi/flash.h
|
||||
@@ -13,6 +13,8 @@ struct mtd_part_parser_data;
|
||||
* @part_probe_types: optional list of MTD parser names to use for
|
||||
* partitioning
|
||||
*
|
||||
+ * @max_transfer_len: option maximum read/write length limitation for
|
||||
+ * SPI controllers not able to transfer any length commands.
|
||||
* Board init code (in arch/.../mach-xxx/board-yyy.c files) can
|
||||
* provide information about SPI flash parts (such as DataFlash) to
|
||||
* help set up the device and its appropriate default partitioning.
|
||||
@@ -28,6 +30,8 @@ struct flash_platform_data {
|
||||
char *type;
|
||||
|
||||
const char **part_probe_types;
|
||||
+
|
||||
+ unsigned int max_transfer_len;
|
||||
/* we'll likely add more ... use JEDEC IDs, etc */
|
||||
};
|
||||
|
Loading…
Reference in new issue