summaryrefslogtreecommitdiff
path: root/drivers/mtd/nand
diff options
context:
space:
mode:
authorMiquel Raynal <miquel.raynal@bootlin.com>2026-05-04 15:14:36 +0200
committerMiquel Raynal <miquel.raynal@bootlin.com>2026-05-04 15:14:36 +0200
commit7a0d420e42a5f0e8451d142cf1154dda7183e146 (patch)
tree7b24f86be388a476b38c574e0c61f037208fecee /drivers/mtd/nand
parent7fd2df204f342fc17d1a0bfcd474b24232fb0f32 (diff)
parent38fbe4b3f66e5b8e2f2ab8e7ca3d912e1e935fe2 (diff)
Merge tag 'mtd/spi-mem-cont-read-for-7.2' into nand/next
Aside from preparation changes in the SPI NAND core, the changes carried here focus on the shared spi-mem layer which is enhanced in order to bring two new features: - The possibility to fill a primary and a secondary operation template in the direct mapping structure in order to support continuous reads in SPI NAND, which may require two different read operations. - SPI controllers may indicate possible CS instabilities over long transfers by setting a boolean. This capability is related to the previous one, the need for it has arised while testing SPI NAND continuous reads with the Cadence QSPI controller which cannot, under certain conditions, keep the CS asserted for the length of an eraseblock-large transfer.
Diffstat (limited to 'drivers/mtd/nand')
-rw-r--r--drivers/mtd/nand/spi/core.c76
1 files changed, 29 insertions, 47 deletions
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index 0b076790bd9d..786e3fb4874d 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -501,10 +501,13 @@ static int spinand_read_from_cache_op(struct spinand_device *spinand,
}
}
- if (req->mode == MTD_OPS_RAW)
- rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+ rdesc = spinand->dirmaps[req->pos.plane].rdesc;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
+ req->mode != MTD_OPS_RAW)
+ rdesc->info.op_tmpl->data.ecc = true;
else
- rdesc = spinand->dirmaps[req->pos.plane].rdesc_ecc;
+ rdesc->info.op_tmpl->data.ecc = false;
if (spinand->flags & SPINAND_HAS_READ_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -593,10 +596,13 @@ static int spinand_write_to_cache_op(struct spinand_device *spinand,
req->ooblen);
}
- if (req->mode == MTD_OPS_RAW)
- wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+ wdesc = spinand->dirmaps[req->pos.plane].wdesc;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED &&
+ req->mode != MTD_OPS_RAW)
+ wdesc->info.op_tmpl->data.ecc = true;
else
- wdesc = spinand->dirmaps[req->pos.plane].wdesc_ecc;
+ wdesc->info.op_tmpl->data.ecc = false;
if (spinand->flags & SPINAND_HAS_PROG_PLANE_SELECT_BIT)
column |= req->pos.plane << fls(nanddev_page_size(nand));
@@ -892,6 +898,12 @@ static int spinand_mtd_continuous_page_read(struct mtd_info *mtd, loff_t from,
* Each data read must be a multiple of 4-bytes and full pages should be read;
* otherwise, the data output might get out of sequence from one read command
* to another.
+ *
+ * Continuous reads never cross LUN boundaries. Some devices don't
+ * support crossing planes boundaries. Some devices don't even support
+ * crossing blocks boundaries. The common case being to read through UBI,
+ * we will very rarely read two consequent blocks or more, so let's only enable
+ * continuous reads when reading within the same erase block.
*/
nanddev_io_for_each_block(nand, NAND_PAGE_READ, from, ops, &iter) {
ret = spinand_select_target(spinand, iter.req.pos.target);
@@ -982,19 +994,6 @@ static bool spinand_use_cont_read(struct mtd_info *mtd, loff_t from,
nanddev_offs_to_pos(nand, from, &start_pos);
nanddev_offs_to_pos(nand, from + ops->len - 1, &end_pos);
- /*
- * Continuous reads never cross LUN boundaries. Some devices don't
- * support crossing planes boundaries. Some devices don't even support
- * crossing blocks boundaries. The common case being to read through UBI,
- * we will very rarely read two consequent blocks or more, so it is safer
- * and easier (can be improved) to only enable continuous reads when
- * reading within the same erase block.
- */
- if (start_pos.target != end_pos.target ||
- start_pos.plane != end_pos.plane ||
- start_pos.eraseblock != end_pos.eraseblock)
- return false;
-
return start_pos.page < end_pos.page;
}
@@ -1252,12 +1251,18 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
struct nand_device *nand = spinand_to_nand(spinand);
struct spi_mem_dirmap_info info = { 0 };
struct spi_mem_dirmap_desc *desc;
+ bool enable_ecc = false;
+
+ if (nand->ecc.engine->integration == NAND_ECC_ENGINE_INTEGRATION_PIPELINED)
+ enable_ecc = true;
/* The plane number is passed in MSB just above the column address */
info.offset = plane << fls(nand->memorg.pagesize);
+ /* Write descriptor */
info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand);
- info.op_tmpl = *spinand->op_templates->update_cache;
+ info.primary_op_tmpl = *spinand->op_templates->update_cache;
+ info.primary_op_tmpl.data.ecc = enable_ecc;
desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev,
spinand->spimem, &info);
if (IS_ERR(desc))
@@ -1265,38 +1270,15 @@ static int spinand_create_dirmap(struct spinand_device *spinand,
spinand->dirmaps[plane].wdesc = desc;
- info.op_tmpl = *spinand->op_templates->read_cache;
+ /* Read descriptor */
+ info.primary_op_tmpl = *spinand->op_templates->read_cache;
+ info.primary_op_tmpl.data.ecc = enable_ecc;
desc = spinand_create_rdesc(spinand, &info);
if (IS_ERR(desc))
return PTR_ERR(desc);
spinand->dirmaps[plane].rdesc = desc;
- if (nand->ecc.engine->integration != NAND_ECC_ENGINE_INTEGRATION_PIPELINED) {
- spinand->dirmaps[plane].wdesc_ecc = spinand->dirmaps[plane].wdesc;
- spinand->dirmaps[plane].rdesc_ecc = spinand->dirmaps[plane].rdesc;
-
- 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,
- spinand->spimem, &info);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
-
- spinand->dirmaps[plane].wdesc_ecc = desc;
-
- info.op_tmpl = *spinand->op_templates->read_cache;
- info.op_tmpl.data.ecc = true;
- desc = spinand_create_rdesc(spinand, &info);
- if (IS_ERR(desc))
- return PTR_ERR(desc);
-
- spinand->dirmaps[plane].rdesc_ecc = desc;
-
return 0;
}
@@ -1421,7 +1403,7 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand)
return spinand->manufacturer->ops->cleanup(spinand);
}
-static bool spinand_op_is_odtr(const struct spi_mem_op *op)
+bool spinand_op_is_odtr(const struct spi_mem_op *op)
{
return op->cmd.dtr && op->cmd.buswidth == 8;
}