All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RFC v2] mtd: spinand: winbond: add support for W25N04LW
@ 2026-08-12  4:14 ` Dominique Martinet
  0 siblings, 0 replies; 8+ messages in thread
From: Dominique Martinet @ 2026-08-12  4:14 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Md Sadre Alam,
	Vignesh Raghavendra
  Cc: linux-mtd, linux-kernel, Daisuke Mizobuchi, Dominique Martinet

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 defined to only show ECC-protected "user data I", leaving
"user data II" unavailable.

Signed-off-by: Daisuke Mizobuchi <mizo@atmark-techno.com>
Signed-off-by: Dominique Martinet <dominique.martinet@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

I've tested the NAND with various mtd test utils (mtd_oobtest,
mtd_pagetest, mtd_subpagetest, mtd_nandbiterrs and some mtd-utils
userspace counterparts) with no problem, but I'm not sure what to do
with the ooblayout free() informations: as written in the commit
message, this chip has 2+12 bytes of user data per sector where the
first two are not ECC protected and the later 12 are.

I believe user should make the choice of which oob bytes they want to
use, but afaik we can't say "this is not protected", so we can't let
them choose and must either say both are free or only provide one like I
did here.

As an extra data point, this "user area I and II" distinction is the same
in W25N04KW (same 2+12), but w25n02kv_ooblayout_free() use there returns
the whole 14 bytes as a single chunk, so I explicitly made a different
choice here.
(I believe that's not something that can be changed easily, so we should
discuss this before merging)

That aside:
- Alam, would you like your name somewhere in the commit? I didn't keep
  anything from your commit because I already had one, but happy to add
  a Co-developed-by or something
- I kept NAND_ECCREQ(8, 512) like W25N04KW but the datasheet says it's
  based on 8-bits/544-bytes ECC, so I should set it as (8, 544)?
  (since it's protecting 512+12 bytes for up to 8 bits corruption)
  In practice ECC is done by hardware and looks like it is correctly
  reported (e.g. manually overwriting a couple of bits with nandwrite
  and checking with nanddump properly corrects and reports number of
  corrected bits), but might as well get this right, even if as far as I
  understand nothing actually uses the ECC oob data to double-check
  hardware status?
- I'll shamefully admit I do not understand the read / write /
  update_cache_variants() I copied from KW,
  Alam had the same but it'd be great to confirm using the same
  callbacks makes sense?
- (I wrote about OTP in the commit message but have no plan of
  implementing it at this point as we have no use for it, I still
  intend to look at continuous read after some other work)

Thanks!

-Dominique
---
 drivers/mtd/nand/spi/winbond.c | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 9b78c1e6cbc9..5b86e5cb06a3 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -335,6 +335,34 @@ static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section,
 	return 0;
 }
 
+static int w25n04lw_ooblayout_ecc(struct mtd_info *mtd, int section,
+				  struct mtd_oob_region *region)
+{
+	if (section > 7)
+		return -ERANGE;
+
+	region->offset = 128 + (16 * section);
+	region->length = 13;
+
+	return 0;
+}
+
+static int w25n04lw_ooblayout_free(struct mtd_info *mtd, int section,
+				   struct mtd_oob_region *region)
+{
+	if (section > 7)
+		return -ERANGE;
+
+	// the W25N04LW chip actually has two free ranges per section:
+	// "User Data I" at (16 * section) + 4, length 12
+	// "User Data II" at (16 * section) + 2, length 2
+	// The later is not ECC protected so this only returns User Data I
+	region->offset = (16 * section) + 4;
+	region->length = 12;
+
+	return 0;
+}
+
 static const struct mtd_ooblayout_ops w25n01kv_ooblayout = {
 	.ecc = w25n01kv_ooblayout_ecc,
 	.free = w25n02kv_ooblayout_free,
@@ -345,6 +373,11 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
 	.free = w25n02kv_ooblayout_free,
 };
 
+static const struct mtd_ooblayout_ops w25n04lw_ooblayout = {
+	.ecc = w25n04lw_ooblayout_ecc,
+	.free = w25n04lw_ooblayout_free,
+};
+
 static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
 				  struct mtd_oob_region *region)
 {
@@ -768,6 +801,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(&w25n04lw_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>



______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/

^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-08-12  8:08 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-12  4:14 [PATCH RFC v2] mtd: spinand: winbond: add support for W25N04LW Dominique Martinet
2026-08-12  4:14 ` Dominique Martinet
2026-08-12  5:04 ` Dominique Martinet
2026-08-12  5:04   ` Dominique Martinet
2026-08-12  7:35   ` Miquel Raynal
2026-08-12  7:35     ` Miquel Raynal
2026-08-12  8:08     ` Dominique Martinet
2026-08-12  8:08       ` Dominique Martinet

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.