Browse Source

ramips-mt7621: Add support for ZBT-WG3526-32M (#1296)

including backport of mtd patches.

The general flash issue (32MB) was discussed here:
https://gist.github.com/dmke/d389bc364b3f73f525076eaee0019dc1
The resulting patch was the following:
http://lists.infradead.org/pipermail/linux-mtd/2016-December/070889.html

Another patch by nbd168 added the required mtd read operations for supporting ZBT-WG3526-32M:
https://github.com/lede-project/source/commit/22d982e#diff-3835f027e16e6416090c1833bc732af3
Vincent Wiemann 6 years ago
parent
commit
273a60fe62

+ 456 - 0
patches/lede/0064-ramips-mtd-spi-nor-add-support-for-switching-between-3-byte-and-4-byte-addressing-on-w25q256-flash.patch

@@ -0,0 +1,456 @@
+From: Vincent Wiemann <me@bibbl.com>
+Date: Sat, 6 Jan 2018 04:33:09 +0100
+Subject: ramips: mtd: spi-nor: add support for switching between 3-byte and 4-byte addressing on w25q256 flash
+
+CAUTION! NEEDS TESTING!
+Tries to backport switching between 3-byte and 4-byte addressing on w25q256 flash from Lede master (62ede4f78389c313a8004e79330a7d055eda2f7d) by Felix Fietkau.
+
+Applied patches:
+mtd: spi-nor: rename SPINOR_OP_* macros of the 4-byte address op codes
+mtd: spi-nor: add a stateless method to support memory size above 128Mib
+mtd: spi-nor: add w25q256 3b-mode-switch
+mtd: add chunked read-io to m25p80
+
+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: Vincent Wiemann (me@bibbl.com)
+
+diff --git a/target/linux/ramips/patches-4.4/400-mtd-spi-nor-add-w25q256-3b-mode-switch.patch b/target/linux/ramips/patches-4.4/400-mtd-spi-nor-add-w25q256-3b-mode-switch.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..5399f547e4edd0eb1cfee8c1ec5b35e69c692ee9
+--- /dev/null
++++ b/target/linux/ramips/patches-4.4/400-mtd-spi-nor-add-w25q256-3b-mode-switch.patch
+@@ -0,0 +1,399 @@
++--- a/drivers/mtd/devices/serial_flash_cmds.h
+++++ b/drivers/mtd/devices/serial_flash_cmds.h
++@@ -18,19 +18,12 @@
++ #define SPINOR_OP_RDVCR		0x85
++ 
++ /* JEDEC Standard - Serial Flash Discoverable Parmeters (SFDP) Commands */
++-#define SPINOR_OP_READ_1_2_2	0xbb	/* DUAL I/O READ */
++-#define SPINOR_OP_READ_1_4_4	0xeb	/* QUAD I/O READ */
++-
++ #define SPINOR_OP_WRITE		0x02	/* PAGE PROGRAM */
++ #define SPINOR_OP_WRITE_1_1_2	0xa2	/* DUAL INPUT PROGRAM */
++ #define SPINOR_OP_WRITE_1_2_2	0xd2	/* DUAL INPUT EXT PROGRAM */
++ #define SPINOR_OP_WRITE_1_1_4	0x32	/* QUAD INPUT PROGRAM */
++ #define SPINOR_OP_WRITE_1_4_4	0x12	/* QUAD INPUT EXT PROGRAM */
++ 
++-/* READ commands with 32-bit addressing */
++-#define SPINOR_OP_READ4_1_2_2	0xbc
++-#define SPINOR_OP_READ4_1_4_4	0xec
++-
++ /* Configuration flags */
++ #define FLASH_FLAG_SINGLE	0x000000ff
++ #define FLASH_FLAG_READ_WRITE	0x00000001
++--- a/drivers/mtd/devices/st_spi_fsm.c
+++++ b/drivers/mtd/devices/st_spi_fsm.c
++@@ -507,13 +507,13 @@ static struct seq_rw_config n25q_read3_c
++  *	- 'FAST' variants configured for 8 dummy cycles (see note above.)
++  */
++ static struct seq_rw_config n25q_read4_configs[] = {
++-	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ4_1_4_4,	0, 4, 4, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ4_1_1_4,	0, 1, 4, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ4_1_2_2,	0, 2, 2, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ4_1_1_2,	0, 1, 2, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ4_FAST,	0, 1, 1, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ4,	0, 1, 1, 0x00, 0, 0},
++-	{0x00,			0,			0, 0, 0, 0x00, 0, 0},
+++	{FLASH_FLAG_READ_1_4_4, SPINOR_OP_READ_1_4_4_4B, 0, 4, 4, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_1_1_4, SPINOR_OP_READ_1_1_4_4B, 0, 1, 4, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_1_2_2, SPINOR_OP_READ_1_2_2_4B, 0, 2, 2, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_1_1_2, SPINOR_OP_READ_1_1_2_4B, 0, 1, 2, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_FAST,	SPINOR_OP_READ_FAST_4B,  0, 1, 1, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_WRITE, SPINOR_OP_READ_4B,       0, 1, 1, 0x00, 0, 0},
+++	{0x00,			0,                       0, 0, 0, 0x00, 0, 0},
++ };
++ 
++ /*
++@@ -553,13 +553,13 @@ static int stfsm_mx25_en_32bit_addr_seq(
++  * entering a state that is incompatible with the SPIBoot Controller.
++  */
++ static struct seq_rw_config stfsm_s25fl_read4_configs[] = {
++-	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ4_1_4_4,  0, 4, 4, 0x00, 2, 4},
++-	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ4_1_1_4,  0, 1, 4, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ4_1_2_2,  0, 2, 2, 0x00, 4, 0},
++-	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ4_1_1_2,  0, 1, 2, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ4_FAST,   0, 1, 1, 0x00, 0, 8},
++-	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ4,        0, 1, 1, 0x00, 0, 0},
++-	{0x00,                   0,                      0, 0, 0, 0x00, 0, 0},
+++	{FLASH_FLAG_READ_1_4_4,  SPINOR_OP_READ_1_4_4_4B,  0, 4, 4, 0x00, 2, 4},
+++	{FLASH_FLAG_READ_1_1_4,  SPINOR_OP_READ_1_1_4_4B,  0, 1, 4, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_1_2_2,  SPINOR_OP_READ_1_2_2_4B,  0, 2, 2, 0x00, 4, 0},
+++	{FLASH_FLAG_READ_1_1_2,  SPINOR_OP_READ_1_1_2_4B,  0, 1, 2, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_FAST,   SPINOR_OP_READ_FAST_4B,   0, 1, 1, 0x00, 0, 8},
+++	{FLASH_FLAG_READ_WRITE,  SPINOR_OP_READ_4B,        0, 1, 1, 0x00, 0, 0},
+++	{0x00,                   0,                        0, 0, 0, 0x00, 0, 0},
++ };
++ 
++ static struct seq_rw_config stfsm_s25fl_write4_configs[] = {
++--- a/drivers/mtd/spi-nor/spi-nor.c
+++++ b/drivers/mtd/spi-nor/spi-nor.c
++@@ -69,6 +69,14 @@ struct flash_info {
++ #define	SPI_NOR_QUAD_READ	0x40    /* Flash supports Quad Read */
++ #define	USE_FSR			0x80	/* use flag status register */
++ #define	SPI_NOR_HAS_LOCK	0x100	/* Flash supports lock/unlock via SR */
+++#define SPI_NOR_4B_OPCODES	0x200	/*
+++					 * Use dedicated 4byte address op codes
+++					 * to support memory size above 128Mib.
+++					 */
+++#define SPI_NOR_4B_READ_OP	0x400	/*
+++					 * Like SPI_NOR_4B_OPCODES, but for read
+++					 * op code only.
+++					 */
++ };
++ 
++ #define JEDEC_MFR(info)	((info)->id[0])
++@@ -182,6 +190,89 @@ static inline struct spi_nor *mtd_to_spi
++ 	return mtd->priv;
++ }
++ 
+++
+++static u8 spi_nor_convert_opcode(u8 opcode, const u8 table[][2], size_t size)
+++{
+++	size_t i;
+++
+++	for (i = 0; i < size; i++)
+++		if (table[i][0] == opcode)
+++			return table[i][1];
+++
+++	/* No conversion found, keep input op code. */
+++	return opcode;
+++}
+++
+++static inline u8 spi_nor_convert_3to4_read(u8 opcode)
+++{
+++	static const u8 spi_nor_3to4_read[][2] = {
+++		{ SPINOR_OP_READ,	SPINOR_OP_READ_4B },
+++		{ SPINOR_OP_READ_FAST,	SPINOR_OP_READ_FAST_4B },
+++		{ SPINOR_OP_READ_1_1_2,	SPINOR_OP_READ_1_1_2_4B },
+++		{ SPINOR_OP_READ_1_2_2,	SPINOR_OP_READ_1_2_2_4B },
+++		{ SPINOR_OP_READ_1_1_4,	SPINOR_OP_READ_1_1_4_4B },
+++		{ SPINOR_OP_READ_1_4_4,	SPINOR_OP_READ_1_4_4_4B },
+++	};
+++
+++	return spi_nor_convert_opcode(opcode, spi_nor_3to4_read,
+++				      ARRAY_SIZE(spi_nor_3to4_read));
+++}
+++
+++static inline u8 spi_nor_convert_3to4_program(u8 opcode)
+++{
+++	static const u8 spi_nor_3to4_program[][2] = {
+++		{ SPINOR_OP_PP,		SPINOR_OP_PP_4B },
+++		{ SPINOR_OP_PP_1_1_4,	SPINOR_OP_PP_1_1_4_4B },
+++		{ SPINOR_OP_PP_1_4_4,	SPINOR_OP_PP_1_4_4_4B },
+++	};
+++
+++	return spi_nor_convert_opcode(opcode, spi_nor_3to4_program,
+++				      ARRAY_SIZE(spi_nor_3to4_program));
+++}
+++
+++static inline u8 spi_nor_convert_3to4_erase(u8 opcode)
+++{
+++	static const u8 spi_nor_3to4_erase[][2] = {
+++		{ SPINOR_OP_BE_4K,	SPINOR_OP_BE_4K_4B },
+++		{ SPINOR_OP_BE_32K,	SPINOR_OP_BE_32K_4B },
+++		{ SPINOR_OP_SE,		SPINOR_OP_SE_4B },
+++	};
+++
+++	return spi_nor_convert_opcode(opcode, spi_nor_3to4_erase,
+++				      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)
+++{
+++	/* Do some manufacturer fixups first */
+++	switch (JEDEC_MFR(info)) {
+++	case SNOR_MFR_SPANSION:
+++		/* No small sector erase for 4-byte command set */
+++		nor->erase_opcode = SPINOR_OP_SE;
+++		nor->mtd.erasesize = info->sector_size;
+++		break;
+++
+++	default:
+++		break;
+++	}
+++
+++	nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
+++	nor->program_opcode = spi_nor_convert_3to4_program(nor->program_opcode);
+++	nor->erase_opcode = spi_nor_convert_3to4_erase(nor->erase_opcode);
+++}
+++
++ /* Enable/disable 4-byte addressing mode. */
++ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info,
++ 			    int enable)
++@@ -313,6 +404,36 @@ static void spi_nor_unlock_and_unprep(st
++ 	mutex_unlock(&nor->lock);
++ }
++ 
+++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.
++@@ -338,6 +459,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) {
++ 		unsigned long timeout;
++@@ -396,6 +521,7 @@ static int spi_nor_erase(struct mtd_info
++ 	return ret;
++ 
++ erase_err:
+++	spi_nor_check_ext_addr(nor, 0);
++ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
++ 	instr->state = MTD_ERASE_FAILED;
++ 	return ret;
++@@ -585,7 +711,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;
++@@ -600,7 +728,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;
++@@ -851,7 +981,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) },
++@@ -898,8 +1028,23 @@ static int spi_nor_read(struct mtd_info
++ 	if (ret)
++ 		return ret;
++ 
+++	if (nor->flags & SNOR_F_4B_EXT_ADDR)
+++		nor->addr_width = 4;
+++
++ 	ret = nor->read(nor, from, len, retlen, buf);
++ 
+++	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;
++ }
++@@ -988,6 +1133,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;
+++
++ 	write_enable(nor);
++ 
++ 	page_offset = to & (nor->page_size - 1);
++@@ -1018,6 +1167,7 @@ static int spi_nor_write(struct mtd_info
++ 
++ 	ret = spi_nor_wait_till_ready(nor);
++ write_err:
+++	spi_nor_check_ext_addr(nor, 0);
++ 	spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
++ 	return ret;
++ }
++@@ -1366,27 +1516,12 @@ 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) {
++-			/* Dedicated 4-byte command set */
++-			switch (nor->flash_read) {
++-			case SPI_NOR_QUAD:
++-				nor->read_opcode = SPINOR_OP_READ4_1_1_4;
++-				break;
++-			case SPI_NOR_DUAL:
++-				nor->read_opcode = SPINOR_OP_READ4_1_1_2;
++-				break;
++-			case SPI_NOR_FAST:
++-				nor->read_opcode = SPINOR_OP_READ4_FAST;
++-				break;
++-			case SPI_NOR_NORMAL:
++-				nor->read_opcode = SPINOR_OP_READ4;
++-				break;
++-			}
++-			nor->program_opcode = SPINOR_OP_PP_4B;
++-			/* No small sector erase for 4-byte command set */
++-			nor->erase_opcode = SPINOR_OP_SE_4B;
++-			mtd->erasesize = info->sector_size;
++-		} else
+++		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);
++ 	} else {
++ 		nor->addr_width = 3;
++--- a/include/linux/mtd/spi-nor.h
+++++ b/include/linux/mtd/spi-nor.h
++@@ -42,9 +42,13 @@
++ #define SPINOR_OP_WRSR		0x01	/* Write status register 1 byte */
++ #define SPINOR_OP_READ		0x03	/* Read data bytes (low frequency) */
++ #define SPINOR_OP_READ_FAST	0x0b	/* Read data bytes (high frequency) */
++-#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual SPI) */
++-#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad SPI) */
+++#define SPINOR_OP_READ_1_1_2	0x3b	/* Read data bytes (Dual Output SPI) */
+++#define SPINOR_OP_READ_1_2_2	0xbb	/* Read data bytes (Dual I/O SPI) */
+++#define SPINOR_OP_READ_1_1_4	0x6b	/* Read data bytes (Quad Output SPI) */
+++#define SPINOR_OP_READ_1_4_4	0xeb	/* Read data bytes (Quad I/O SPI) */
++ #define SPINOR_OP_PP		0x02	/* Page program (up to 256 bytes) */
+++#define SPINOR_OP_PP_1_1_4	0x32	/* Quad page program */
+++#define SPINOR_OP_PP_1_4_4	0x38	/* Quad page program */
++ #define SPINOR_OP_BE_4K		0x20	/* Erase 4KiB block */
++ #define SPINOR_OP_BE_4K_PMC	0xd7	/* Erase 4KiB block on PMC chips */
++ #define SPINOR_OP_BE_32K	0x52	/* Erase 32KiB block */
++@@ -55,11 +59,17 @@
++ #define SPINOR_OP_RDFSR		0x70	/* Read flag status register */
++ 
++ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */
++-#define SPINOR_OP_READ4		0x13	/* Read data bytes (low frequency) */
++-#define SPINOR_OP_READ4_FAST	0x0c	/* Read data bytes (high frequency) */
++-#define SPINOR_OP_READ4_1_1_2	0x3c	/* Read data bytes (Dual SPI) */
++-#define SPINOR_OP_READ4_1_1_4	0x6c	/* Read data bytes (Quad SPI) */
+++#define SPINOR_OP_READ_4B	0x13	/* Read data bytes (low frequency) */
+++#define SPINOR_OP_READ_FAST_4B	0x0c	/* Read data bytes (high frequency) */
+++#define SPINOR_OP_READ_1_1_2_4B	0x3c	/* Read data bytes (Dual Output SPI) */
+++#define SPINOR_OP_READ_1_2_2_4B	0xbc	/* Read data bytes (Dual I/O SPI) */
+++#define SPINOR_OP_READ_1_1_4_4B	0x6c	/* Read data bytes (Quad Output SPI) */
+++#define SPINOR_OP_READ_1_4_4_4B	0xec	/* Read data bytes (Quad I/O SPI) */
++ #define SPINOR_OP_PP_4B		0x12	/* Page program (up to 256 bytes) */
+++#define SPINOR_OP_PP_1_1_4_4B	0x34	/* Quad page program */
+++#define SPINOR_OP_PP_1_4_4_4B	0x3e	/* Quad page program */
+++#define SPINOR_OP_BE_4K_4B	0x21	/* Erase 4KiB block */
+++#define SPINOR_OP_BE_32K_4B	0x5c	/* Erase 32KiB block */
++ #define SPINOR_OP_SE_4B		0xdc	/* Sector erase (usually 64KiB) */
++ 
++ /* Used for SST flashes only. */
++@@ -70,6 +80,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 */
++@@ -117,6 +128,7 @@ enum spi_nor_ops {
++ enum spi_nor_option_flags {
++ 	SNOR_F_USE_FSR		= BIT(0),
++ 	SNOR_F_SST		= BIT(1),
+++	SNOR_F_4B_EXT_ADDR	= BIT(5),
++ };
++ 
++ /**
++@@ -166,6 +178,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.4/401-mtd-add-chunked-read-io-to-m25p80.patch b/target/linux/ramips/patches-4.4/401-mtd-add-chunked-read-io-to-m25p80.patch
+new file mode 100644
+index 0000000000000000000000000000000000000000..b6111abad7063951c07688c66854fa743daeb6c6
+--- /dev/null
++++ b/target/linux/ramips/patches-4.4/401-mtd-add-chunked-read-io-to-m25p80.patch
+@@ -0,0 +1,21 @@
++--- a/drivers/mtd/spi-nor/spi-nor.c
+++++ b/drivers/mtd/spi-nor/spi-nor.c
++@@ -1176,6 +1176,7 @@ static int spi_nor_chunked_write(struct
++ 				 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;
++@@ -1184,8 +1185,8 @@ static int spi_nor_chunked_write(struct
++ 	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);

+ 99 - 0
patches/lede/0065-ramips-prepare-ZBT-WG3526-for-hardware-variants.patch

@@ -0,0 +1,99 @@
+From: Mathias Kresin <dev@kresin.me>
+Date: Fri, 7 Apr 2017 23:52:27 +0200
+Subject: ramips: prepare ZBT-WG3526 for hardware variants
+
+The ZBT-WG3526 is available with 16 or 32 MByte of flash. Rename the
+current supported 16MByte version to indicate which flash size variant
+is supported.
+
+Signed-off-by: Mathias Kresin <dev@kresin.me>
+
+Conflicts (Resolved):
+	target/linux/ramips/image/mt7621.mk
+
+Signed-off-by: Vincent Wiemann <me@bibbl.com>
+
+further adjustments for backport to lede-17.01 branch
+
+Signed-off-by: Andreas Ziegler <github@andreas-ziegler.de>
+
+diff --git a/target/linux/ramips/base-files/lib/ramips.sh b/target/linux/ramips/base-files/lib/ramips.sh
+index b1091f66b2897a19bd01d4a26a1b3647656fa62b..8e27a79fc1744d6bed9db8d930207c418899cab1 100755
+--- a/target/linux/ramips/base-files/lib/ramips.sh
++++ b/target/linux/ramips/base-files/lib/ramips.sh
+@@ -637,8 +637,8 @@ ramips_board_detect() {
+ 	*"ZBT-WG2626")
+ 		name="zbt-wg2626"
+ 		;;
+-	*"ZBT-WG3526")
+-		name="zbt-wg3526"
++	*"ZBT-WG3526 (16M)")
++		name="zbt-wg3526-16M"
+ 		;;
+ 	*"ZBT-WR8305RT")
+ 		name="zbt-wr8305rt"
+diff --git a/target/linux/ramips/dts/ZBT-WG3526-16M.dts b/target/linux/ramips/dts/ZBT-WG3526-16M.dts
+new file mode 100644
+index 0000000000000000000000000000000000000000..fb644502071d6641194c66baf552035937f35669
+--- /dev/null
++++ b/target/linux/ramips/dts/ZBT-WG3526-16M.dts
+@@ -0,0 +1,15 @@
++/dts-v1/;
++
++#include "ZBT-WG3526.dtsi"
++
++/ {
++	model = "ZBT-WG3526 (16M)";
++};
++
++&firmware {
++	reg = <0x50000 0xfb0000>;
++};
++
++&status_led {
++	label = "zbt-wg3526:green:status";
++};
+diff --git a/target/linux/ramips/dts/ZBT-WG3526.dts b/target/linux/ramips/dts/ZBT-WG3526.dts
+deleted file mode 100644
+index c361bdd827bffdc24405b1a1b191403ebeec46d0..0000000000000000000000000000000000000000
+--- a/target/linux/ramips/dts/ZBT-WG3526.dts
++++ /dev/null
+@@ -1,15 +0,0 @@
+-/dts-v1/;
+-
+-#include "ZBT-WG3526.dtsi"
+-
+-/ {
+-	model = "ZBT-WG3526";
+-};
+-
+-&firmware {
+-	reg = <0x50000 0xfb0000>;
+-};
+-
+-&status_led {
+-	label = "zbt-wg3526:green:status";
+-};
+diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk
+index 8218bea84410536151a216cd2cc0300369ad977a..cabc86b28eb040f0d1e58d917e6edc4a97e80db7 100644
+--- a/target/linux/ramips/image/mt7621.mk
++++ b/target/linux/ramips/image/mt7621.mk
+@@ -226,13 +226,14 @@ define Device/zbt-wg2626
+ endef
+ TARGET_DEVICES += zbt-wg2626
+ 
+-define Device/zbt-wg3526
+-  DTS := ZBT-WG3526
++define Device/zbt-wg3526-16M
++  DTS := ZBT-WG3526-16M
+   IMAGE_SIZE := $(ralink_default_fw_size_16M)
+-  DEVICE_TITLE := ZBT WG3526
++  SUPPORTED_DEVICES += zbt-wg3526
++  DEVICE_TITLE := ZBT WG3526 (16MB flash)
+   DEVICE_PACKAGES := kmod-usb3 kmod-usb-ledtrig-usbport kmod-ata-core kmod-ata-ahci
+ endef
+-TARGET_DEVICES += zbt-wg3526
++TARGET_DEVICES += zbt-wg3526-16M
+ 
+ # FIXME: is this still needed?
+ define Image/Prepare

+ 193 - 0
patches/lede/0066-ramips-rename-Digineo-AC1200-Pro-to-ZBT-WG3526-32MB.patch

@@ -0,0 +1,193 @@
+From: Mathias Kresin <dev@kresin.me>
+Date: Sat, 1 Apr 2017 10:46:44 +0200
+Subject: ramips: rename Digineo AC1200 Pro to ZBT-WG3526 32MB
+
+The Digineo AC1200 Pro is the 32MB flash variant of the ZBT-WG3526 with
+unpopulated/exposed sdhci slot. Rename to board to the OEM/ODM name and
+add the sdhci kernel module to use it for multiple clones.
+
+Signed-off-by: Mathias Kresin <dev@kresin.me>
+
+Conflicts (Resolved):
+	target/linux/ramips/base-files/etc/diag.sh
+
+Signed-off-by: Vincent Wiemann <me@bibbl.com>
+
+further adjustments for backport to lede-17.01 branch
+
+Signed-off-by: Andreas Ziegler <github@andreas-ziegler.de>
+
+diff --git a/target/linux/ramips/base-files/etc/board.d/02_network b/target/linux/ramips/base-files/etc/board.d/02_network
+index 5a4042339c6761703578e014612062ac78babd9d..b9c8fe774fedab34267a8635033b9235d9d6c3aa 100755
+--- a/target/linux/ramips/base-files/etc/board.d/02_network
++++ b/target/linux/ramips/base-files/etc/board.d/02_network
+@@ -70,7 +70,6 @@ ramips_setup_interfaces()
+ 		ucidef_set_interface_lan "eth0.2"
+ 		;;
+ 	3g-6200n|\
+-	ac1200pro|\
+ 	ai-br100|\
+ 	d240|\
+ 	db-wrt01|\
+diff --git a/target/linux/ramips/base-files/etc/diag.sh b/target/linux/ramips/base-files/etc/diag.sh
+index 9dbd6c4141d7facef2108cbb5cf3a7057522c88e..c4f521954c74635c61f42558f0d9b71f0f671c22 100644
+--- a/target/linux/ramips/base-files/etc/diag.sh
++++ b/target/linux/ramips/base-files/etc/diag.sh
+@@ -59,7 +59,10 @@ get_status_led() {
+ 	wn3000rpv3)
+ 		status_led="$board:red:power"
+ 		;;
+-	ac1200pro|\
++	ai-br100|\
++	ht-tm02)
++		status_led="$board:blue:wlan"
++		;;
+ 	all0239-3g|\
+ 	dcs-930|\
+ 	dir-300-b1|\
+@@ -85,10 +88,6 @@ get_status_led() {
+ 	zbt-wg3526)
+ 		status_led="$board:green:status"
+ 		;;
+-	ai-br100|\
+-	ht-tm02)
+-		status_led="$board:blue:wlan"
+-		;;
+ 	atp-52b|\
+ 	ip2202)
+ 		status_led="$board:green:run"
+diff --git a/target/linux/ramips/base-files/lib/ramips.sh b/target/linux/ramips/base-files/lib/ramips.sh
+index 8e27a79fc1744d6bed9db8d930207c418899cab1..b9279a5a1e9baf55ca7f59dbb22ac0b0b7b01db4 100755
+--- a/target/linux/ramips/base-files/lib/ramips.sh
++++ b/target/linux/ramips/base-files/lib/ramips.sh
+@@ -130,9 +130,6 @@ ramips_board_detect() {
+ 	*"DCS-930L B1")
+ 		name="dcs-930l-b1"
+ 		;;
+-	*"Digineo AC1200 Pro")
+-		name="ac1200pro"
+-		;;
+ 	*"DIR-300 B1")
+ 		name="dir-300-b1"
+ 		;;
+@@ -640,6 +637,9 @@ ramips_board_detect() {
+ 	*"ZBT-WG3526 (16M)")
+ 		name="zbt-wg3526-16M"
+ 		;;
++	*"ZBT-WG3526 (32M)")
++		name="zbt-wg3526-32M"
++		;;
+ 	*"ZBT-WR8305RT")
+ 		name="zbt-wr8305rt"
+ 		;;
+diff --git a/target/linux/ramips/base-files/lib/upgrade/platform.sh b/target/linux/ramips/base-files/lib/upgrade/platform.sh
+index d3efc2dd37ed855a1c0e966e448b67ae73324d5f..a45275077df8fa317c38adc8f0ab12fd4b0ecb83 100755
+--- a/target/linux/ramips/base-files/lib/upgrade/platform.sh
++++ b/target/linux/ramips/base-files/lib/upgrade/platform.sh
+@@ -17,7 +17,6 @@ platform_check_image() {
+ 	3g150b|\
+ 	3g300m|\
+ 	a5-v11|\
+-	ac1200pro|\
+ 	ai-br100|\
+ 	air3gii|\
+ 	all0239-3g|\
+diff --git a/target/linux/ramips/dts/AC1200pro.dts b/target/linux/ramips/dts/AC1200pro.dts
+deleted file mode 100644
+index fbec81844025cf2a6544dec8dfeca02fce89fdc1..0000000000000000000000000000000000000000
+--- a/target/linux/ramips/dts/AC1200pro.dts
++++ /dev/null
+@@ -1,15 +0,0 @@
+-/dts-v1/;
+-
+-#include "ZBT-WG3526.dtsi"
+-
+-/ {
+-	model = "Digineo AC1200 Pro";
+-};
+-
+-&firmware {
+-	reg = <0x50000 0x1fb0000>;
+-};
+-
+-&status_led {
+-	label = "ac1200pro:green:status";
+-};
+diff --git a/target/linux/ramips/dts/ZBT-WG3526-16M.dts b/target/linux/ramips/dts/ZBT-WG3526-16M.dts
+index fb644502071d6641194c66baf552035937f35669..1e2f1525028f69468257cd8af8d5bad0319bc551 100644
+--- a/target/linux/ramips/dts/ZBT-WG3526-16M.dts
++++ b/target/linux/ramips/dts/ZBT-WG3526-16M.dts
+@@ -9,7 +9,3 @@
+ &firmware {
+ 	reg = <0x50000 0xfb0000>;
+ };
+-
+-&status_led {
+-	label = "zbt-wg3526:green:status";
+-};
+diff --git a/target/linux/ramips/dts/ZBT-WG3526-32M.dts b/target/linux/ramips/dts/ZBT-WG3526-32M.dts
+new file mode 100644
+index 0000000000000000000000000000000000000000..99a4b45824ba4ee6c1a1c81615291c8efde1a7ac
+--- /dev/null
++++ b/target/linux/ramips/dts/ZBT-WG3526-32M.dts
+@@ -0,0 +1,11 @@
++/dts-v1/;
++
++#include "ZBT-WG3526.dtsi"
++
++/ {
++	model = "ZBT-WG3526 (32M)";
++};
++
++&firmware {
++	reg = <0x50000 0x1fb0000>;
++};
+diff --git a/target/linux/ramips/dts/ZBT-WG3526.dtsi b/target/linux/ramips/dts/ZBT-WG3526.dtsi
+index 582e42083920c465588f14e59e10eafe735640fb..5e195fb8120d3a66f19acf83c9f76bb4b1ec3e7e 100644
+--- a/target/linux/ramips/dts/ZBT-WG3526.dtsi
++++ b/target/linux/ramips/dts/ZBT-WG3526.dtsi
+@@ -35,7 +35,8 @@
+ 	gpio-leds {
+ 		compatible = "gpio-leds";
+ 
+-		status_led: status {
++		status {
++			label = "zbt-wg3526:green:status";
+ 			gpios = <&gpio0 24 1>;
+ 		};
+ 	};
+diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk
+index cabc86b28eb040f0d1e58d917e6edc4a97e80db7..aa84d78b9e42259cbee7de6c052fa7ab500a040a 100644
+--- a/target/linux/ramips/image/mt7621.mk
++++ b/target/linux/ramips/image/mt7621.mk
+@@ -34,14 +34,6 @@ define Device/11acnas
+ endef
+ TARGET_DEVICES += 11acnas
+ 
+-define Device/ac1200pro
+-  DTS := AC1200pro
+-  IMAGE_SIZE := $(ralink_default_fw_size_32M)
+-  DEVICE_TITLE := Digineo AC1200 Pro
+-  DEVICE_PACKAGES := kmod-usb3 kmod-ata-core kmod-ata-ahci
+-endef
+-TARGET_DEVICES += ac1200pro
+-
+ define Device/dir-860l-b1
+   DTS := DIR-860L-B1
+   BLOCKSIZE := 64k
+@@ -235,6 +227,15 @@ define Device/zbt-wg3526-16M
+ endef
+ TARGET_DEVICES += zbt-wg3526-16M
+ 
++define Device/zbt-wg3526-32M
++  DTS := ZBT-WG3526-32M
++  IMAGE_SIZE := $(ralink_default_fw_size_32M)
++  SUPPORTED_DEVICES += ac1200pro
++  DEVICE_TITLE := ZBT WG3526 (32MB flash)
++  DEVICE_PACKAGES := kmod-usb3 kmod-usb-ledtrig-usbport kmod-ata-core kmod-ata-ahci kmod-sdhci-mt7620
++endef
++TARGET_DEVICES += zbt-wg3526-32M
++
+ # FIXME: is this still needed?
+ define Image/Prepare
+ #define Build/Compile

+ 5 - 1
targets/ramips-mt7621

@@ -14,7 +14,11 @@ factory
 
 ## ZBT
 
-device zbt-wg3526 zbt-wg3526 # BROKEN: Untested
+device zbt-wg3526-16m zbt-wg3526-16M # BROKEN: Untested
+factory
+manifest_alias zbt-wg3526
+
+device zbt-wg3526-32m zbt-wg3526-32M # BROKEN: Untested
 factory
 
 fi