[openwrt/openwrt] kernel: mtd: spinand: continuous mode fixes
LEDE Commits
lede-commits at lists.infradead.org
Thu Oct 9 07:37:34 PDT 2025
robimarko pushed a commit to openwrt/openwrt.git, branch main:
https://git.openwrt.org/32229a4bb7d2a8dd2c3a719c9f4e0e5fa08bbd33
commit 32229a4bb7d2a8dd2c3a719c9f4e0e5fa08bbd33
Author: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
AuthorDate: Sat Oct 4 09:17:33 2025 +0300
kernel: mtd: spinand: continuous mode fixes
Continuous reading mode is broken for some spi controllers. There are two
possible bug scenarios:
1) "continuous mode" flash and spi controller without dirmap support,
but with restriction on transfer length in adjust_op_size()
2) "continuous mode" flash and spi controller with dirmap support for a
single flash page
In the first case, any read that exceeds the limit specified in adjust_op_size()
will result in an EIO error. The limit may even be less than a size of a single
flash page. In this case, any read will result in an error.
In the second case, any read larger than flash page size will result in an EIO
error or spinand driver spoofing (because the spi controller driver returns
more bytes than were actually read).
This patch series tries to fix continuous reading (spinand driver side).
Unfortunately these fixes can't resolve "spinand driver spoofing" case.
Spi controller drivers might need fixes as well.
Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
Link: https://github.com/openwrt/openwrt/pull/20295
Signed-off-by: Robert Marko <robimarko at gmail.com>
---
.../patches-6.12/901-snand-mtk-bmt-support.patch | 6 +-
...spinand-fix-direct-mapping-creation-sizes.patch | 62 ++++++++++++++
...and-try-a-regular-dirmap-if-creating-a-di.patch | 97 ++++++++++++++++++++++
...and-repeat-reading-in-regular-mode-if-con.patch | 63 ++++++++++++++
...spinand-Add-support-for-Etron-EM73D044VCx.patch | 2 +-
.../patches-6.12/121-hack-spi-nand-1b-bbm.patch | 4 +-
.../patches-6.12/330-snand-mtk-bmt-support.patch | 6 +-
...nand-Add-support-for-the-Fidelix-FM35X1GA.patch | 2 +-
...-spinand-Add-calibration-support-for-spin.patch | 4 +-
...-spi-nor-Add-calibration-support-for-spi-.patch | 2 +-
.../960-asus-hack-u-boot-ignore-mtdparts.patch | 4 +-
11 files changed, 237 insertions(+), 15 deletions(-)
diff --git a/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch b/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch
index f715652319..eb77fde066 100644
--- a/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch
+++ b/target/linux/airoha/patches-6.12/901-snand-mtk-bmt-support.patch
@@ -8,7 +8,7 @@
static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
{
-@@ -1530,6 +1531,7 @@ static int spinand_probe(struct spi_mem
+@@ -1574,6 +1575,7 @@ static int spinand_probe(struct spi_mem
if (ret)
return ret;
@@ -16,7 +16,7 @@
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
goto err_spinand_cleanup;
-@@ -1537,6 +1539,7 @@ static int spinand_probe(struct spi_mem
+@@ -1581,6 +1583,7 @@ static int spinand_probe(struct spi_mem
return 0;
err_spinand_cleanup:
@@ -24,7 +24,7 @@
spinand_cleanup(spinand);
return ret;
-@@ -1555,6 +1558,7 @@ static int spinand_remove(struct spi_mem
+@@ -1599,6 +1602,7 @@ static int spinand_remove(struct spi_mem
if (ret)
return ret;
diff --git a/target/linux/generic/pending-6.12/405-01-mtd-spinand-fix-direct-mapping-creation-sizes.patch b/target/linux/generic/pending-6.12/405-01-mtd-spinand-fix-direct-mapping-creation-sizes.patch
new file mode 100644
index 0000000000..3cb7844800
--- /dev/null
+++ b/target/linux/generic/pending-6.12/405-01-mtd-spinand-fix-direct-mapping-creation-sizes.patch
@@ -0,0 +1,62 @@
+From 5eea4228aaa3c5aeba843d8ea2a60993c268419d Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+Date: Sun, 3 Aug 2025 17:23:27 +0300
+Subject: [PATCH RESEND v5 1/3] mtd: spinand: fix direct mapping creation sizes
+
+Continuous mode is only supported for data reads, thus writing
+requires only single flash page mapping.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+---
+ drivers/mtd/nand/spi/core.c | 14 +++++++-------
+ 1 file changed, 7 insertions(+), 7 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1028,18 +1028,13 @@ static int spinand_create_dirmap(struct
+ unsigned int plane)
+ {
+ struct nand_device *nand = spinand_to_nand(spinand);
+- struct spi_mem_dirmap_info info = {
+- .length = nanddev_page_size(nand) +
+- nanddev_per_page_oobsize(nand),
+- };
++ struct spi_mem_dirmap_info info = { 0 };
+ struct spi_mem_dirmap_desc *desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+-
+ /* The plane number is passed in MSB just above the column address */
+ info.offset = plane << fls(nand->memorg.pagesize);
+
++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
+ info.op_tmpl = *spinand->op_templates.update_cache;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+@@ -1048,6 +1043,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc = desc;
+
++ if (spinand->cont_read_possible)
++ info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+ spinand->spimem, &info);
+@@ -1063,6 +1060,7 @@ static int spinand_create_dirmap(struct
+ return 0;
+ }
+
++ info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
+ info.op_tmpl = *spinand->op_templates.update_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+@@ -1072,6 +1070,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc_ecc = desc;
+
++ if (spinand->cont_read_possible)
++ info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl.data.ecc = true;
+ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
diff --git a/target/linux/generic/pending-6.12/405-02-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch b/target/linux/generic/pending-6.12/405-02-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch
new file mode 100644
index 0000000000..5c54a9116c
--- /dev/null
+++ b/target/linux/generic/pending-6.12/405-02-mtd-spinand-try-a-regular-dirmap-if-creating-a-di.patch
@@ -0,0 +1,97 @@
+From 9d13b7857de8834c6246fa5bc83f767675360240 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+Date: Sun, 3 Aug 2025 19:06:40 +0300
+Subject: [PATCH RESEND v5 2/3] mtd: spinand: try a regular dirmap if creating
+ a dirmap for continuous reading fails
+
+Continuous reading may result in multiple flash pages reading in one
+operation. Typically only one flash page has read/written (a little bit
+more than 2-4 Kb), but continuous reading requires the spi controller
+to read up to 512 Kb in one operation without toggling CS in beetween.
+
+Roughly speaking spi controllers can be divided on 2 categories:
+ * spi controllers without dirmap acceleration support
+ * spi controllers with dirmap acceleration support
+
+Firt of them will have issues with continuous reading if restriction on
+the transfer length is implemented in the adjust_op_size() handler.
+Second group often supports acceleration of single page only reading.
+Thus enabling of continuous reading can break flash reading.
+
+This patch tries to create dirmap for continuous reading first and
+fallback to regular reading if spi controller refuses to create it.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+---
+ drivers/mtd/nand/spi/core.c | 43 ++++++++++++++++++++++++++++++-------
+ 1 file changed, 35 insertions(+), 8 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -1024,6 +1024,39 @@ static int spinand_mtd_block_isreserved(
+ return ret;
+ }
+
++static struct spi_mem_dirmap_desc *spinand_create_rdesc(
++ struct spinand_device *spinand,
++ struct spi_mem_dirmap_info *info)
++{
++ struct nand_device *nand = spinand_to_nand(spinand);
++ struct spi_mem_dirmap_desc *desc = NULL;
++
++ if (spinand->cont_read_possible) {
++ /*
++ * spi controller may return an error if info->length is
++ * too large
++ */
++ info->length = nanddev_eraseblock_size(nand);
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, info);
++ }
++
++ if (IS_ERR_OR_NULL(desc)) {
++ /*
++ * continuous reading is not supported by flash or
++ * its spi controller, use regular reading
++ */
++ spinand->cont_read_possible = false;
++
++ info->length = nanddev_page_size(nand) +
++ nanddev_per_page_oobsize(nand);
++ desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
++ spinand->spimem, info);
++ }
++
++ return desc;
++}
++
+ static int spinand_create_dirmap(struct spinand_device *spinand,
+ unsigned int plane)
+ {
+@@ -1043,11 +1076,8 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc = desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+- spinand->spimem, &info);
++ desc = spinand_create_rdesc(spinand, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
+@@ -1070,12 +1100,9 @@ static int spinand_create_dirmap(struct
+
+ spinand->dirmaps[plane].wdesc_ecc = desc;
+
+- if (spinand->cont_read_possible)
+- info.length = nanddev_eraseblock_size(nand);
+ info.op_tmpl = *spinand->op_templates.read_cache;
+ info.op_tmpl.data.ecc = true;
+- desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
+- spinand->spimem, &info);
++ desc = spinand_create_rdesc(spinand, &info);
+ if (IS_ERR(desc))
+ return PTR_ERR(desc);
+
diff --git a/target/linux/generic/pending-6.12/405-03-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch b/target/linux/generic/pending-6.12/405-03-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch
new file mode 100644
index 0000000000..ad13fbafc3
--- /dev/null
+++ b/target/linux/generic/pending-6.12/405-03-mtd-spinand-repeat-reading-in-regular-mode-if-con.patch
@@ -0,0 +1,63 @@
+From 6ffbbe473b98e3944d4cb9379bf725b374e587a9 Mon Sep 17 00:00:00 2001
+From: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+Date: Sun, 3 Aug 2025 17:54:17 +0300
+Subject: [PATCH RESEND v5 3/3] mtd: spinand: repeat reading in regular mode if
+ continuous reading fails
+
+Continuous reading may result in multiple flash pages reading in one
+operation. Unfortunately, not all spinand controllers support such
+large reading. They will read less data. Unfortunately, the operation
+can't be continued.
+
+In this case:
+ * disable continuous reading on this (not good enough) spi controller
+ * repeat reading in regular mode.
+
+Signed-off-by: Mikhail Kshevetskiy <mikhail.kshevetskiy at iopsys.eu>
+---
+ drivers/mtd/nand/spi/core.c | 25 +++++++++++++++++++++----
+ 1 file changed, 21 insertions(+), 4 deletions(-)
+
+--- a/drivers/mtd/nand/spi/core.c
++++ b/drivers/mtd/nand/spi/core.c
+@@ -427,8 +427,16 @@ static int spinand_read_from_cache_op(st
+ * Dirmap accesses are allowed to toggle the CS.
+ * Toggling the CS during a continuous read is forbidden.
+ */
+- if (nbytes && req->continuous)
+- return -EIO;
++ if (nbytes && req->continuous) {
++ /*
++ * Spi controller with broken support of continuous
++ * reading was detected. Disable future use of
++ * continuous reading and return -EAGAIN to retry
++ * reading within regular mode.
++ */
++ spinand->cont_read_possible = false;
++ return -EAGAIN;
++ }
+ }
+
+ if (req->datalen)
+@@ -841,10 +849,19 @@ static int spinand_mtd_read(struct mtd_i
+
+ old_stats = mtd->ecc_stats;
+
+- if (spinand_use_cont_read(mtd, from, ops))
++ if (spinand_use_cont_read(mtd, from, ops)) {
+ ret = spinand_mtd_continuous_page_read(mtd, from, ops, &max_bitflips);
+- else
++ if (ret == -EAGAIN && !spinand->cont_read_possible) {
++ /*
++ * Spi controller with broken support of continuous
++ * reading was detected (see spinand_read_from_cache_op()),
++ * repeat reading in regular mode.
++ */
++ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
++ }
++ } else {
+ ret = spinand_mtd_regular_page_read(mtd, from, ops, &max_bitflips);
++ }
+
+ if (ops->stats) {
+ ops->stats->uncorrectable_errors +=
diff --git a/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch b/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
index df22fda59c..34fb47a5fb 100644
--- a/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
+++ b/target/linux/generic/pending-6.12/487-mtd-spinand-Add-support-for-Etron-EM73D044VCx.patch
@@ -49,7 +49,7 @@ Submitted-by: Daniel Danzberger <daniel at dd-wrt.com>
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -1115,6 +1115,7 @@ static const struct spinand_manufacturer
+@@ -1159,6 +1159,7 @@ static const struct spinand_manufacturer
&alliancememory_spinand_manufacturer,
&ato_spinand_manufacturer,
&esmt_c8_spinand_manufacturer,
diff --git a/target/linux/mediatek/patches-6.12/121-hack-spi-nand-1b-bbm.patch b/target/linux/mediatek/patches-6.12/121-hack-spi-nand-1b-bbm.patch
index df29ec91d1..cc2410e352 100644
--- a/target/linux/mediatek/patches-6.12/121-hack-spi-nand-1b-bbm.patch
+++ b/target/linux/mediatek/patches-6.12/121-hack-spi-nand-1b-bbm.patch
@@ -1,6 +1,6 @@
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -896,7 +896,7 @@ static int spinand_mtd_write(struct mtd_
+@@ -913,7 +913,7 @@ static int spinand_mtd_write(struct mtd_
static bool spinand_isbad(struct nand_device *nand, const struct nand_pos *pos)
{
struct spinand_device *spinand = nand_to_spinand(nand);
@@ -9,7 +9,7 @@
struct nand_page_io_req req = {
.pos = *pos,
.ooblen = sizeof(marker),
-@@ -907,7 +907,7 @@ static bool spinand_isbad(struct nand_de
+@@ -924,7 +924,7 @@ static bool spinand_isbad(struct nand_de
spinand_select_target(spinand, pos->target);
spinand_read_page(spinand, &req);
diff --git a/target/linux/mediatek/patches-6.12/330-snand-mtk-bmt-support.patch b/target/linux/mediatek/patches-6.12/330-snand-mtk-bmt-support.patch
index f715652319..eb77fde066 100644
--- a/target/linux/mediatek/patches-6.12/330-snand-mtk-bmt-support.patch
+++ b/target/linux/mediatek/patches-6.12/330-snand-mtk-bmt-support.patch
@@ -8,7 +8,7 @@
static int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val)
{
-@@ -1530,6 +1531,7 @@ static int spinand_probe(struct spi_mem
+@@ -1574,6 +1575,7 @@ static int spinand_probe(struct spi_mem
if (ret)
return ret;
@@ -16,7 +16,7 @@
ret = mtd_device_register(mtd, NULL, 0);
if (ret)
goto err_spinand_cleanup;
-@@ -1537,6 +1539,7 @@ static int spinand_probe(struct spi_mem
+@@ -1581,6 +1583,7 @@ static int spinand_probe(struct spi_mem
return 0;
err_spinand_cleanup:
@@ -24,7 +24,7 @@
spinand_cleanup(spinand);
return ret;
-@@ -1555,6 +1558,7 @@ static int spinand_remove(struct spi_mem
+@@ -1599,6 +1602,7 @@ static int spinand_remove(struct spi_mem
if (ret)
return ret;
diff --git a/target/linux/mediatek/patches-6.12/340-mtd-spinand-Add-support-for-the-Fidelix-FM35X1GA.patch b/target/linux/mediatek/patches-6.12/340-mtd-spinand-Add-support-for-the-Fidelix-FM35X1GA.patch
index a7dc5105f5..7857f4c6aa 100644
--- a/target/linux/mediatek/patches-6.12/340-mtd-spinand-Add-support-for-the-Fidelix-FM35X1GA.patch
+++ b/target/linux/mediatek/patches-6.12/340-mtd-spinand-Add-support-for-the-Fidelix-FM35X1GA.patch
@@ -24,7 +24,7 @@ Signed-off-by: Davide Fioravanti <pantanastyle at gmail.com>
obj-$(CONFIG_MTD_SPI_NAND) += spinand.o
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -1118,6 +1118,7 @@ static const struct spinand_manufacturer
+@@ -1162,6 +1162,7 @@ static const struct spinand_manufacturer
&esmt_c8_spinand_manufacturer,
&etron_spinand_manufacturer,
&fmsh_spinand_manufacturer,
diff --git a/target/linux/mediatek/patches-6.12/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch b/target/linux/mediatek/patches-6.12/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch
index 676ddec61e..258b8e19a3 100644
--- a/target/linux/mediatek/patches-6.12/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch
+++ b/target/linux/mediatek/patches-6.12/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch
@@ -11,7 +11,7 @@ Signed-off-by: SkyLake.Huang <skylake.huang at mediatek.com>
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -1156,6 +1156,56 @@ static int spinand_manufacturer_match(st
+@@ -1200,6 +1200,56 @@ static int spinand_manufacturer_match(st
return -EOPNOTSUPP;
}
@@ -68,7 +68,7 @@ Signed-off-by: SkyLake.Huang <skylake.huang at mediatek.com>
static int spinand_id_detect(struct spinand_device *spinand)
{
u8 *id = spinand->id.data;
-@@ -1407,6 +1457,10 @@ static int spinand_init(struct spinand_d
+@@ -1451,6 +1501,10 @@ static int spinand_init(struct spinand_d
if (!spinand->scratchbuf)
return -ENOMEM;
diff --git a/target/linux/mediatek/patches-6.12/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch b/target/linux/mediatek/patches-6.12/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch
index 4626a86e99..b55f76eed7 100644
--- a/target/linux/mediatek/patches-6.12/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch
+++ b/target/linux/mediatek/patches-6.12/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch
@@ -12,7 +12,7 @@ Signed-off-by: SkyLake.Huang <skylake.huang at mediatek.com>
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -1197,7 +1197,10 @@ static int spinand_cal_read(void *priv,
+@@ -1241,7 +1241,10 @@ static int spinand_cal_read(void *priv,
if (ret)
return ret;
diff --git a/target/linux/mediatek/patches-6.12/960-asus-hack-u-boot-ignore-mtdparts.patch b/target/linux/mediatek/patches-6.12/960-asus-hack-u-boot-ignore-mtdparts.patch
index 07d067125a..8d1680d5d4 100644
--- a/target/linux/mediatek/patches-6.12/960-asus-hack-u-boot-ignore-mtdparts.patch
+++ b/target/linux/mediatek/patches-6.12/960-asus-hack-u-boot-ignore-mtdparts.patch
@@ -29,7 +29,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
-@@ -1624,6 +1624,7 @@ static int spinand_remove(struct spi_mem
+@@ -1668,6 +1668,7 @@ static int spinand_remove(struct spi_mem
static const struct spi_device_id spinand_ids[] = {
{ .name = "spi-nand" },
@@ -37,7 +37,7 @@ Signed-off-by: Daniel Golle <daniel at makrotopia.org>
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(spi, spinand_ids);
-@@ -1631,6 +1632,7 @@ MODULE_DEVICE_TABLE(spi, spinand_ids);
+@@ -1675,6 +1676,7 @@ MODULE_DEVICE_TABLE(spi, spinand_ids);
#ifdef CONFIG_OF
static const struct of_device_id spinand_of_ids[] = {
{ .compatible = "spi-nand" },
More information about the lede-commits
mailing list