[PATCH v5 12/25] mtd: spi-nor: winbond: Add support for W25Q12RV-Q/N
From: Miquel Raynal <miquel.raynal@bootlin.com>
Date: 2026-09-04 18:13:53
Also in:
linux-doc, lkml
Subsystem:
memory technology devices (mtd), spi nor subsystem, the rest · Maintainers:
Miquel Raynal, Richard Weinberger, Vignesh Raghavendra, Pratyush Yadav, Michael Walle, Linus Torvalds
There is an ID collision with the chip of same density from the JV family. 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). This density wrongly advertizes its size and requires a fixup for that. Reviewed-by: Takahiro Kuwano <takahiro.kuwano@infineon.com> Reviewed-by: Michael Walle <mwalle@kernel.org> Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com> --- + cat /sys/bus/spi/devices/spi0.0/spi-nor/jedec_id ef4018 + cat /sys/bus/spi/devices/spi0.0/spi-nor/manufacturer winbond + xxd -p /sys/bus/spi/devices/spi0.0/spi-nor/sfdp 534644500a0100ff00080117800000ffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff ffffffffffffffffe520f9ffffffff0344eb086b083b42bbfeffffffffff 0000ffff44eb0c200f5210d80000142a010186e114c1e96376337a757a75 f7a2d55c39f66dffe930f880000000000000ac0000000000f7f5ffff0f00 0000060d44bd27ed080d + sha256sum /sys/bus/spi/devices/spi0.0/spi-nor/sfdp 94b52d6e903a2046e144eab0760384b6932c6fdbfec17eb7336f1778c3898daf /sys/bus/spi/devices/spi0.0/spi-nor/sfdp + cat /sys/kernel/debug/spi-nor/spi0.0/capabilities Supported read modes by the flash 1S-1S-1S opcode 0x03 mode cycles 0 dummy cycles 0 1S-1S-2S opcode 0x3b mode cycles 0 dummy cycles 8 1S-2S-2S opcode 0xbb mode cycles 2 dummy cycles 2 1S-1S-4S opcode 0x6b mode cycles 0 dummy cycles 8 1S-4S-4S opcode 0xeb mode cycles 2 dummy cycles 4 4S-4S-4S opcode 0xeb mode cycles 2 dummy cycles 4 Supported page program modes by the flash 1S-1S-1S opcode 0x02 1S-1S-4S opcode 0x32 + cat /sys/kernel/debug/spi-nor/spi0.0/params name (null) id ef 40 18 00 00 00 size 16.0 MiB write size 1 page size 256 address nbytes 3 flags HAS_SR_TB | HAS_LOCK | SOFT_RESET | NO_WP | HAS_SR2_CMP_BIT6 opcodes read 0xeb dummy cycles 6 erase 0xd8 program 0x32 SR1 read 0x05 SR2 read 0x35 SR1 write 0x01 SR2 write 0x31 QE SR2 bit 1 8D extension repeat protocols read 1S-4S-4S write 1S-1S-4S register 1S-1S-1S erase commands 20 (4.00 KiB) [1] 52 (32.0 KiB) [2] d8 (64.0 KiB) [3] c7 (16.0 MiB) sector map region (in hex) | erase mask | overlaid ------------------+------------+--------- 00000000-007fffff | [ 3] | no locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007fffff | unlocked | 64 + dd 'if=/dev/urandom' 'of=./spi_test' 'bs=1M' 'count=2' [ 15.791564] random: crng init done 2+0 records in 2+0 records out + mtd_debug erase /dev/mtd0 0 2097152 Erased 2097152 bytes from address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read Copied 2097152 bytes from address 0x00000000 in flash to spi_read + hexdump spi_read 0000000 ffff ffff ffff ffff ffff ffff ffff ffff * 0200000 + sha256sum spi_read 4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read + mtd_debug write /dev/mtd0 0 2097152 spi_test Copied 2097152 bytes from spi_test to address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read Copied 2097152 bytes from address 0x00000000 in flash to spi_read + sha256sum spi_read spi_test b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_read b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_test + mtd_debug erase /dev/mtd0 0 2097152 Erased 2097152 bytes from address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read Copied 2097152 bytes from address 0x00000000 in flash to spi_read + sha256sum spi_read spi_test 4bda3a28f4ffe603c0ec1258c0034d65a1a0d35ab7bd523a834608adabf03cc5 spi_read b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_test + mtd_debug info /dev/mtd0 mtd.type = MTD_NORFLASH mtd.flags = MTD_CAP_NORFLASH mtd.size = 8388608 (8M) mtd.erasesize = 65536 (64K) mtd.writesize = 1 mtd.oobsize = 0 regions = 0 + alias 'show_sectors=grep -A4 "locked sectors" /sys/kernel/debug/spi-nor/spi0.0/params' + flash_lock -u /dev/mtd0 + flash_lock -i /dev/mtd0 Device: /dev/mtd0 Start: 0 Len: 0x800000 Lock status: unlocked Return code: 0 + mtd_debug erase /dev/mtd0 0 2097152 Erased 2097152 bytes from address 0x00000000 in flash + mtd_debug write /dev/mtd0 0 2097152 spi_test Copied 2097152 bytes from spi_test to address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read Copied 2097152 bytes from address 0x00000000 in flash to spi_read + sha256sum spi_read spi_test b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_read b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_test + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007fffff | unlocked | 64 + flash_lock -l /dev/mtd0 + flash_lock -i /dev/mtd0 Device: /dev/mtd0 Start: 0 Len: 0x800000 Lock status: locked Return code: 1 + mtd_debug erase /dev/mtd0 0 2097152 Erased 2097152 bytes from address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read Copied 2097152 bytes from address 0x00000000 in flash to spi_read + sha256sum spi_read spi_test b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_read b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_test + dd 'if=/dev/urandom' 'of=./spi_test2' 'bs=1M' 'count=2' 2+0 records in 2+0 records out + mtd_debug write /dev/mtd0 0 2097152 spi_test2 Copied 2097152 bytes from spi_test2 to address 0x00000000 in flash + mtd_debug read /dev/mtd0 0 2097152 spi_read2 Copied 2097152 bytes from address 0x00000000 in flash to spi_read2 + sha256sum spi_read spi_read2 spi_test spi_test2 b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_read b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_read2 b3260b68e0373cd253fc558fa9a5c89009d3edb2393f4110702318f69a6d74c2 spi_test 884cedd0494868db17c609edc62e2471ef344025e30cd511fa28b41b9b50e50d spi_test2 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007fffff | locked | 64 + flash_lock -u /dev/mtd0 + cat /sys/class/mtd/mtd0/size + size=8388608 + cat /sys/class/mtd/mtd0/erasesize + bs=65536 + grep unlocked /sys/kernel/debug/spi-nor/spi0.0/params + sed -e 's/.*unlocked | //' + nsectors=64 + ss=131072 + bps=2 + flash_lock -u /dev/mtd0 + flash_lock -l /dev/mtd0 8126464 4 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007bffff | unlocked | 62 007c0000-007fffff | locked | 2 + flash_lock -u /dev/mtd0 8126464 2 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007dffff | unlocked | 63 007e0000-007fffff | locked | 1 + flash_lock -u /dev/mtd0 + flash_lock -l /dev/mtd0 0 4 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-0003ffff | locked | 2 00040000-007fffff | unlocked | 62 + flash_lock -u /dev/mtd0 131072 2 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-0001ffff | locked | 1 00020000-007fffff | unlocked | 63 + all_but_one=126 + flash_lock -u /dev/mtd0 + flash_lock -l /dev/mtd0 131072 126 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-0001ffff | unlocked | 1 00020000-007fffff | locked | 63 + flash_lock -u /dev/mtd0 131072 2 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-0003ffff | unlocked | 2 00040000-007fffff | locked | 62 + flash_lock -u /dev/mtd0 + flash_lock -l /dev/mtd0 0 126 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007dffff | locked | 63 007e0000-007fffff | unlocked | 1 + flash_lock -u /dev/mtd0 8126464 2 + grep -A4 'locked sectors' /sys/kernel/debug/spi-nor/spi0.0/params locked sectors region (in hex) | status | #sectors ------------------+----------+--------- 00000000-007bffff | locked | 62 007c0000-007fffff | unlocked | 2 --- drivers/mtd/spi-nor/winbond.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/spi-nor/winbond.c b/drivers/mtd/spi-nor/winbond.c
index a013234951f8..38355d26bfd4 100644
--- a/drivers/mtd/spi-nor/winbond.c
+++ b/drivers/mtd/spi-nor/winbond.c@@ -43,6 +43,20 @@ static bool is_w25qxxrv(const struct spi_nor *nor) return sfdp_h->minor >= SFDP_JESD216F_MINOR; } +static bool is_zd25q128c(const struct spi_nor *nor, + const struct sfdp_parameter_header *bfpt_header) +{ + /* + * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded + * size is really wrong. It seems that they confused Mbit with MiB. + * Thus the flash is discovered as a 2MiB device. + */ + return bfpt_header->major == SFDP_JESD216_MAJOR && + bfpt_header->minor == SFDP_JESD216_MINOR && + nor->params->size == SZ_2M && + nor->params->erase_map.regions[0].size == SZ_2M; +} + /* * Since SFDP is populated after ->default_init(), the match functions using * nor->sfdp as discriminant cannot be used for this specific early fixup.
@@ -62,15 +76,7 @@ w25q128_post_bfpt_fixups(struct spi_nor *nor, const struct sfdp_parameter_header *bfpt_header, const struct sfdp_bfpt *bfpt) { - /* - * Zetta ZD25Q128C is a clone of the Winbond device. But the encoded - * size is really wrong. It seems that they confused Mbit with MiB. - * Thus the flash is discovered as a 2MiB device. - */ - if (bfpt_header->major == SFDP_JESD216_MAJOR && - bfpt_header->minor == SFDP_JESD216_MINOR && - nor->params->size == SZ_2M && - nor->params->erase_map.regions[0].size == SZ_2M) { + if (is_zd25q128c(nor, bfpt_header) || winbond_rv_match(nor)) { nor->params->size = SZ_16M; nor->params->erase_map.regions[0].size = SZ_16M; }
@@ -261,7 +267,7 @@ static const struct flash_info winbond_nor_parts[] = { .no_sfdp_flags = SECT_4K | SPI_NOR_DUAL_READ | SPI_NOR_QUAD_READ, .flags = SPI_NOR_QUAD_PP | SPI_NOR_HAS_LOCK | SPI_NOR_HAS_TB | SPI_NOR_HAS_CMP, }, { - /* W25Q128JV-Q/N */ + /* W25Q128JV-Q/N, W25Q12RV-Q/N */ .id = SNOR_ID(0xef, 0x40, 0x18), /* Flavors w/ and w/o SFDP. */ .name = "w25q128",
--
2.54.0