[source] ramips: add support for switching between 3-byte and 4-byte addressing on w25q256 flash

LEDE Commits lede-commits at lists.infradead.org
Thu Nov 2 07:59:37 PDT 2017


nbd pushed a commit to source.git, branch master:
https://git.lede-project.org/22d982ea003360d86ff1fef5114fb41dfa09ce9b

commit 22d982ea003360d86ff1fef5114fb41dfa09ce9b
Author: Felix Fietkau <nbd at nbd.name>
AuthorDate: Thu Jul 20 12:57:44 2017 +0200

    ramips: add support for switching between 3-byte and 4-byte addressing on w25q256 flash
    
    On some devices the flash chip needs to be in 3-byte addressing mode during
    reboot, otherwise the boot loader will fail to start.
    This mode however does not allow regular reads/writes onto the upper 16M
    half. W25Q256 has separate read commands for reading from >16M, however
    it does not have any separate write commands.
    This patch changes the code to leave the chip in 3-byte mode most of the
    time and only switch during erase/write cycles that go to >16M
    addresses.
    
    Signed-off-by: Felix Fietkau <nbd at nbd.name>
---
 ...53-mtd-spi-nor-add-w25q256-3b-mode-switch.patch | 214 +++++++++++++++++++++
 .../0054-mtd-add-chunked-read-io-to-m25p80.patch   |  19 +-
 2 files changed, 224 insertions(+), 9 deletions(-)

