brintos

brintos / linux-shallow public Read only

0
0
Text · 9.7 KiB · 9f7ce57 Raw
362 lines · c
1// SPDX-License-Identifier: GPL-2.02/*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 int21w25q128_post_bfpt_fixups(struct spi_nor *nor,22			 const struct sfdp_parameter_header *bfpt_header,23			 const struct sfdp_bfpt *bfpt)24{25	/*26	 * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded27	 * size is really wrong. It seems that they confused Mbit with MiB.28	 * Thus the flash is discovered as a 2MiB device.29	 */30	if (bfpt_header->major == SFDP_JESD216_MAJOR &&31	    bfpt_header->minor == SFDP_JESD216_MINOR &&32	    nor->params->size == SZ_2M &&33	    nor->params->erase_map.regions[0].size == SZ_2M) {34		nor->params->size = SZ_16M;35		nor->params->erase_map.regions[0].size = SZ_16M;36	}37 38	return 0;39}40 41static const struct spi_nor_fixups w25q128_fixups = {42	.post_bfpt = w25q128_post_bfpt_fixups,43};44 45static int46w25q256_post_bfpt_fixups(struct spi_nor *nor,47			 const struct sfdp_parameter_header *bfpt_header,48			 const struct sfdp_bfpt *bfpt)49{50	/*51	 * W25Q256JV supports 4B opcodes but W25Q256FV does not.52	 * Unfortunately, Winbond has re-used the same JEDEC ID for both53	 * variants which prevents us from defining a new entry in the parts54	 * table.55	 * To differentiate between W25Q256JV and W25Q256FV check SFDP header56	 * version: only JV has JESD216A compliant structure (version 5).57	 */58	if (bfpt_header->major == SFDP_JESD216_MAJOR &&59	    bfpt_header->minor == SFDP_JESD216A_MINOR)60		nor->flags |= SNOR_F_4B_OPCODES;61 62	return 0;63}64 65static const struct spi_nor_fixups w25q256_fixups = {66	.post_bfpt = w25q256_post_bfpt_fixups,67};68 69static const struct flash_info winbond_nor_parts[] = {70	{71		.id = SNOR_ID(0xef, 0x30, 0x10),72		.name = "w25x05",73		.size = SZ_64K,74		.no_sfdp_flags = SECT_4K,75	}, {76		.id = SNOR_ID(0xef, 0x30, 0x11),77		.name = "w25x10",78		.size = SZ_128K,79		.no_sfdp_flags = SECT_4K,80	}, {81		.id = SNOR_ID(0xef, 0x30, 0x12),82		.name = "w25x20",83		.size = SZ_256K,84		.no_sfdp_flags = SECT_4K,85	}, {86		.id = SNOR_ID(0xef, 0x30, 0x13),87		.name = "w25x40",88		.size = SZ_512K,89		.no_sfdp_flags = SECT_4K,90	}, {91		.id = SNOR_ID(0xef, 0x30, 0x14),92		.name = "w25x80",93		.size = SZ_1M,94		.no_sfdp_flags = SECT_4K,95	}, {96		.id = SNOR_ID(0xef, 0x30, 0x15),97		.name = "w25x16",98		.size = SZ_2M,99		.no_sfdp_flags = SECT_4K,100	}, {101		.id = SNOR_ID(0xef, 0x30, 0x16),102		.name = "w25x32",103		.size = SZ_4M,104		.no_sfdp_flags = SECT_4K,105	}, {106		.id = SNOR_ID(0xef, 0x30, 0x17),107		.name = "w25x64",108		.size = SZ_8M,109		.no_sfdp_flags = SECT_4K,110	}, {111		.id = SNOR_ID(0xef, 0x40, 0x12),112		.name = "w25q20cl",113		.size = SZ_256K,114		.no_sfdp_flags = SECT_4K,115	}, {116		.id = SNOR_ID(0xef, 0x40, 0x14),117		.name = "w25q80bl",118		.size = SZ_1M,119		.no_sfdp_flags = SECT_4K,120	}, {121		.id = SNOR_ID(0xef, 0x40, 0x16),122		.name = "w25q32",123		.size = SZ_4M,124		.no_sfdp_flags = SECT_4K,125	}, {126		.id = SNOR_ID(0xef, 0x40, 0x17),127		.name = "w25q64",128		.size = SZ_8M,129		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,130	}, {131		.id = SNOR_ID(0xef, 0x40, 0x18),132		.name = "w25q128",133		.size = SZ_16M,134		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,135		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,136		.fixups = &w25q128_fixups,137	}, {138		.id = SNOR_ID(0xef, 0x40, 0x19),139		.name = "w25q256",140		.size = SZ_32M,141		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,142		.fixups = &w25q256_fixups,143	}, {144		.id = SNOR_ID(0xef, 0x40, 0x20),145		.name = "w25q512jvq",146		.size = SZ_64M,147		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,148	}, {149		.id = SNOR_ID(0xef, 0x50, 0x12),150		.name = "w25q20bw",151		.size = SZ_256K,152		.no_sfdp_flags = SECT_4K,153	}, {154		.id = SNOR_ID(0xef, 0x50, 0x14),155		.name = "w25q80",156		.size = SZ_1M,157		.no_sfdp_flags = SECT_4K,158	}, {159		.id = SNOR_ID(0xef, 0x60, 0x12),160		.name = "w25q20ew",161		.size = SZ_256K,162		.no_sfdp_flags = SECT_4K,163	}, {164		.id = SNOR_ID(0xef, 0x60, 0x15),165		.name = "w25q16dw",166		.size = SZ_2M,167		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,168		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,169	}, {170		.id = SNOR_ID(0xef, 0x60, 0x16),171		.name = "w25q32dw",172		.size = SZ_4M,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		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),176	}, {177		.id = SNOR_ID(0xef, 0x60, 0x17),178		.name = "w25q64dw",179		.size = SZ_8M,180		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,181		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,182	}, {183		.id = SNOR_ID(0xef, 0x60, 0x18),184		.name = "w25q128fw",185		.size = SZ_16M,186		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,187		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,188	}, {189		.id = SNOR_ID(0xef, 0x60, 0x19),190		.name = "w25q256jw",191		.size = SZ_32M,192		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,193	}, {194		.id = SNOR_ID(0xef, 0x60, 0x20),195		.name = "w25q512nwq",196		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),197	}, {198		.id = SNOR_ID(0xef, 0x70, 0x15),199		.name = "w25q16jv-im/jm",200		.size = SZ_2M,201		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,202		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,203	}, {204		.id = SNOR_ID(0xef, 0x70, 0x16),205		.name = "w25q32jv",206		.size = SZ_4M,207		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,208		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,209	}, {210		.id = SNOR_ID(0xef, 0x70, 0x17),211		.name = "w25q64jvm",212		.size = SZ_8M,213		.no_sfdp_flags = SECT_4K,214	}, {215		.id = SNOR_ID(0xef, 0x70, 0x18),216		.name = "w25q128jv",217		.size = SZ_16M,218		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,219		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,220	}, {221		.id = SNOR_ID(0xef, 0x70, 0x19),222		.name = "w25q256jvm",223	}, {224		.id = SNOR_ID(0xef, 0x71, 0x19),225		.name = "w25m512jv",226		.size = SZ_64M,227		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,228	}, {229		.id = SNOR_ID(0xef, 0x80, 0x16),230		.name = "w25q32jwm",231		.size = SZ_4M,232		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,233		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,234		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),235	}, {236		.id = SNOR_ID(0xef, 0x80, 0x17),237		.name = "w25q64jwm",238		.size = SZ_8M,239		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,240		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,241	}, {242		.id = SNOR_ID(0xef, 0x80, 0x18),243		.name = "w25q128jwm",244		.size = SZ_16M,245		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,246		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,247	}, {248		.id = SNOR_ID(0xef, 0x80, 0x19),249		.name = "w25q256jwm",250		.size = SZ_32M,251		.flags = SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB,252		.no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ,253	}, {254		.id = SNOR_ID(0xef, 0x80, 0x20),255		.name = "w25q512nwm",256		.otp = SNOR_OTP(256, 3, 0x1000, 0x1000),257	},258};259 260/**261 * winbond_nor_write_ear() - Write Extended Address Register.262 * @nor:	pointer to 'struct spi_nor'.263 * @ear:	value to write to the Extended Address Register.264 *265 * Return: 0 on success, -errno otherwise.266 */267static int winbond_nor_write_ear(struct spi_nor *nor, u8 ear)268{269	int ret;270 271	nor->bouncebuf[0] = ear;272 273	if (nor->spimem) {274		struct spi_mem_op op = WINBOND_NOR_WREAR_OP(nor->bouncebuf);275 276		spi_nor_spimem_setup_op(nor, &op, nor->reg_proto);277 278		ret = spi_mem_exec_op(nor->spimem, &op);279	} else {280		ret = spi_nor_controller_ops_write_reg(nor,281						       WINBOND_NOR_OP_WREAR,282						       nor->bouncebuf, 1);283	}284 285	if (ret)286		dev_dbg(nor->dev, "error %d writing EAR\n", ret);287 288	return ret;289}290 291/**292 * winbond_nor_set_4byte_addr_mode() - Set 4-byte address mode for Winbond293 * flashes.294 * @nor:	pointer to 'struct spi_nor'.295 * @enable:	true to enter the 4-byte address mode, false to exit the 4-byte296 *		address mode.297 *298 * Return: 0 on success, -errno otherwise.299 */300static int winbond_nor_set_4byte_addr_mode(struct spi_nor *nor, bool enable)301{302	int ret;303 304	ret = spi_nor_set_4byte_addr_mode_en4b_ex4b(nor, enable);305	if (ret || enable)306		return ret;307 308	/*309	 * On Winbond W25Q256FV, leaving 4byte mode causes the Extended Address310	 * Register to be set to 1, so all 3-byte-address reads come from the311	 * second 16M. We must clear the register to enable normal behavior.312	 */313	ret = spi_nor_write_enable(nor);314	if (ret)315		return ret;316 317	ret = winbond_nor_write_ear(nor, 0);318	if (ret)319		return ret;320 321	return spi_nor_write_disable(nor);322}323 324static const struct spi_nor_otp_ops winbond_nor_otp_ops = {325	.read = spi_nor_otp_read_secr,326	.write = spi_nor_otp_write_secr,327	.erase = spi_nor_otp_erase_secr,328	.lock = spi_nor_otp_lock_sr2,329	.is_locked = spi_nor_otp_is_locked_sr2,330};331 332static int winbond_nor_late_init(struct spi_nor *nor)333{334	struct spi_nor_flash_parameter *params = nor->params;335 336	if (params->otp.org)337		params->otp.ops = &winbond_nor_otp_ops;338 339	/*340	 * Winbond seems to require that the Extended Address Register to be set341	 * to zero when exiting the 4-Byte Address Mode, at least for W25Q256FV.342	 * This requirement is not described in the JESD216 SFDP standard, thus343	 * it is Winbond specific. Since we do not know if other Winbond flashes344	 * have the same requirement, play safe and overwrite the method parsed345	 * from BFPT, if any.346	 */347	params->set_4byte_addr_mode = winbond_nor_set_4byte_addr_mode;348 349	return 0;350}351 352static const struct spi_nor_fixups winbond_nor_fixups = {353	.late_init = winbond_nor_late_init,354};355 356const struct spi_nor_manufacturer spi_nor_winbond = {357	.name = "winbond",358	.parts = winbond_nor_parts,359	.nparts = ARRAY_SIZE(winbond_nor_parts),360	.fixups = &winbond_nor_fixups,361};362