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: &op, proto: nor->reg_proto);
249
250 ret = spi_mem_exec_op(mem: nor->spimem, op: &op);
251 } else {
252 ret = spi_nor_controller_ops_write_reg(nor,
253 WINBOND_NOR_OP_WREAR,
254 buf: nor->bouncebuf, len: 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, ear: 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};
334

source code of linux/drivers/mtd/spi-nor/winbond.c