Linux Audio

Check our new training course

Loading...
v6.8
  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};
v6.2
  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};