1 mtd: spi-nor: add support for switching between 3-byte and 4-byte addressing on w25q256 flash
3 On some devices the flash chip needs to be in 3-byte addressing mode during
4 reboot, otherwise the boot loader will fail to start.
5 This mode however does not allow regular reads/writes onto the upper 16M
6 half. W25Q256 has separate read commands for reading from >16M, however
7 it does not have any separate write commands.
8 This patch changes the code to leave the chip in 3-byte mode most of the
9 time and only switch during erase/write cycles that go to >16M
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
14 --- a/drivers/mtd/spi-nor/spi-nor.c
15 +++ b/drivers/mtd/spi-nor/spi-nor.c
16 @@ -89,6 +89,10 @@ struct flash_info {
17 #define NO_CHIP_ERASE BIT(12) /* Chip does not support chip erase */
18 #define SPI_NOR_SKIP_SFDP BIT(13) /* Skip parsing of SFDP tables */
19 #define USE_CLSR BIT(14) /* use CLSR command */
20 +#define SPI_NOR_4B_READ_OP BIT(15) /*
21 + * Like SPI_NOR_4B_OPCODES, but for read
26 #define JEDEC_MFR(info) ((info)->id[0])
27 @@ -240,6 +244,15 @@ static inline u8 spi_nor_convert_3to4_er
28 ARRAY_SIZE(spi_nor_3to4_erase));
31 +static void spi_nor_set_4byte_read(struct spi_nor *nor,
32 + const struct flash_info *info)
34 + nor->addr_width = 3;
36 + nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
37 + nor->flags |= SNOR_F_4B_EXT_ADDR;
40 static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
41 const struct flash_info *info)
43 @@ -467,6 +480,36 @@ static int spi_nor_erase_sector(struct s
44 return nor->write_reg(nor, nor->erase_opcode, buf, nor->addr_width);
47 +static int spi_nor_check_ext_addr(struct spi_nor *nor, u32 addr)
53 + if (!(nor->flags & SNOR_F_4B_EXT_ADDR))
56 + ext_addr = !!(addr & 0xff000000);
57 + if (nor->ext_addr == ext_addr)
60 + cmd = ext_addr ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
62 + ret = nor->write_reg(nor, cmd, NULL, 0);
67 + ret = nor->write_reg(nor, SPINOR_OP_WREAR, &cmd, 1);
71 + nor->addr_width = 3 + ext_addr;
72 + nor->ext_addr = ext_addr;
78 * Erase an address range on the nor chip. The address range may extend
79 * one or more erase sectors. Return an error is there is a problem erasing.
80 @@ -492,6 +535,10 @@ static int spi_nor_erase(struct mtd_info
84 + ret = spi_nor_check_ext_addr(nor, addr + len);
88 /* whole-chip erase? */
89 if (len == mtd->size && !(nor->flags & SNOR_F_NO_OP_CHIP_ERASE)) {
90 unsigned long timeout;
91 @@ -542,6 +589,7 @@ static int spi_nor_erase(struct mtd_info
95 + spi_nor_check_ext_addr(nor, 0);
96 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
98 instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
99 @@ -834,7 +882,9 @@ static int spi_nor_lock(struct mtd_info
103 + spi_nor_check_ext_addr(nor, ofs + len);
104 ret = nor->flash_lock(nor, ofs, len);
105 + spi_nor_check_ext_addr(nor, 0);
107 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
109 @@ -849,7 +899,9 @@ static int spi_nor_unlock(struct mtd_inf
113 + spi_nor_check_ext_addr(nor, ofs + len);
114 ret = nor->flash_unlock(nor, ofs, len);
115 + spi_nor_check_ext_addr(nor, 0);
117 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
119 @@ -1177,7 +1229,7 @@ static const struct flash_info spi_nor_i
120 { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16, SECT_4K) },
121 { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16, SECT_4K) },
122 { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256, SECT_4K) },
123 - { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ) },
124 + { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ | SPI_NOR_4B_READ_OP) },
125 { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024,
126 SECT_4K | SPI_NOR_QUAD_READ | SPI_NOR_DUAL_READ) },
128 @@ -1237,6 +1289,9 @@ static int spi_nor_read(struct mtd_info
132 + if (nor->flags & SNOR_F_4B_EXT_ADDR)
133 + nor->addr_width = 4;
138 @@ -1261,6 +1316,18 @@ static int spi_nor_read(struct mtd_info
142 + if (nor->flags & SNOR_F_4B_EXT_ADDR) {
145 + if ((from + len) & 0xff000000) {
147 + nor->write_reg(nor, SPINOR_OP_WREAR, &val, 1);
148 + write_disable(nor);
151 + nor->addr_width = 3;
154 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
157 @@ -1362,6 +1429,10 @@ static int spi_nor_write(struct mtd_info
161 + ret = spi_nor_check_ext_addr(nor, to + len);
165 for (i = 0; i < len; ) {
167 loff_t addr = to + i;
168 @@ -1402,6 +1473,7 @@ static int spi_nor_write(struct mtd_info
172 + spi_nor_check_ext_addr(nor, 0);
173 spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
176 @@ -2818,8 +2890,10 @@ int spi_nor_scan(struct spi_nor *nor, co
177 } else if (mtd->size > 0x1000000) {
178 /* enable 4-byte addressing if the device exceeds 16MiB */
180 - if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
181 - info->flags & SPI_NOR_4B_OPCODES)
182 + if (info->flags & SPI_NOR_4B_READ_OP)
183 + spi_nor_set_4byte_read(nor, info);
184 + else if (JEDEC_MFR(info) == SNOR_MFR_SPANSION ||
185 + info->flags & SPI_NOR_4B_OPCODES)
186 spi_nor_set_4byte_opcodes(nor, info);
188 set_4byte(nor, info, 1);
189 --- a/include/linux/mtd/spi-nor.h
190 +++ b/include/linux/mtd/spi-nor.h
192 /* Used for Macronix and Winbond flashes. */
193 #define SPINOR_OP_EN4B 0xb7 /* Enter 4-byte mode */
194 #define SPINOR_OP_EX4B 0xe9 /* Exit 4-byte mode */
195 +#define SPINOR_OP_WREAR 0xc5 /* Write extended address register */
197 /* Used for Spansion flashes only. */
198 #define SPINOR_OP_BRWR 0x17 /* Bank register write */
199 @@ -229,6 +230,7 @@ enum spi_nor_option_flags {
200 SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
201 SNOR_F_READY_XSR_RDY = BIT(4),
202 SNOR_F_USE_CLSR = BIT(5),
203 + SNOR_F_4B_EXT_ADDR = BIT(6),
207 @@ -280,6 +282,7 @@ struct spi_nor {
208 enum spi_nor_protocol reg_proto;
209 bool sst_write_second;
212 u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
214 int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);