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;
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:
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;
--- /dev/null
+From 5eea4228aaa3c5aeba843d8ea2a60993c268419d Mon Sep 17 00:00:00 2001
+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.
+
+---
+ 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,
--- /dev/null
+From 9d13b7857de8834c6246fa5bc83f767675360240 Mon Sep 17 00:00:00 2001
+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.
+
+---
+ 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);
+
--- /dev/null
+From 6ffbbe473b98e3944d4cb9379bf725b374e587a9 Mon Sep 17 00:00:00 2001
+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.
+
+---
+ 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 +=
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,
--- 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);
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);
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;
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:
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;
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,
--- 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;
}
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;
--- 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;
--- 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" },
{ /* 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" },