[PATCH v4] mtd: spinand: winbond: add support for W25N04LW
Dominique Martinet
dominique.martinet at atmark-techno.com
Sun Aug 16 22:29:17 PDT 2026
Add support for Winbond W25N04LW 4Gbit SPI-NAND:
> 1.8V 4G-bit
> Raw serial SLC NAND flash memory
> Dual/Quad SPI with 104MHz
> buffer read, continuous read & sequential read
Highlights copied from datasheet:
– Page size: 4,352 Bytes (4096 + 256 Bytes)
– Block size: 64 pages (256K + 16K Bytes)
- Built-in 8-Bit ECC for memory array
– ECC status bits indicate ECC results
– Bad Block Management and LUT access
– Software and Hardware Write-Protect
– Power Supply Lock-Down and OTP protection
– Unique ID and Parameter page
– Ten 4KB OTP pages per die
– Read Level Setting for Read Retry
The 4096+256 byte page layout is as follow:
- 4096 bytes ECC protected "main memory array", subdivided in 8
512 bytes sectors
- 128 bytes spare array, subdivided in 8 16 bytes sectors as follow:
- 2 bytes bad block marker (no ECC protection)
- 2 bytes "user data II" (no ECC protection)
- 12 bytes "user data I" (ECC protected)
- 128 bytes parity array, subdivided in 8 16 bytes sectors:
- 13 bytes ECC for main data and user data
- 3 unused bytes
This commit does not support most advanced features such as continuous
read, LUT management and OTP pages, and only provides basic usage.
OOB layout was kept compatible with previous models (exposing both user
data I and II), dependent on mtd->oobsize.
Co-developed-by: Daisuke Mizobuchi <mizo at atmark-techno.com>
Signed-off-by: Daisuke Mizobuchi <mizo at atmark-techno.com>
Signed-off-by: Dominique Martinet <dominique.martinet at atmark-techno.com>
---
Changes in v4:
- Updated ooblayout function to if oobsize = 256 instead of use oobsize
directly
- Link to v3: https://patch.msgid.link/20260814-w25n04lw-v3-1-c6a7423ac7d0@atmark-techno.com
Changes in v3:
- Fixed Co-authored-by / comment style
- Merged ooblayout helper with w25n02kv_ooblayout, using mtd->oobsize
(with an extra comment describing User Data I/II, but keeping current
behaviour)
- Link to v2: https://patch.msgid.link/20260812-w25n04lw-v2-1-deee97602fc4@atmark-techno.com
Changes in v2:
- Link to v1: https://patch.msgid.link/ansYUoNKJyDQ+6f2@hu-mdalam-blr.qualcomm.com
- fixed oob layout
- fixed NAND_MEMORG oob size
---
drivers/mtd/nand/spi/winbond.c | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 9b78c1e6cbc9..a1807fa9ae03 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -314,10 +314,18 @@ static int w25n01kv_ooblayout_ecc(struct mtd_info *mtd, int section,
static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
- if (section > 3)
+ int num_sections = 4;
+ int offset = 64;
+
+ if (mtd->oobsize == 256) {
+ num_sections = 8;
+ offset = 128;
+ }
+
+ if (section >= num_sections)
return -ERANGE;
- region->offset = 64 + (16 * section);
+ region->offset = offset + (16 * section);
region->length = 13;
return 0;
@@ -326,9 +334,21 @@ static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section,
static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
- if (section > 3)
+ int num_sections = 4;
+
+ if (mtd->oobsize == 256)
+ num_sections = 8;
+
+ if (section >= num_sections)
return -ERANGE;
+ /*
+ * For at least W25N04KW and W25N04LW, this region is actually
+ * split in two:
+ * ECC-protected "User Data I" at + 4 offset, length 12
+ * unprotected "User Data II" at + 2 offset, length 2
+ * This returns the full region for backwards compatibility.
+ */
region->offset = (16 * section) + 2;
region->length = 14;
@@ -768,6 +788,15 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants),
0,
SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
+ SPINAND_INFO("W25N04LW", /* 1.8V */
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb2, 0x23),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 1, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)),
SPINAND_INFO("W35N04JW", /* 1.8V */
SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xdf, 0x23),
NAND_MEMORG(1, 4096, 128, 64, 512, 10, 1, 4, 1),
---
base-commit: db2ddb87143519e20a95aa36c60b36107b736a58
change-id: 20260812-w25n04lw-3e9146d8340f
Best regards,
--
Dominique Martinet <dominique.martinet at atmark-techno.com>
More information about the linux-mtd
mailing list