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 #define WINBOND_NOR_OP_SELDIE	0xc2	/* Select active die */
14 
15 #define WINBOND_NOR_WREAR_OP(buf)					\
16 	SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_WREAR, 0),		\
17 		   SPI_MEM_OP_NO_ADDR,					\
18 		   SPI_MEM_OP_NO_DUMMY,					\
19 		   SPI_MEM_OP_DATA_OUT(1, buf, 0))
20 
21 #define WINBOND_NOR_SELDIE_OP(buf)					\
22 	SPI_MEM_OP(SPI_MEM_OP_CMD(WINBOND_NOR_OP_SELDIE, 0),		\
23 		   SPI_MEM_OP_NO_ADDR,					\
24 		   SPI_MEM_OP_NO_DUMMY,					\
25 		   SPI_MEM_OP_DATA_OUT(1, buf, 0))
26 
27 static int
w25q128_post_bfpt_fixups(struct spi_nor * nor,const struct sfdp_parameter_header * bfpt_header,const struct sfdp_bfpt * bfpt)28 w25q128_post_bfpt_fixups(struct spi_nor *nor,
29 			 const struct sfdp_parameter_header *bfpt_header,
30 			 const struct sfdp_bfpt *bfpt)
31 {
32 	/*
33 	 * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded
34 	 * size is really wrong. It seems that they confused Mbit with MiB.
35 	 * Thus the flash is discovered as a 2MiB device.
36 	 */
37 	if (bfpt_header->major == SFDP_JESD216_MAJOR &&
38 	    bfpt_header->minor == SFDP_JESD216_MINOR &&
39 	    nor->params->size == SZ_2M &&
40 	    nor->params->erase_map.regions[0].size == SZ_2M) {
41 		nor->params->size = SZ_16M;
42 		nor->params->erase_map.regions[0].size = SZ_16M;
43 	}
44 
45 	return 0;
46 }
47 
48 static const struct spi_nor_fixups w25q128_fixups = {
49 	.post_bfpt = w25q128_post_bfpt_fixups,
50 };
51 
52 static int
w25q256_post_bfpt_fixups(struct spi_nor * nor,const struct sfdp_parameter_header * bfpt_header,const struct sfdp_bfpt * bfpt)53 w25q256_post_bfpt_fixups(struct spi_nor *nor,
54 			 const struct sfdp_parameter_header *bfpt_header,
55 			 const struct sfdp_bfpt *bfpt)
56 {
57 	/*
58 	 * W25Q256JV supports 4B opcodes but W25Q256FV does not.
59 	 * Unfortunately, Winbond has re-used the same JEDEC ID for both
60 	 * variants which prevents us from defining a new entry in the parts
61 	 * table.
62 	 * To differentiate between W25Q256JV and W25Q256FV check SFDP header
63 	 * version: only JV has JESD216A compliant structure (version 5).
64 	 */
65 	if (bfpt_header->major == SFDP_JESD216_MAJOR &&
66 	    bfpt_header->minor == SFDP_JESD216A_MINOR)
67 		nor->flags |= SNOR_F_4B_OPCODES;
68 
69 	return 0;
70 }
71 
72 static const struct spi_nor_fixups w25q256_fixups = {
73 	.post_bfpt = w25q256_post_bfpt_fixups,
74 };
75 
76 /**
77  * winbond_nor_select_die() - Set active die.
78  * @nor:	pointer to 'struct spi_nor'.
79  * @die:	die to set active.
80  *
81  * Certain Winbond chips feature more than a single die. This is mostly hidden
82  * to the user, except that some chips may experience time deviation when
83  * modifying the status bits between dies, which in some corner cases may
84  * produce problematic races. Being able to explicitly select a die to check its
85  * state in this case may be useful.
86  *
87  * Return: 0 on success, -errno otherwise.
88  */
winbond_nor_select_die(struct spi_nor * nor,u8 die)89 static int winbond_nor_select_die(struct spi_nor *nor, u8 die)
90 {
91 	int ret;
92 
93 	nor->bouncebuf[0] = die;
94 
95 	if (nor->spimem) {
96 		struct spi_mem_op op = WINBOND_NOR_SELDIE_OP(nor->bouncebuf);
97 
98 		spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
99 
100 		ret = spi_mem_exec_op(nor->spimem, &op);
101 	} else {
102 		ret = spi_nor_controller_ops_write_reg(nor,
103 						       WINBOND_NOR_OP_SELDIE,
104 						       nor->bouncebuf, 1);
105 	}
106 
107 	if (ret)
108 		dev_dbg(nor->dev, "error %d selecting die %d\n", ret, die);
109 
110 	return ret;
111 }
112 
winbond_nor_multi_die_ready(struct spi_nor * nor)113 static int winbond_nor_multi_die_ready(struct spi_nor *nor)
114 {
115 	int ret, i;
116 
117 	for (i = 0; i < nor->params->n_dice; i++) {
118 		ret = winbond_nor_select_die(nor, i);
119 		if (ret)
120 			return ret;
121 
122 		ret = spi_nor_sr_ready(nor);
123 		if (ret <= 0)
124 			return ret;
125 	}
126 
127 	return 1;
128 }
129 
130 static int
winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor * nor)131 winbond_nor_multi_die_post_sfdp_fixups(struct spi_nor *nor)
132 {
133 	/*
134 	 * SFDP supports dice numbers, but this information is only available in
135 	 * optional additional tables which are not provided by these chips.
136 	 * Dice number has an impact though, because these devices need extra
137 	 * care when reading the busy bit.
138 	 */
139 	nor->params->n_dice = nor->params->size / SZ_64M;
140 	nor->params->ready = winbond_nor_multi_die_ready;
141 
142 	return 0;
143 }
144 
145 static const struct spi_nor_fixups winbond_nor_multi_die_fixups = {
146 	.post_sfdp = winbond_nor_multi_die_post_sfdp_fixups,
147 };
148 
149 static const struct flash_info winbond_nor_parts[] = {
150 	{
151 		.id = SNOR_ID(0xef, 0x30, 0x10),
152 		.name = "w25x05",
153 		.size = SZ_64K,
154 		.no_sfdp_flags = SECT_4K,
155 	}, {
156 		.id = SNOR_ID(0xef, 0x30, 0x11),
157 		.name = "w25x10",
158 		.size = SZ_128K,
159 		.no_sfdp_flags = SECT_4K,
160 	}, {
161 		.id = SNOR_ID(0xef, 0x30, 0x12),
162 		.name = "w25x20",
163 		.size = SZ_256K,
164 		.no_sfdp_flags = SECT_4K,
165 	}, {
166 		.id = SNOR_ID(0xef, 0x30, 0x13),
167 		.name = "w25x40",
168 		.size = SZ_512K,
169 		.no_sfdp_flags = SECT_4K,
170 	}, {
171 		.id = SNOR_ID(0xef, 0x30, 0x14),
172 		.name = "w25x80",
173 		.size = SZ_1M,
174 		.no_sfdp_flags = SECT_4K,
175 	}, {
176 		.id = SNOR_ID(0xef, 0x30, 0x15),
177 		.name = "w25x16",
178 		.size = SZ_2M,
179 		.no_sfdp_flags = SECT_4K,
180 	}, {
181 		.id = SNOR_ID(0xef, 0x30, 0x16),
182 		.name = "w25x32",
183 		.size = SZ_4M,
184 		.no_sfdp_flags = SECT_4K,
185 	}, {
186 		.id = SNOR_ID(0xef, 0x30, 0x17),
187 		.name = "w25x64",
188 		.size = SZ_8M,
189 		.no_sfdp_flags = SECT_4K,
190 	}, {
191 		.id = SNOR_ID(0xef, 0x40, 0x12),
192 		.name = "w25q20cl",
193 		.size = SZ_256K,
194 		.no_sfdp_flags = SECT_4K,
195 	}, {
196 		.id = SNOR_ID(0xef, 0x40, 0x14),
197 		.name = "w25q80bl",
198 		.size = SZ_1M,
199 		.no_sfdp_flags = SECT_4K,
200 	}, {
201 		.id = SNOR_ID(0xef, 0x40, 0x16),
202 		.name = "w25q32",
203 		.size = SZ_4M,
204 		.no_sfdp_flags = SECT_4K,
205 	}, {
206 		.id = SNOR_ID(0xef, 0x40, 0x17),
207 		.name = "w25q64",
208 		.size = SZ_8M,
209 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
210 	}, {
211 		.id = SNOR_ID(0xef, 0x40, 0x18),
212 		/* Flavors w/ and w/o SFDP. */
213 		.name = "w25q128",
214 		.size = SZ_16M,
215 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
216 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
217 		.fixups = &w25q128_fixups,
218 	}, {
219 		.id = SNOR_ID(0xef, 0x40, 0x19),
220 		.name = "w25q256",
221 		.size = SZ_32M,
222 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
223 		.fixups = &w25q256_fixups,
224 	}, {
225 		.id = SNOR_ID(0xef, 0x40, 0x20),
226 		.name = "w25q512jvq",
227 		.size = SZ_64M,
228 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
229 	}, {
230 		/* W25Q01JV */
231 		.id = SNOR_ID(0xef, 0x40, 0x21),
232 		.fixups = &winbond_nor_multi_die_fixups,
233 	}, {
234 		.id = SNOR_ID(0xef, 0x50, 0x12),
235 		.name = "w25q20bw",
236 		.size = SZ_256K,
237 		.no_sfdp_flags = SECT_4K,
238 	}, {
239 		.id = SNOR_ID(0xef, 0x50, 0x14),
240 		.name = "w25q80",
241 		.size = SZ_1M,
242 		.no_sfdp_flags = SECT_4K,
243 	}, {
244 		.id = SNOR_ID(0xef, 0x60, 0x12),
245 		.name = "w25q20ew",
246 		.size = SZ_256K,
247 		.no_sfdp_flags = SECT_4K,
248 	}, {
249 		.id = SNOR_ID(0xef, 0x60, 0x15),
250 		.name = "w25q16dw",
251 		.size = SZ_2M,
252 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
253 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
254 	}, {
255 		.id = SNOR_ID(0xef, 0x60, 0x16),
256 		.name = "w25q32dw",
257 		.size = SZ_4M,
258 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
259 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
260 		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
261 	}, {
262 		.id = SNOR_ID(0xef, 0x60, 0x17),
263 		.name = "w25q64dw",
264 		.size = SZ_8M,
265 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
266 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
267 	}, {
268 		.id = SNOR_ID(0xef, 0x60, 0x18),
269 		.name = "w25q128fw",
270 		.size = SZ_16M,
271 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
272 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
273 	}, {
274 		.id = SNOR_ID(0xef, 0x60, 0x19),
275 		.name = "w25q256jw",
276 		.size = SZ_32M,
277 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
278 	}, {
279 		.id = SNOR_ID(0xef, 0x60, 0x20),
280 		.name = "w25q512nwq",
281 		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
282 	}, {
283 		.id = SNOR_ID(0xef, 0x70, 0x15),
284 		.name = "w25q16jv-im/jm",
285 		.size = SZ_2M,
286 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
287 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
288 	}, {
289 		.id = SNOR_ID(0xef, 0x70, 0x16),
290 		.name = "w25q32jv",
291 		.size = SZ_4M,
292 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
293 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
294 	}, {
295 		.id = SNOR_ID(0xef, 0x70, 0x17),
296 		.name = "w25q64jvm",
297 		.size = SZ_8M,
298 		.no_sfdp_flags = SECT_4K,
299 	}, {
300 		.id = SNOR_ID(0xef, 0x70, 0x18),
301 		.name = "w25q128jv",
302 		.size = SZ_16M,
303 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
304 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
305 	}, {
306 		.id = SNOR_ID(0xef, 0x70, 0x19),
307 		.name = "w25q256jvm",
308 	}, {
309 		/* W25Q02JV */
310 		.id = SNOR_ID(0xef, 0x70, 0x22),
311 		.fixups = &winbond_nor_multi_die_fixups,
312 	}, {
313 		.id = SNOR_ID(0xef, 0x71, 0x19),
314 		.name = "w25m512jv",
315 		.size = SZ_64M,
316 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
317 	}, {
318 		.id = SNOR_ID(0xef, 0x80, 0x16),
319 		.name = "w25q32jwm",
320 		.size = SZ_4M,
321 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
322 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
323 		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
324 	}, {
325 		.id = SNOR_ID(0xef, 0x80, 0x17),
326 		.name = "w25q64jwm",
327 		.size = SZ_8M,
328 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
329 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
330 	}, {
331 		.id = SNOR_ID(0xef, 0x80, 0x18),
332 		.name = "w25q128jwm",
333 		.size = SZ_16M,
334 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
335 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
336 	}, {
337 		.id = SNOR_ID(0xef, 0x80, 0x19),
338 		.name = "w25q256jwm",
339 		.size = SZ_32M,
340 		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,
341 		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,
342 	}, {
343 		.id = SNOR_ID(0xef, 0x80, 0x20),
344 		.name = "w25q512nwm",
345 		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),
346 	},
347 };
348 
349 /**
350  * winbond_nor_write_ear() - Write Extended Address Register.
351  * @nor:	pointer to 'struct spi_nor'.
352  * @ear:	value to write to the Extended Address Register.
353  *
354  * Return: 0 on success, -errno otherwise.
355  */
winbond_nor_write_ear(struct spi_nor * nor,u8 ear)356 static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)
357 {
358 	int ret;
359 
360 	nor->bouncebuf[0] = ear;
361 
362 	if (nor->spimem) {
363 		struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);
364 
365 		spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);
366 
367 		ret = spi_mem_exec_op(nor->spimem, &op);
368 	} else {
369 		ret = spi_nor_controller_ops_write_reg(nor,
370 						       WINBOND_NOR_OP_WREAR,
371 						       nor->bouncebuf, 1);
372 	}
373 
374 	if (ret)
375 		dev_dbg(nor->dev, "error %d writing EAR\n", ret);
376 
377 	return ret;
378 }
379 
380 /**
381  * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond
382  * flashes.
383  * @nor:	pointer to 'struct spi_nor'.
384  * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte
385  *		address mode.
386  *
387  * Return: 0 on success, -errno otherwise.
388  */
winbond_nor_set_4byte_addr_mode(struct spi_nor * nor,bool enable)389 static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)
390 {
391 	int ret;
392 
393 	ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);
394 	if (ret || enable)
395 		return ret;
396 
397 	/*
398 	 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address
399 	 * Register to be set to 1, so all 3-byte-address reads come from the
400 	 * second 16M. We must clear the register to enable normal behavior.
401 	 */
402 	ret = spi_nor_write_enable(nor);
403 	if (ret)
404 		return ret;
405 
406 	ret = winbond_nor_write_ear(nor, 0);
407 	if (ret)
408 		return ret;
409 
410 	return spi_nor_write_disable(nor);
411 }
412 
413 static const struct spi_nor_otp_ops winbond_nor_otp_ops = {
414 	.read = spi_nor_otp_read_secr,
415 	.write = spi_nor_otp_write_secr,
416 	.erase = spi_nor_otp_erase_secr,
417 	.lock = spi_nor_otp_lock_sr2,
418 	.is_locked = spi_nor_otp_is_locked_sr2,
419 };
420 
winbond_nor_late_init(struct spi_nor * nor)421 static int winbond_nor_late_init(struct spi_nor *nor)
422 {
423 	struct spi_nor_flash_parameter *params = nor->params;
424 
425 	if (params->otp.org)
426 		params->otp.ops = &winbond_nor_otp_ops;
427 
428 	/*
429 	 * Winbond seems to require that the Extended Address Register to be set
430 	 * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV.
431 	 * This requirement is not described in the JESD216 SFDP standard, thus
432 	 * it is Winbond specific. Since we do not know if other Winbond flashes
433 	 * have the same requirement, play safe and overwrite the method parsed
434 	 * from BFPT, if any.
435 	 */
436 	params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;
437 
438 	return 0;
439 }
440 
441 static const struct spi_nor_fixups winbond_nor_fixups = {
442 	.late_init = winbond_nor_late_init,
443 };
444 
445 const struct spi_nor_manufacturer spi_nor_winbond = {
446 	.name = "winbond",
447 	.parts = winbond_nor_parts,
448 	.nparts = ARRAY_SIZE(winbond_nor_parts),
449 	.fixups = &winbond_nor_fixups,
450 };
451