brcm63xx: backport upstream solution for SPI message size limits
Backport upstream solution for working around SPI controller maximum message sizes. Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
This commit is contained in:
		@@ -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];
 | 
			
		||||
@@ -11,7 +11,7 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
 | 
			
		||||
--- a/drivers/mtd/devices/m25p80.c
 | 
			
		||||
+++ b/drivers/mtd/devices/m25p80.c
 | 
			
		||||
@@ -248,8 +248,10 @@ static int m25p_probe(struct spi_device
 | 
			
		||||
@@ -260,8 +260,10 @@ static int m25p_probe(struct spi_device
 | 
			
		||||
 	if (ret)
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
@@ -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 */
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
@@ -95,7 +95,7 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
 		if (val & STRAPBUS_63268_BOOT_SEL_SERIAL)
 | 
			
		||||
 			return BCM63XX_FLASH_TYPE_SERIAL;
 | 
			
		||||
 		else
 | 
			
		||||
@@ -195,8 +232,17 @@ int __init bcm63xx_flash_register(void)
 | 
			
		||||
@@ -195,8 +232,14 @@ int __init bcm63xx_flash_register(void)
 | 
			
		||||
 
 | 
			
		||||
 		return platform_device_register(&mtd_dev);
 | 
			
		||||
 	case BCM63XX_FLASH_TYPE_SERIAL:
 | 
			
		||||
@@ -107,9 +107,6 @@ Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
 | 
			
		||||
+			bcm63xx_spi_flash_info[0].mode = SPI_RX_DUAL;
 | 
			
		||||
+		}
 | 
			
		||||
+
 | 
			
		||||
+		if (BCMCPU_IS_6358() || BCMCPU_IS_6368())
 | 
			
		||||
+			bcm63xx_flash_data.max_transfer_len = SPI_6358_MSG_DATA_SIZE;
 | 
			
		||||
+
 | 
			
		||||
+		return spi_register_board_info(bcm63xx_spi_flash_info,
 | 
			
		||||
+					ARRAY_SIZE(bcm63xx_spi_flash_info));
 | 
			
		||||
 	case BCM63XX_FLASH_TYPE_NAND:
 | 
			
		||||
 
 | 
			
		||||
@@ -10,7 +10,7 @@ Subject: [PATCH 64/79] MTD: m25p80: allow passing pp_data
 | 
			
		||||
 | 
			
		||||
--- a/drivers/mtd/devices/m25p80.c
 | 
			
		||||
+++ b/drivers/mtd/devices/m25p80.c
 | 
			
		||||
@@ -276,7 +276,8 @@ static int m25p_probe(struct spi_device
 | 
			
		||||
@@ -261,7 +261,8 @@ static int m25p_probe(struct spi_device
 | 
			
		||||
 		return ret;
 | 
			
		||||
 
 | 
			
		||||
 	return mtd_device_parse_register(&nor->mtd,
 | 
			
		||||
@@ -28,13 +28,13 @@ Subject: [PATCH 64/79] MTD: m25p80: allow passing pp_data
 | 
			
		||||
  *	partitioning
 | 
			
		||||
+ * @pp_data: optional partition parser data.
 | 
			
		||||
  *
 | 
			
		||||
  * @max_transfer_len: option maximum read/write length limitation for
 | 
			
		||||
  *	SPI controllers not able to transfer any length commands.
 | 
			
		||||
@@ -30,6 +31,7 @@ struct flash_platform_data {
 | 
			
		||||
  * Board init code (in arch/.../mach-xxx/board-yyy.c files) can
 | 
			
		||||
  * provide information about SPI flash parts (such as DataFlash) to
 | 
			
		||||
@@ -28,6 +29,7 @@ struct flash_platform_data {
 | 
			
		||||
 	char		*type;
 | 
			
		||||
 
 | 
			
		||||
 	const char	**part_probe_types;
 | 
			
		||||
+	struct mtd_part_parser_data *pp_data;
 | 
			
		||||
 
 | 
			
		||||
 	unsigned int	max_transfer_len;
 | 
			
		||||
 	/* we'll likely add more ... use JEDEC IDs, etc */
 | 
			
		||||
 };
 | 
			
		||||
 
 | 
			
		||||
 
 | 
			
		||||
@@ -11,7 +11,7 @@ Signed-off-by: Jonas Gorski <jogo@openwrt.org>
 | 
			
		||||
 | 
			
		||||
--- a/arch/mips/bcm63xx/dev-flash.c
 | 
			
		||||
+++ b/arch/mips/bcm63xx/dev-flash.c
 | 
			
		||||
@@ -252,3 +252,8 @@ int __init bcm63xx_flash_register(void)
 | 
			
		||||
@@ -249,3 +249,8 @@ int __init bcm63xx_flash_register(void)
 | 
			
		||||
 		return -ENODEV;
 | 
			
		||||
 	}
 | 
			
		||||
 }
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user