diff options
| author | Miquel Raynal <miquel.raynal@bootlin.com> | 2026-09-04 20:12:46 +0200 |
|---|---|---|
| committer | Michael Walle <mwalle@kernel.org> | 2026-09-07 09:45:21 +0200 |
| commit | 7fdc105ff98d712e1e2f7e9f55e4ab2251e1344f (patch) | |
| tree | be7ed2e4d5cb8572aee98a68fcd812acfa303b92 | |
| parent | aa0a09e0e99838ab9349b1958118c44e6db6737d (diff) | |
| download | linux-next-7fdc105ff98d712e1e2f7e9f55e4ab2251e1344f.tar.gz linux-next-7fdc105ff98d712e1e2f7e9f55e4ab2251e1344f.zip | |
mtd: spi-nor: winbond: Prepare introduction of W25QxxRV-Q/N parts
There is an ID collision between the JV and RV families. Both chips are
very similar in practice, it is mostly a matter of electrical
differences (mostly power consumption being lower).
As a significant difference, RV chips identify themselves as supporting
the new SFDP (rev F) field which forces an alternate write SR2 opcode
(0x31). They also do not require the multi-die fixups which must remain
assigned to the JV chips.
Finally, since they share the IDs but not the names, we must hide the
names using a fixup.
Reviewed-by: Takahiro Kuwano <takahiro.kuwano@infineon.com>
Reviewed-by: Michael Walle <mwalle@kernel.org>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Signed-off-by: Michael Walle <mwalle@kernel.org>
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.c | 11 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/sfdp.h | 2 | ||||
| -rw-r--r-- | drivers/mtd/spi-nor/winbond.c | 60 |
3 files changed, 70 insertions, 3 deletions
diff --git a/drivers/mtd/spi-nor/sfdp.c b/drivers/mtd/spi-nor/sfdp.c index 95f47c0c9317..c21a6953db96 100644 --- a/drivers/mtd/spi-nor/sfdp.c +++ b/drivers/mtd/spi-nor/sfdp.c @@ -1715,3 +1715,14 @@ free_param_headers: return err; } + +/** + * spi_nor_sfdp_get_header() - retrieves the SFDP header + * @nor: pointer to a 'struct spi_nor' with a valid SFDP field + * + * Return: the cached SFDP header. + */ +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor) +{ + return (struct sfdp_header *)nor->sfdp->dwords; +} diff --git a/drivers/mtd/spi-nor/sfdp.h b/drivers/mtd/spi-nor/sfdp.h index 2cbe22246c5e..1e49ed548a7e 100644 --- a/drivers/mtd/spi-nor/sfdp.h +++ b/drivers/mtd/spi-nor/sfdp.h @@ -160,4 +160,6 @@ struct sfdp_header { struct sfdp_parameter_header bfpt_header; }; +struct sfdp_header *spi_nor_sfdp_get_header(const struct spi_nor *nor); + #endif /* __LINUX_MTD_SFDP_H */ diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c index 063b85e041cd..9af4f21e82bf 100644 --- a/drivers/mtd/spi-nor/winbond.c +++ b/drivers/mtd/spi-nor/winbond.c @@ -24,6 +24,39 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(1, buf, 0)) +static bool is_w25qxxrv(const struct spi_nor *nor) +{ + struct sfdp_header *sfdp_h = spi_nor_sfdp_get_header(nor); + + /* + * W25QxxRV chips re-use the same ID as the W25QxxJV family. + * + * Chips are very similar, W25QxxRV brings mostly performance and power + * consumption improvements. The RV family does not require the multi + * die fixup. + * + * They can be distinguished based on their SFDP minor revision: + * W25QxxJV: JESD216A, minor revision == 05h + * W25Q512/01/02JV: JESD216B, minor revision == 06h + * W25QxxRV: JESD216F, minor revision >= 0Ah + */ + return sfdp_h->minor >= SFDP_JESD216F_MINOR; +} + +/* + * Since SFDP is populated after ->default_init(), the match functions using + * nor->sfdp as discriminant cannot be used for this specific early fixup. + */ +static bool winbond_jv_match(const struct spi_nor *nor) +{ + return !nor->sfdp || !is_w25qxxrv(nor); +} + +static bool winbond_rv_match(const struct spi_nor *nor) +{ + return nor->sfdp && is_w25qxxrv(nor); +} + static int w25q128_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, @@ -146,6 +179,22 @@ static const struct spi_nor_fixups winbond_nor_multi_die_fixups = { .post_sfdp = winbond_nor_multi_die_post_sfdp_fixups, }; +static int winbond_nor_partname_post_sfdp_fixups(struct spi_nor *nor) +{ + /* + * W25QxxRV parts re-use the JEDEC IDs of the JV family. Their name + * being a legacy field, it is kept for the already established JV parts + * but must not be exposed by the newer RV ones. + */ + nor->partname = NULL; + + return 0; +} + +static const struct spi_nor_fixups winbond_nor_partname_fixups = { + .post_sfdp = winbond_nor_partname_post_sfdp_fixups, +}; + static const struct flash_info winbond_nor_parts[] = { { .id = SNOR_ID(0xef, 0x30, 0x10), @@ -552,9 +601,14 @@ static const struct spi_nor_fixup winbond_fixups[] = { { .fixups = &winbond_nor_fixups }, { .id = SNOR_ID(0xef, 0x40, 0x18), .fixups = &w25q128_fixups }, { .id = SNOR_ID(0xef, 0x40, 0x19), .fixups = &w25q256_fixups }, - { .id = SNOR_ID(0xef, 0x40, 0x21), .fixups = &winbond_nor_multi_die_fixups }, - { .id = SNOR_ID(0xef, 0x70, 0x21), .fixups = &winbond_nor_multi_die_fixups }, - { .id = SNOR_ID(0xef, 0x70, 0x22), .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x40), .match = winbond_rv_match, + .fixups = &winbond_nor_partname_fixups }, + { .id = SNOR_ID(0xef, 0x40, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x21), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, + { .id = SNOR_ID(0xef, 0x70, 0x22), .match = winbond_jv_match, + .fixups = &winbond_nor_multi_die_fixups }, }; const struct spi_nor_manufacturer spi_nor_winbond = { |
