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};
334