diff --git a/target/linux/ramips/patches-4.9/0053-mtd-spi-nor-add-w25q256-3b-mode-switch.patch b/target/linux/ramips/patches-4.9/0053-mtd-spi-nor-add-w25q256-3b-mode-switch.patch
new file mode 100644
index 0000000..e1e2bae
--- /dev/null
+++ b/target/linux/ramips/patches-4.9/0053-mtd-spi-nor-add-w25q256-3b-mode-switch.patch
@@ -0,0 +1,214 @@
+mtd: spi-nor: add support for switching between 3-byte and 4-byte addressing on w25q256 flash
+
+On some devices the flash chip needs to be in 3-byte addressing mode during
+reboot, otherwise the boot loader will fail to start.
+This mode however does not allow regular reads/writes onto the upper 16M
+half. W25Q256 has separate read commands for reading from >16M, however
+it does not have any separate write commands.
+This patch changes the code to leave the chip in 3-byte mode most of the
+time and only switch during erase/write cycles that go to >16M
+addresses.
+
+Signed-off-by: Felix Fietkau <nbd at nbd.name>
+---
+--- a/drivers/mtd/spi-nor/spi-nor.c
++++ b/drivers/mtd/spi-nor/spi-nor.c
+@@ -85,6 +85,10 @@ struct flash_info {
+ 					 * Use dedicated 4byte address op codes
+ 					 * to support memory size above 128Mib.
+ 					 */
++#define SPI_NOR_4B_READ_OP	BIT(12)	/*
++					 * Like SPI_NOR_4B_OPCODES, but for read
++					 * op code only.
++					 */
+ };
+ 
+ #define JEDEC_MFR(info)	((info)->id[0])
+@@ -250,6 +254,15 @@ static inline u8 spi_nor_convert_3to4_er
+ 				      ARRAY_SIZE(spi_nor_3to4_erase));
+ }
+ 
++static void spi_nor_set_4byte_read(struct spi_nor *nor,
++				   const struct flash_info *info)
++{
++	nor->addr_width = 3;
++	nor->ext_addr = 0;
++	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
++	nor->flags |= SNOR_F_4B_EXT_ADDR;
++}
++
+ static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
+ 				      const struct flash_info *info)
+ {
+@@ -467,6 +480,36 @@ static int spi_nor_erase_sector(struct s
+ 	return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
+ }
+ 
++static int spi_nor_check_ext_addr(struct spi_nor *nor, u32 addr)
++{
++	bool ext_addr;
++	int ret;
++	u8 cmd;
++
++	if (!(nor->flags & SNOR_F_4B_EXT_ADDR))
++		return 0;
++
++	ext_addr = !!(addr & 0xff000000);
++	if (nor->ext_addr == ext_addr)
++		return 0;
++
++	cmd = ext_addr ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
++	write_enable(nor);
++	ret = nor->write_reg(nor, cmd, NULL, 0);
++	if (ret)
++		return ret;
++
++	cmd = 0;
++	ret = nor->write_reg(nor, SPINOR_OP_WREAR, &cmd, 1);
++	if (ret)
++		return ret;
++
++	nor->addr_width = 3 + ext_addr;
++	nor->ext_addr = ext_addr;
++	write_disable(nor);
++	return 0;
++}
++
+ /*
+  * Erase an address range on the nor chip.  The address range may extend
+  * one or more erase sectors.  Return an error is there is a problem erasing.
+@@ -492,6 +535,10 @@ static int spi_nor_erase(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
++	ret = spi_nor_check_ext_addr(nor, addr + len);
++	if (ret)
++		return ret;
++
+ 	/* whole-chip erase? */
+ 	if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
+ 		unsigned long timeout;
+@@ -542,6 +589,7 @@ static int spi_nor_erase(struct mtd_info
+ 	write_disable(nor);
+ 
+ erase_err:
++	spi_nor_check_ext_addr(nor, 0);
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
+ 
+ 	instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
+@@ -834,7 +882,9 @@ static int spi_nor_lock(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
++	spi_nor_check_ext_addr(nor, ofs + len);
+ 	ret = nor->flash_lock(nor, ofs, len);
++	spi_nor_check_ext_addr(nor, 0);
+ 
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
+ 	return ret;
+@@ -849,7 +899,9 @@ static int spi_nor_unlock(struct mtd_inf
+ 	if (ret)
+ 		return ret;
+ 
++	spi_nor_check_ext_addr(nor, ofs + len);
+ 	ret = nor->flash_unlock(nor, ofs, len);
++	spi_nor_check_ext_addr(nor, 0);
+ 
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
+ 	return ret;
+@@ -1151,7 +1203,7 @@ static const struct flash_info spi_nor_i
+ 	{ "w25q80", INFO(0xef5014, 0, 64 * 1024,  16, SECT_4K) },
+ 	{ "w25q80bl", INFO(0xef4014, 0, 64 * 1024,  16, SECT_4K) },
+ 	{ "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
+-	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K) },
++	{ "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_READ_OP) },
+ 
+ 	/* Catalyst / On Semiconductor -- non-JEDEC */
+ 	{ "cat25c11", CAT25_INFO(  16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
+@@ -1205,6 +1257,9 @@ static int spi_nor_read(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
++	if (nor->flags & SNOR_F_4B_EXT_ADDR)
++		nor->addr_width = 4;
++
+ 	while (len) {
+ 		loff_t addr = from;
+ 
+@@ -1229,6 +1284,18 @@ static int spi_nor_read(struct mtd_info
+ 	ret = 0;
+ 
+ read_err:
++	if (nor->flags & SNOR_F_4B_EXT_ADDR) {
++		u8 val = 0;
++
++		if ((from + len) & 0xff000000) {
++			write_enable(nor);
++			nor->write_reg(nor, SPINOR_OP_WREAR, &val, 1);
++			write_disable(nor);
++		}
++
++		nor->addr_width = 3;
++	}
++
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
+ 	return ret;
+ }
+@@ -1330,6 +1397,10 @@ static int spi_nor_write(struct mtd_info
+ 	if (ret)
+ 		return ret;
+ 
++	ret = spi_nor_check_ext_addr(nor, to + len);
++	if (ret < 0)
++		return ret;
++
+ 	for (i = 0; i < len; ) {
+ 		ssize_t written;
+ 		loff_t addr = to + i;
+@@ -1377,6 +1448,7 @@ static int spi_nor_write(struct mtd_info
+ 	}
+ 
+ write_err:
++	spi_nor_check_ext_addr(nor, 0);
+ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
+ 	return ret;
+ }
+@@ -1715,8 +1787,10 @@ int spi_nor_scan(struct spi_nor *nor, co
+ 	else if (mtd->size > 0x1000000) {
+ 		/* enable 4-byte addressing if the device exceeds 16MiB */
+ 		nor->addr_width = 4;
+-		if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
+-		    info->flags & SPI_NOR_4B_OPCODES)
++		if (info->flags & SPI_NOR_4B_READ_OP)
++			spi_nor_set_4byte_read(nor, info);
++		else if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
++			 info->flags & SPI_NOR_4B_OPCODES)
+ 			spi_nor_set_4byte_opcodes(nor, info);
+ 		else
+ 			set_4byte(nor, info, 1);
+--- a/include/linux/mtd/spi-nor.h
++++ b/include/linux/mtd/spi-nor.h
+@@ -90,6 +90,7 @@
+ /* Used for Macronix and Winbond flashes. */
+ #define SPINOR_OP_EN4B		0xb7	/* Enter 4-byte mode */
+ #define SPINOR_OP_EX4B		0xe9	/* Exit 4-byte mode */
++#define SPINOR_OP_WREAR		0xc5	/* Write extended address register */
+ 
+ /* Used for Spansion flashes only. */
+ #define SPINOR_OP_BRWR		0x17	/* Bank register write */
+@@ -141,6 +142,7 @@ enum spi_nor_option_flags {
+ 	SNOR_F_NO_OP_CHIP_ERASE	= BIT(2),
+ 	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
+ 	SNOR_F_READY_XSR_RDY	= BIT(4),
++	SNOR_F_4B_EXT_ADDR	= BIT(5),
+ };
+ 
+ /**
+@@ -188,6 +190,7 @@ struct spi_nor {
+ 	enum read_mode		flash_read;
+ 	bool			sst_write_second;
+ 	u32			flags;
++	u8			ext_addr;
+ 	u8			cmd_buf[SPI_NOR_MAX_CMD_SIZE];
+ 
+ 	int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);
diff --git a/target/linux/ramips/patches-4.9/0054-mtd-add-chunked-read-io-to-m25p80.patch b/target/linux/ramips/patches-4.9/0054-mtd-add-chunked-read-io-to-m25p80.patch
index 40994fb..7f88ec5 100644
--- a/target/linux/ramips/patches-4.9/0054-mtd-add-chunked-read-io-to-m25p80.patch
+++ b/target/linux/ramips/patches-4.9/0054-mtd-add-chunked-read-io-to-m25p80.patch
@@ -1,6 +1,6 @@
 --- a/drivers/mtd/spi-nor/spi-nor.c
 +++ b/drivers/mtd/spi-nor/spi-nor.c
-@@ -1381,6 +1381,66 @@ write_err:
+@@ -1453,6 +1453,67 @@ write_err:
  	return ret;
  }
  
@@ -8,6 +8,7 @@
 +				 size_t *_retlen, const u_char *_buf)
 +{
 +	struct spi_nor *nor = mtd_to_spi_nor(mtd);
++	u32 addr_width = nor->addr_width + !!(nor->flags & SNOR_F_4B_EXT_ADDR);
 +	int chunk_size;
 +	int retlen = 0;
 +	int ret;
@@ -16,8 +17,8 @@
 +	if (!chunk_size)
 +		chunk_size = _len;
 +
-+	if (nor->addr_width > 3)
-+		chunk_size -= nor->addr_width - 3;
++	if (addr_width > 3)
++		chunk_size -= addr_width - 3;
 +
 +	while (retlen < _len) {
 +		size_t len = min_t(int, chunk_size, _len - retlen);
@@ -67,7 +68,7 @@
  static int macronix_quad_enable(struct spi_nor *nor)
  {
  	int ret, val;
-@@ -1630,10 +1690,12 @@ int spi_nor_scan(struct spi_nor *nor, co
+@@ -1702,10 +1763,12 @@ int spi_nor_scan(struct spi_nor *nor, co
  	}
  
  	/* sst nor chips use AAI word program */
@@ -82,7 +83,7 @@
  
  	if (info->flags & USE_FSR)
  		nor->flags |= SNOR_F_USE_FSR;
-@@ -1663,11 +1725,20 @@ int spi_nor_scan(struct spi_nor *nor, co
+@@ -1735,11 +1798,20 @@ int spi_nor_scan(struct spi_nor *nor, co
  	mtd->writebufsize = nor->page_size;
  
  	if (np) {
@@ -105,15 +106,15 @@
  		nor->flash_read = SPI_NOR_FAST;
 --- a/include/linux/mtd/spi-nor.h
 +++ b/include/linux/mtd/spi-nor.h
-@@ -141,6 +141,7 @@ enum spi_nor_option_flags {
- 	SNOR_F_NO_OP_CHIP_ERASE	= BIT(2),
+@@ -143,6 +143,7 @@ enum spi_nor_option_flags {
  	SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
  	SNOR_F_READY_XSR_RDY	= BIT(4),
-+	SNOR_F_SST		= BIT(5),
+ 	SNOR_F_4B_EXT_ADDR	= BIT(5),
++	SNOR_F_SST		= BIT(6),
  };
  
  /**
-@@ -180,6 +181,7 @@ struct spi_nor {
+@@ -182,6 +183,7 @@ struct spi_nor {
  	struct mutex		lock;
  	struct device		*dev;
  	u32			page_size;



More information about the lede-commits mailing list