[PATCH 03/13] mtd: spinand: winbond: Generalize the LW OOB layout

From: Miquel Raynal

Date: Fri Oct 30 2026 - 13:46:39 EST


All W25NxxLV and W25NxxLW chips use the same OOB layout. Rename the
helpers and structures to make it more obvious, and improve the comment
to reflect this situation.

Signed-off-by: Miquel Raynal <miquel.raynal@xxxxxxxxxxx>
---
drivers/mtd/nand/spi/winbond.c | 41 ++++++++++++++++++++++-------------------
1 file changed, 22 insertions(+), 19 deletions(-)

diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c
index 4ef30f1a4aa8..e3be93690bb7 100644
--- a/drivers/mtd/nand/spi/winbond.c
+++ b/drivers/mtd/nand/spi/winbond.c
@@ -339,6 +339,8 @@ static int w25nxxkv_kw_ooblayout_ecc(struct mtd_info *mtd, int section,
* 12 ECC-protected "User Data I" bytes.
*
* This layout exposes both User Data I and II for backward compatibility.
+ * See w25nxxlv_lw_ooblayout_free() for a variant that only exposes the
+ * ECC-protected bytes.
*/
static int w25nxxkv_kw_ooblayout_free(struct mtd_info *mtd, int section,
struct mtd_oob_region *region)
@@ -364,37 +366,38 @@ static const struct mtd_ooblayout_ops w25nxxkv_kw_ooblayout = {
.free = w25nxxkv_kw_ooblayout_free,
};

-static int w25n08lw_ooblayout_ecc(struct mtd_info *mtd, int section,
- struct mtd_oob_region *region)
+/*
+ * The second half of the OOB is not accessible when the on-die ECC
+ * engine is enabled. This prevents accessing the ECC bytes.
+ */
+static int w25nxxlv_lw_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)
+/*
+ * Same layout as w25nxxkv_kw_ooblayout_free(), except that only the
+ * ECC-protected "User Data I" bytes are exposed. The non ECC-protected
+ * "User Data II" bytes are not exposed.
+ */
+static int w25nxxlv_lw_ooblayout_free(struct mtd_info *mtd, int section,
+ struct mtd_oob_region *region)
{
- if (section > 7)
+ unsigned int num_sections = mtd->writesize / 512;
+
+ if (section >= num_sections)
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 const struct mtd_ooblayout_ops w25nxxlv_lw_ooblayout = {
+ .ecc = w25nxxlv_lw_ooblayout_ecc,
+ .free = w25nxxlv_lw_ooblayout_free,
};

static int w25n01jw_ooblayout_ecc(struct mtd_info *mtd, int section,
@@ -852,7 +855,7 @@ static const struct spinand_info winbond_spinand_table[] = {
&update_cache_variants,
&cont_read_cache_variants),
0,
- SPINAND_ECCINFO(&w25n08lw_ooblayout, w25n02kv_ecc_get_status),
+ SPINAND_ECCINFO(&w25nxxlv_lw_ooblayout, w25n02kv_ecc_get_status),
SPINAND_CONT_READ(w25n0xjw_set_cont_read)),
};


--
2.56.0