[PATCH v4] mtd: spinand: winbond: add support for W25N04LW

Dominique Martinet posted 1 patch 1 month, 1 week ago
drivers/mtd/nand/spi/winbond.c | 35 ++++++++++++++++++++++++++++++++---
1 file changed, 32 insertions(+), 3 deletions(-)
[PATCH v4] mtd: spinand: winbond: add support for W25N04LW
Posted by Dominique Martinet 1 month, 1 week ago
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@atmark-techno.com>
Signed-off-by: Daisuke Mizobuchi <mizo@atmark-techno.com>
Signed-off-by: Dominique Martinet <dominique.martinet@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@atmark-techno.com>


Re: [PATCH v4] mtd: spinand: winbond: add support for W25N04LW
Posted by Miquel Raynal 3 weeks, 3 days ago
On Mon, 17 Aug 2026 05:29:17 +0000, Dominique Martinet wrote:
> 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
> 
> [...]

Applied to nand/next, thanks!

[1/1] mtd: spinand: winbond: add support for W25N04LW
      commit: ea609469a3e37fc731636c7be6bc3e8983b2070b

Patche(s) should be available on mtd/linux.git and will be
part of the next PR (provided that no robot complains by then).

Kind regards,
Miquèl