Loading...
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2005, Intec Automation Inc.
4 * Copyright (C) 2014, Freescale Semiconductor, Inc.
5 */
6
7#include <linux/mtd/spi-nor.h>
8
9#include "core.h"
10
11#define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
12#define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */
13
14#define WINBOND_NOR_WREAR_OP(buf) \
15 SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \
16 SPI_MEM_OP_NO_ADDR, \
17 SPI_MEM_OP_NO_DUMMY, \
18 SPI_MEM_OP_DATA_OUT(1, buf, 0))
19
20static int
21w25q256_post_bfpt_fixups(struct spi_nor *nor,
22 const struct sfdp_parameter_header *bfpt_header,
23 const struct sfdp_bfpt *bfpt)
24{
25 /*
26 * W25Q256JV supports 4B opcodes but W25Q256FV does not.
27 * Unfortunately, Winbond has re-used the same JEDEC ID for both
28 * variants which prevents us from defining a new entry in the parts
29 * table.
30 * To differentiate between W25Q256JV and W25Q256FV check SFDP header
31 * version: only JV has JESD216A compliant structure (version 5).
32 */
33 if (bfpt_header->major == SFDP_JESD216_MAJOR &&
34 bfpt_header->minor == SFDP_JESD216A_MINOR)
35 nor->flags |= SNOR_F_4B_OPCODES;
36
37 return 0;
38}
39
40static const struct spi_nor_fixups w25q256_fixups = {
41 .post_bfpt = w25q256_post_bfpt_fixups,
42};
43
44static const struct flash_info winbond_nor_parts[] = {
45 {
46 .id = SNOR_ID(0xef, 0x30, 0x10),
47 .name = "w25x05",
48 .size = SZ_64K,
49 .no_sfdp_flags = SECT_4K,
50 }, {
51 .id = SNOR_ID(0xef, 0x30, 0x11),
52 .name = "w25x10",
53 .size = SZ_128K,
54 .no_sfdp_flags = SECT_4K,
55 }, {
56 .id = SNOR_ID(0xef, 0x30, 0x12),
57 .name = "w25x20",
58 .size = SZ_256K,
59 .no_sfdp_flags = SECT_4K,
60 }, {
61 .id = SNOR_ID(0xef, 0x30, 0x13),
62 .name = "w25x40",
63 .size = SZ_512K,
64 .no_sfdp_flags = SECT_4K,
65 }, {
66 .id = SNOR_ID(0xef, 0x30, 0x14),
67 .name = "w25x80",
68 .size = SZ_1M,
69 .no_sfdp_flags = SECT_4K,
70 }, {
71 .id = SNOR_ID(0xef, 0x30, 0x15),
72 .name = "w25x16",
73 .size = SZ_2M,
74 .no_sfdp_flags = SECT_4K,
75 }, {
76 .id = SNOR_ID(0xef, 0x30, 0x16),
77 .name = "w25x32",
78 .size = SZ_4M,
79 .no_sfdp_flags = SECT_4K,
80 }, {
81 .id = SNOR_ID(0xef, 0x30, 0x17),
82 .name = "w25x64",
83 .size = SZ_8M,
84 .no_sfdp_flags = SECT_4K,
85 }, {
86 .id = SNOR_ID(0xef, 0x40, 0x12),
87 .name = "w25q20cl",
88 .size = SZ_256K,
89 .no_sfdp_flags = SECT_4K,
90 }, {
91 .id = SNOR_ID(0xef, 0x40, 0x14),
92 .name = "w25q80bl",
93 .size = SZ_1M,
94 .no_sfdp_flags = SECT_4K,
95 }, {
96 .id = SNOR_ID(0xef, 0x40, 0x16),
97 .name = "w25q32",
98 .size = SZ_4M,
99 .no_sfdp_flags = SECT_4K,
100 }, {
101 .id = SNOR_ID(0xef, 0x40, 0x17),
102 .name = "w25q64",
103 .size = SZ_8M,
104 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
105 }, {
106 .id = SNOR_ID(0xef, 0x40, 0x18),
107 .name = "w25q128",
108 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
109 }, {
110 .id = SNOR_ID(0xef, 0x40, 0x19),
111 .name = "w25q256",
112 .size = SZ_32M,
113 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
114 .fixups = &w25q256_fixups,
115 }, {
116 .id = SNOR_ID(0xef, 0x40, 0x20),
117 .name = "w25q512jvq",
118 .size = SZ_64M,
119 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
120 }, {
121 .id = SNOR_ID(0xef, 0x50, 0x12),
122 .name = "w25q20bw",
123 .size = SZ_256K,
124 .no_sfdp_flags = SECT_4K,
125 }, {
126 .id = SNOR_ID(0xef, 0x50, 0x14),
127 .name = "w25q80",
128 .size = SZ_1M,
129 .no_sfdp_flags = SECT_4K,
130 }, {
131 .id = SNOR_ID(0xef, 0x60, 0x12),
132 .name = "w25q20ew",
133 .size = SZ_256K,
134 .no_sfdp_flags = SECT_4K,
135 }, {
136 .id = SNOR_ID(0xef, 0x60, 0x15),
137 .name = "w25q16dw",
138 .size = SZ_2M,
139 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
140 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
141 }, {
142 .id = SNOR_ID(0xef, 0x60, 0x16),
143 .name = "w25q32dw",
144 .size = SZ_4M,
145 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
146 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
147 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
148 }, {
149 .id = SNOR_ID(0xef, 0x60, 0x17),
150 .name = "w25q64dw",
151 .size = SZ_8M,
152 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
153 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
154 }, {
155 .id = SNOR_ID(0xef, 0x60, 0x18),
156 .name = "w25q128fw",
157 .size = SZ_16M,
158 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
159 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
160 }, {
161 .id = SNOR_ID(0xef, 0x60, 0x19),
162 .name = "w25q256jw",
163 .size = SZ_32M,
164 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
165 }, {
166 .id = SNOR_ID(0xef, 0x60, 0x20),
167 .name = "w25q512nwq",
168 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
169 }, {
170 .id = SNOR_ID(0xef, 0x70, 0x15),
171 .name = "w25q16jv-im/jm",
172 .size = SZ_2M,
173 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
174 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
175 }, {
176 .id = SNOR_ID(0xef, 0x70, 0x16),
177 .name = "w25q32jv",
178 .size = SZ_4M,
179 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
180 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
181 }, {
182 .id = SNOR_ID(0xef, 0x70, 0x17),
183 .name = "w25q64jvm",
184 .size = SZ_8M,
185 .no_sfdp_flags = SECT_4K,
186 }, {
187 .id = SNOR_ID(0xef, 0x70, 0x18),
188 .name = "w25q128jv",
189 .size = SZ_16M,
190 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
191 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
192 }, {
193 .id = SNOR_ID(0xef, 0x70, 0x19),
194 .name = "w25q256jvm",
195 }, {
196 .id = SNOR_ID(0xef, 0x71, 0x19),
197 .name = "w25m512jv",
198 .size = SZ_64M,
199 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
200 }, {
201 .id = SNOR_ID(0xef, 0x80, 0x16),
202 .name = "w25q32jwm",
203 .size = SZ_4M,
204 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
205 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
206 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
207 }, {
208 .id = SNOR_ID(0xef, 0x80, 0x17),
209 .name = "w25q64jwm",
210 .size = SZ_8M,
211 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
212 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
213 }, {
214 .id = SNOR_ID(0xef, 0x80, 0x18),
215 .name = "w25q128jwm",
216 .size = SZ_16M,
217 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
218 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
219 }, {
220 .id = SNOR_ID(0xef, 0x80, 0x19),
221 .name = "w25q256jwm",
222 .size = SZ_32M,
223 .flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
224 .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
225 }, {
226 .id = SNOR_ID(0xef, 0x80, 0x20),
227 .name = "w25q512nwm",
228 .otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
229 },
230};
231
232/**
233 * winbond_nor_write_ear() - Write Extended Address Register.
234 * @nor: pointer to 'struct spi_nor'.
235 * @ear: value to write to the Extended Address Register.
236 *
237 * Return: 0 on success, -errno otherwise.
238 */
239static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
240{
241 int ret;
242
243 nor->bouncebuf[0] = ear;
244
245 if (nor->spimem) {
246 struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
247
248 spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
249
250 ret = spi_mem_exec_op(nor->spimem, &op);
251 } else {
252 ret = spi_nor_controller_ops_write_reg(nor,
253 WINBOND_NOR_OP_WREAR,
254 nor->bouncebuf, 1);
255 }
256
257 if (ret)
258 dev_dbg(nor->dev, "error %d writing EAR\n", ret);
259
260 return ret;
261}
262
263/**
264 * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
265 * flashes.
266 * @nor: pointer to 'struct spi_nor'.
267 * @enable: true to enter the 4-byte address mode, false to exit the 4-byte
268 * address mode.
269 *
270 * Return: 0 on success, -errno otherwise.
271 */
272static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
273{
274 int ret;
275
276 ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);
277 if (ret || enable)
278 return ret;
279
280 /*
281 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
282 * Register to be set to 1, so all 3-byte-address reads come from the
283 * second 16M. We must clear the register to enable normal behavior.
284 */
285 ret = spi_nor_write_enable(nor);
286 if (ret)
287 return ret;
288
289 ret = winbond_nor_write_ear(nor, 0);
290 if (ret)
291 return ret;
292
293 return spi_nor_write_disable(nor);
294}
295
296static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
297 .read = spi_nor_otp_read_secr,
298 .write = spi_nor_otp_write_secr,
299 .erase = spi_nor_otp_erase_secr,
300 .lock = spi_nor_otp_lock_sr2,
301 .is_locked = spi_nor_otp_is_locked_sr2,
302};
303
304static int winbond_nor_late_init(struct spi_nor *nor)
305{
306 struct spi_nor_flash_parameter *params = nor->params;
307
308 if (params->otp.org)
309 params->otp.ops = &winbond_nor_otp_ops;
310
311 /*
312 * Winbond seems to require that the Extended Address Register to be set
313 * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV.
314 * This requirement is not described in the JESD216 SFDP standard, thus
315 * it is Winbond specific. Since we do not know if other Winbond flashes
316 * have the same requirement, play safe and overwrite the method parsed
317 * from BFPT, if any.
318 */
319 params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
320
321 return 0;
322}
323
324static const struct spi_nor_fixups winbond_nor_fixups = {
325 .late_init = winbond_nor_late_init,
326};
327
328const struct spi_nor_manufacturer spi_nor_winbond = {
329 .name = "winbond",
330 .parts = winbond_nor_parts,
331 .nparts = ARRAY_SIZE(winbond_nor_parts),
332 .fixups = &winbond_nor_fixups,
333};
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2005, Intec Automation Inc.
4 * Copyright (C) 2014, Freescale Semiconductor, Inc.
5 */
6
7#include <linux/mtd/spi-nor.h>
8
9#include "core.h"
10
11#define WINBOND_NOR_OP_RDEAR 0xc8 /* Read Extended Address Register */
12#define WINBOND_NOR_OP_WREAR 0xc5 /* Write Extended Address Register */
13
14#define WINBOND_NOR_WREAR_OP(buf) \
15 SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0), \
16 SPI_MEM_OP_NO_ADDR, \
17 SPI_MEM_OP_NO_DUMMY, \
18 SPI_MEM_OP_DATA_OUT(1, buf, 0))
19
20static int
21w25q256_post_bfpt_fixups(struct spi_nor *nor,
22 const struct sfdp_parameter_header *bfpt_header,
23 const struct sfdp_bfpt *bfpt)
24{
25 /*
26 * W25Q256JV supports 4B opcodes but W25Q256FV does not.
27 * Unfortunately, Winbond has re-used the same JEDEC ID for both
28 * variants which prevents us from defining a new entry in the parts
29 * table.
30 * To differentiate between W25Q256JV and W25Q256FV check SFDP header
31 * version: only JV has JESD216A compliant structure (version 5).
32 */
33 if (bfpt_header->major == SFDP_JESD216_MAJOR &&
34 bfpt_header->minor == SFDP_JESD216A_MINOR)
35 nor->flags |= SNOR_F_4B_OPCODES;
36
37 return 0;
38}
39
40static const struct spi_nor_fixups w25q256_fixups = {
41 .post_bfpt = w25q256_post_bfpt_fixups,
42};
43
44static const struct flash_info winbond_nor_parts[] = {
45 /* Winbond -- w25x "blocks" are 64K, "sectors" are 4KiB */
46 { "w25x05", INFO(0xef3010, 0, 64 * 1024, 1)
47 NO_SFDP_FLAGS(SECT_4K) },
48 { "w25x10", INFO(0xef3011, 0, 64 * 1024, 2)
49 NO_SFDP_FLAGS(SECT_4K) },
50 { "w25x20", INFO(0xef3012, 0, 64 * 1024, 4)
51 NO_SFDP_FLAGS(SECT_4K) },
52 { "w25x40", INFO(0xef3013, 0, 64 * 1024, 8)
53 NO_SFDP_FLAGS(SECT_4K) },
54 { "w25x80", INFO(0xef3014, 0, 64 * 1024, 16)
55 NO_SFDP_FLAGS(SECT_4K) },
56 { "w25x16", INFO(0xef3015, 0, 64 * 1024, 32)
57 NO_SFDP_FLAGS(SECT_4K) },
58 { "w25q16dw", INFO(0xef6015, 0, 64 * 1024, 32)
59 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
60 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
61 SPI_NOR_QUAD_READ) },
62 { "w25x32", INFO(0xef3016, 0, 64 * 1024, 64)
63 NO_SFDP_FLAGS(SECT_4K) },
64 { "w25q16jv-im/jm", INFO(0xef7015, 0, 64 * 1024, 32)
65 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
66 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
67 SPI_NOR_QUAD_READ) },
68 { "w25q20cl", INFO(0xef4012, 0, 64 * 1024, 4)
69 NO_SFDP_FLAGS(SECT_4K) },
70 { "w25q20bw", INFO(0xef5012, 0, 64 * 1024, 4)
71 NO_SFDP_FLAGS(SECT_4K) },
72 { "w25q20ew", INFO(0xef6012, 0, 64 * 1024, 4)
73 NO_SFDP_FLAGS(SECT_4K) },
74 { "w25q32", INFO(0xef4016, 0, 64 * 1024, 64)
75 NO_SFDP_FLAGS(SECT_4K) },
76 { "w25q32dw", INFO(0xef6016, 0, 64 * 1024, 64)
77 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
78 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
79 OTP_INFO(256, 3, 0x1000, 0x1000) },
80 { "w25q32jv", INFO(0xef7016, 0, 64 * 1024, 64)
81 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
82 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
83 SPI_NOR_QUAD_READ) },
84 { "w25q32jwm", INFO(0xef8016, 0, 64 * 1024, 64)
85 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
86 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
87 OTP_INFO(256, 3, 0x1000, 0x1000) },
88 { "w25q64jwm", INFO(0xef8017, 0, 64 * 1024, 128)
89 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
90 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
91 SPI_NOR_QUAD_READ) },
92 { "w25q128jwm", INFO(0xef8018, 0, 64 * 1024, 256)
93 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
94 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
95 SPI_NOR_QUAD_READ) },
96 { "w25q256jwm", INFO(0xef8019, 0, 64 * 1024, 512)
97 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
98 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
99 SPI_NOR_QUAD_READ) },
100 { "w25x64", INFO(0xef3017, 0, 64 * 1024, 128)
101 NO_SFDP_FLAGS(SECT_4K) },
102 { "w25q64", INFO(0xef4017, 0, 64 * 1024, 128)
103 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
104 SPI_NOR_QUAD_READ) },
105 { "w25q64dw", INFO(0xef6017, 0, 64 * 1024, 128)
106 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
107 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
108 SPI_NOR_QUAD_READ) },
109 { "w25q64jvm", INFO(0xef7017, 0, 64 * 1024, 128)
110 NO_SFDP_FLAGS(SECT_4K) },
111 { "w25q128fw", INFO(0xef6018, 0, 64 * 1024, 256)
112 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
113 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
114 SPI_NOR_QUAD_READ) },
115 { "w25q128jv", INFO(0xef7018, 0, 64 * 1024, 256)
116 FLAGS(SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB)
117 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
118 SPI_NOR_QUAD_READ) },
119 { "w25q80", INFO(0xef5014, 0, 64 * 1024, 16)
120 NO_SFDP_FLAGS(SECT_4K) },
121 { "w25q80bl", INFO(0xef4014, 0, 64 * 1024, 16)
122 NO_SFDP_FLAGS(SECT_4K) },
123 { "w25q128", INFO(0xef4018, 0, 64 * 1024, 256)
124 NO_SFDP_FLAGS(SECT_4K) },
125 { "w25q256", INFO(0xef4019, 0, 64 * 1024, 512)
126 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ)
127 .fixups = &w25q256_fixups },
128 { "w25q256jvm", INFO(0xef7019, 0, 64 * 1024, 512)
129 PARSE_SFDP },
130 { "w25q256jw", INFO(0xef6019, 0, 64 * 1024, 512)
131 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
132 SPI_NOR_QUAD_READ) },
133 { "w25m512jv", INFO(0xef7119, 0, 64 * 1024, 1024)
134 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_QUAD_READ |
135 SPI_NOR_DUAL_READ) },
136 { "w25q512nwq", INFO(0xef6020, 0, 0, 0)
137 PARSE_SFDP
138 OTP_INFO(256, 3, 0x1000, 0x1000) },
139 { "w25q512nwm", INFO(0xef8020, 0, 64 * 1024, 1024)
140 PARSE_SFDP
141 OTP_INFO(256, 3, 0x1000, 0x1000) },
142 { "w25q512jvq", INFO(0xef4020, 0, 64 * 1024, 1024)
143 NO_SFDP_FLAGS(SECT_4K | SPI_NOR_DUAL_READ |
144 SPI_NOR_QUAD_READ) },
145};
146
147/**
148 * winbond_nor_write_ear() - Write Extended Address Register.
149 * @nor: pointer to 'struct spi_nor'.
150 * @ear: value to write to the Extended Address Register.
151 *
152 * Return: 0 on success, -errno otherwise.
153 */
154static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
155{
156 int ret;
157
158 nor->bouncebuf[0] = ear;
159
160 if (nor->spimem) {
161 struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
162
163 spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
164
165 ret = spi_mem_exec_op(nor->spimem, &op);
166 } else {
167 ret = spi_nor_controller_ops_write_reg(nor,
168 WINBOND_NOR_OP_WREAR,
169 nor->bouncebuf, 1);
170 }
171
172 if (ret)
173 dev_dbg(nor->dev, "error %d writing EAR\n", ret);
174
175 return ret;
176}
177
178/**
179 * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
180 * flashes.
181 * @nor: pointer to 'struct spi_nor'.
182 * @enable: true to enter the 4-byte address mode, false to exit the 4-byte
183 * address mode.
184 *
185 * Return: 0 on success, -errno otherwise.
186 */
187static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
188{
189 int ret;
190
191 ret = spi_nor_set_4byte_addr_mode(nor, enable);
192 if (ret || enable)
193 return ret;
194
195 /*
196 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
197 * Register to be set to 1, so all 3-byte-address reads come from the
198 * second 16M. We must clear the register to enable normal behavior.
199 */
200 ret = spi_nor_write_enable(nor);
201 if (ret)
202 return ret;
203
204 ret = winbond_nor_write_ear(nor, 0);
205 if (ret)
206 return ret;
207
208 return spi_nor_write_disable(nor);
209}
210
211static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
212 .read = spi_nor_otp_read_secr,
213 .write = spi_nor_otp_write_secr,
214 .erase = spi_nor_otp_erase_secr,
215 .lock = spi_nor_otp_lock_sr2,
216 .is_locked = spi_nor_otp_is_locked_sr2,
217};
218
219static void winbond_nor_default_init(struct spi_nor *nor)
220{
221 nor->params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
222}
223
224static void winbond_nor_late_init(struct spi_nor *nor)
225{
226 if (nor->params->otp.org->n_regions)
227 nor->params->otp.ops = &winbond_nor_otp_ops;
228}
229
230static const struct spi_nor_fixups winbond_nor_fixups = {
231 .default_init = winbond_nor_default_init,
232 .late_init = winbond_nor_late_init,
233};
234
235const struct spi_nor_manufacturer spi_nor_winbond = {
236 .name = "winbond",
237 .parts = winbond_nor_parts,
238 .nparts = ARRAY_SIZE(winbond_nor_parts),
239 .fixups = &winbond_nor_fixups,
240};