9d45ad010dbe1c8b67c37511cf3fdcd185ae6953
[oweals/openwrt.git] / target / linux / ramips / patches-4.9 / 0053-mtd-spi-nor-add-w25q256-3b-mode-switch.patch
1 mtd: spi-nor: add support for switching between 3-byte and 4-byte addressing on w25q256 flash
2
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
10 addresses.
11
12 Signed-off-by: Felix Fietkau <nbd@nbd.name>
13 ---
14 --- a/drivers/mtd/spi-nor/spi-nor.c
15 +++ b/drivers/mtd/spi-nor/spi-nor.c
16 @@ -85,6 +85,10 @@ struct flash_info {
17                                          * Use dedicated 4byte address op codes
18                                          * to support memory size above 128Mib.
19                                          */
20 +#define SPI_NOR_4B_READ_OP     BIT(12) /*
21 +                                        * Like SPI_NOR_4B_OPCODES, but for read
22 +                                        * op code only.
23 +                                        */
24  };
25  
26  #define JEDEC_MFR(info)        ((info)->id[0])
27 @@ -250,6 +254,15 @@ static inline u8 spi_nor_convert_3to4_er
28                                       ARRAY_SIZE(spi_nor_3to4_erase));
29  }
30  
31 +static void spi_nor_set_4byte_read(struct spi_nor *nor,
32 +                                  const struct flash_info *info)
33 +{
34 +       nor->addr_width = 3;
35 +       nor->ext_addr = 0;
36 +       nor->read_opcode = spi_nor_convert_3to4_read(nor->read_opcode);
37 +       nor->flags |= SNOR_F_4B_EXT_ADDR;
38 +}
39 +
40  static void spi_nor_set_4byte_opcodes(struct spi_nor *nor,
41                                       const struct flash_info *info)
42  {
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);
45  }
46  
47 +static int spi_nor_check_ext_addr(struct spi_nor *nor, u32 addr)
48 +{
49 +       bool ext_addr;
50 +       int ret;
51 +       u8 cmd;
52 +
53 +       if (!(nor->flags & SNOR_F_4B_EXT_ADDR))
54 +               return 0;
55 +
56 +       ext_addr = !!(addr & 0xff000000);
57 +       if (nor->ext_addr == ext_addr)
58 +               return 0;
59 +
60 +       cmd = ext_addr ? SPINOR_OP_EN4B : SPINOR_OP_EX4B;
61 +       write_enable(nor);
62 +       ret = nor->write_reg(nor, cmd, NULL, 0);
63 +       if (ret)
64 +               return ret;
65 +
66 +       cmd = 0;
67 +       ret = nor->write_reg(nor, SPINOR_OP_WREAR, &cmd, 1);
68 +       if (ret)
69 +               return ret;
70 +
71 +       nor->addr_width = 3 + ext_addr;
72 +       nor->ext_addr = ext_addr;
73 +       write_disable(nor);
74 +       return 0;
75 +}
76 +
77  /*
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
81         if (ret)
82                 return ret;
83  
84 +       ret = spi_nor_check_ext_addr(nor, addr + len);
85 +       if (ret)
86 +               return ret;
87 +
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
92         write_disable(nor);
93  
94  erase_err:
95 +       spi_nor_check_ext_addr(nor, 0);
96         spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_ERASE);
97  
98         instr->state = ret ? MTD_ERASE_FAILED : MTD_ERASE_DONE;
99 @@ -834,7 +882,9 @@ static int spi_nor_lock(struct mtd_info
100         if (ret)
101                 return ret;
102  
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);
106  
107         spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_UNLOCK);
108         return ret;
109 @@ -849,7 +899,9 @@ static int spi_nor_unlock(struct mtd_inf
110         if (ret)
111                 return ret;
112  
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);
116  
117         spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_LOCK);
118         return ret;
119 @@ -1151,7 +1203,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) },
124 +       { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512, SECT_4K | SPI_NOR_4B_READ_OP) },
125  
126         /* Catalyst / On Semiconductor -- non-JEDEC */
127         { "cat25c11", CAT25_INFO(  16, 8, 16, 1, SPI_NOR_NO_ERASE | SPI_NOR_NO_FR) },
128 @@ -1205,6 +1257,9 @@ static int spi_nor_read(struct mtd_info
129         if (ret)
130                 return ret;
131  
132 +       if (nor->flags & SNOR_F_4B_EXT_ADDR)
133 +               nor->addr_width = 4;
134 +
135         while (len) {
136                 loff_t addr = from;
137  
138 @@ -1229,6 +1284,18 @@ static int spi_nor_read(struct mtd_info
139         ret = 0;
140  
141  read_err:
142 +       if (nor->flags & SNOR_F_4B_EXT_ADDR) {
143 +               u8 val = 0;
144 +
145 +               if ((from + len) & 0xff000000) {
146 +                       write_enable(nor);
147 +                       nor->write_reg(nor, SPINOR_OP_WREAR, &val, 1);
148 +                       write_disable(nor);
149 +               }
150 +
151 +               nor->addr_width = 3;
152 +       }
153 +
154         spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_READ);
155         return ret;
156  }
157 @@ -1330,6 +1397,10 @@ static int spi_nor_write(struct mtd_info
158         if (ret)
159                 return ret;
160  
161 +       ret = spi_nor_check_ext_addr(nor, to + len);
162 +       if (ret < 0)
163 +               return ret;
164 +
165         for (i = 0; i < len; ) {
166                 ssize_t written;
167                 loff_t addr = to + i;
168 @@ -1377,6 +1448,7 @@ static int spi_nor_write(struct mtd_info
169         }
170  
171  write_err:
172 +       spi_nor_check_ext_addr(nor, 0);
173         spi_nor_unlock_and_unprep(nor, SPI_NOR_OPS_WRITE);
174         return ret;
175  }
176 @@ -1717,8 +1789,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 */
179                 nor->addr_width = 4;
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);
187                 else
188                         set_4byte(nor, info, 1);
189 --- a/include/linux/mtd/spi-nor.h
190 +++ b/include/linux/mtd/spi-nor.h
191 @@ -90,6 +90,7 @@
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 */
196  
197  /* Used for Spansion flashes only. */
198  #define SPINOR_OP_BRWR         0x17    /* Bank register write */
199 @@ -141,6 +142,7 @@ enum spi_nor_option_flags {
200         SNOR_F_NO_OP_CHIP_ERASE = BIT(2),
201         SNOR_F_S3AN_ADDR_DEFAULT = BIT(3),
202         SNOR_F_READY_XSR_RDY    = BIT(4),
203 +       SNOR_F_4B_EXT_ADDR      = BIT(5),
204  };
205  
206  /**
207 @@ -188,6 +190,7 @@ struct spi_nor {
208         enum read_mode          flash_read;
209         bool                    sst_write_second;
210         u32                     flags;
211 +       u8                      ext_addr;
212         u8                      cmd_buf[SPI_NOR_MAX_CMD_SIZE];
213  
214         int (*prepare)(struct spi_nor *nor, enum spi_nor_ops ops);