[PATCH v2] mtd: spinand: winbond: Add support for W25N08LW

From: Nuno Sá

Date: Fri Sep 11 2026 - 10:32:19 EST


Add support for the W25N08LW, a 1.8V 8Gbit SPI-NAND made of two
4Gbit LUNs, with 4096-byte pages and 256 bytes of spare area.

The chip reuses the KV ECC status helper, but needs its own OOB
layout because the geometry of its spare area differs from the
existing Winbond parts. Only half of it is visible while the
internal ECC is enabled, so the ECC region is reported as
inaccessible and just the free bytes are exposed.

Assisted-by: Claude:Opus-5
Signed-off-by: Nuno Sá <nuno.sa@xxxxxxxxxx>
---
Changes in v2:
- Only expose the ECC-protected "User Data I" bytes in the OOB free
layout. The "User Data II" bytes are not covered by the on-die ECC
engine, so they are no longer exposed (suggested by Miquel). This
lowers oobavail from 126 to 96 bytes.
- Add continuous read support: add the continuous read operation
variants and wire the chip entry to them via
SPINAND_INFO_OP_VARIANTS_WITH_CONT() and SPINAND_CONT_READ(). The
existing Winbond continuous read templates and the W25NxxJW cache
read enable helper are reused as-is.
- Link to v1: https://patch.msgid.link/20260831-mtd-nand-new-chip-support-v1-1-dca9f63ac0f9@xxxxxxxxxx
---
drivers/mtd/nand/spi/winbond.c | 52 ++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index a1807fa9ae03..810975808c97 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -211,6 +211,13 @@ static SPINAND_OP_VARIANTS(read_cache_variants,
SPINAND_PAGE_READ_FROM_CACHE_FAST_1S_1S_1S_OP(0, 1, NULL, 0, 0),
SPINAND_PAGE_READ_FROM_CACHE_1S_1S_1S_OP(0, 1, NULL, 0, 0));

+static SPINAND_OP_VARIANTS(cont_read_cache_variants,
+ WINBOND_CONT_READ_FROM_CACHE_1S_4S_4S_OP(6, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_4S_OP(4, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_2S_2S_OP(4, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_1S_1S_2S_OP(4, NULL, 0, 0),
+ WINBOND_CONT_READ_FROM_CACHE_FAST_1S_1S_1S_OP(4, NULL, 0, 0));
+
static SPINAND_OP_VARIANTS(write_cache_variants,
SPINAND_PROG_LOAD_1S_1S_4S_OP(true, 0, NULL, 0),
SPINAND_PROG_LOAD_1S_1S_1S_OP(true, 0, NULL, 0));
@@ -365,6 +372,39 @@ static const struct mtd_ooblayout_ops w25n02kv_ooblayout = {
.free = w25n02kv_ooblayout_free,
};

+static int w25n08lw_ooblayout_ecc(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ /*
+ * With ecc enabled the parity bits are not accessible. So we can
+ * only see page + 128. Without ecc the full page + 256 is accessible.
+ * To make it simple just return the area as not accessible.
+ */
+ return -ERANGE;
+}
+
+static int w25n08lw_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
+{
+ if (section > 7)
+ return -ERANGE;
+
+ /*
+ * Only expose the ECC protected "User Data I" bytes. The "User Data II"
+ * bytes at offset + 2 are not covered by the on-die ECC engine, and the
+ * first two bytes of the section hold the BBM.
+ */
+ region->offset = (16 * section) + 4;
+ region->length = 12;
+
+ return 0;
+}
+
+static const struct mtd_ooblayout_ops w25n08lw_ooblayout = {
+ .ecc = w25n08lw_ooblayout_ecc,
+ .free = w25n08lw_ooblayout_free,
+};
+
static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
{
@@ -810,6 +850,18 @@ static const struct spinand_info winbond_spinand_table[] = {
SPINAND_ECCINFO(&w35n01jw_ooblayout, w25w35nxxjw_ecc_get_status),
SPINAND_CONFIGURE_CHIP(w35n0xjw_vcr_cfg),
SPINAND_CONT_READ(w35n0xjw_set_cont_read)),
+ /* 8G-bit densities */
+ SPINAND_INFO("W25N08LW", /* 2x4G-bit 1.8V */
+ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xb3, 0x24),
+ NAND_MEMORG(1, 4096, 256, 64, 2048, 40, 1, 2, 1),
+ NAND_ECCREQ(8, 512),
+ SPINAND_INFO_OP_VARIANTS_WITH_CONT(&read_cache_variants,
+ &write_cache_variants,
+ &update_cache_variants,
+ &cont_read_cache_variants),
+ 0,
+ SPINAND_ECCINFO(&w25n08lw_ooblayout, w25n02kv_ecc_get_status),
+ SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
};

static int winbond_spinand_init(struct spinand_device *spinand)

---
base-commit: 7e874b1750a40f3dc9a629aeb72eba09c77f77e9
change-id: 20260831-mtd-nand-new-chip-support-44b10657243f
--

Thanks!
- Nuno Sá