summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorCheng Ming Lin <[email protected]>2025-09-30 03:21:01 +0300
committerMichael Trimarchi <[email protected]>2025-10-05 20:26:17 +0200
commit49b855662f7076d2eea3cd3e12a434f7be9a8449 (patch)
treed0329204d7c5bf10de7f8fd0623c9efa9db6ec2e /drivers
parent90772e45030ce3c92c079c94124399b33b7493fa (diff)
mtd: spinand: Add read retry support
When the host ECC fails to correct the data error of NAND device, there's a special read for data recovery method which can be setup by the host for the next read. There are several retry levels that can be attempted until the lost data is recovered or definitely assumed lost. This is the port of linux commit f2cb43c98010 (mtd: spinand: Add read retry support) Signed-off-by: Cheng Ming Lin <[email protected]> Signed-off-by: Miquel Raynal <[email protected]> Signed-off-by: Mikhail Kshevetskiy <[email protected]> # U-Boot port Reviewed-by: Frieder Schrempf <[email protected]> Signed-off-by: Michael Trimarchi <[email protected]>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mtd/nand/spi/core.c35
1 files changed, 33 insertions, 2 deletions
diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c
index d4777ec7c8f..33d0c27f788 100644
--- a/drivers/mtd/nand/spi/core.c
+++ b/drivers/mtd/nand/spi/core.c
@@ -687,11 +687,15 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
{
struct spinand_device *spinand = mtd_to_spinand(mtd);
struct nand_device *nand = mtd_to_nanddev(mtd);
+ struct mtd_ecc_stats old_stats;
struct nand_io_iter iter;
bool disable_ecc = false;
bool ecc_failed = false;
+ unsigned int retry_mode = 0;
int ret;
+ old_stats = mtd->ecc_stats;
+
if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout)
disable_ecc = true;
@@ -704,18 +708,43 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
if (ret)
break;
+read_retry:
ret = spinand_read_page(spinand, &iter.req);
if (ret < 0 && ret != -EBADMSG)
break;
- if (ret == -EBADMSG)
+ if (ret == -EBADMSG && spinand->set_read_retry) {
+ if (spinand->read_retries && (++retry_mode <= spinand->read_retries)) {
+ ret = spinand->set_read_retry(spinand, retry_mode);
+ if (ret < 0) {
+ spinand->set_read_retry(spinand, 0);
+ return ret;
+ }
+
+ /* Reset ecc_stats; retry */
+ mtd->ecc_stats = old_stats;
+ goto read_retry;
+ } else {
+ /* No more retry modes; real failure */
+ ecc_failed = true;
+ }
+ } else if (ret == -EBADMSG) {
ecc_failed = true;
- else
+ } else {
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
+ }
ret = 0;
ops->retlen += iter.req.datalen;
ops->oobretlen += iter.req.ooblen;
+
+ /* Reset to retry mode 0 */
+ if (retry_mode) {
+ retry_mode = 0;
+ ret = spinand->set_read_retry(spinand, retry_mode);
+ if (ret < 0)
+ return ret;
+ }
}
if (ecc_failed && !ret)
@@ -1309,6 +1338,8 @@ int spinand_match_and_init(struct spinand_device *spinand,
spinand->id.len = 1 + table[i].devid.len;
spinand->select_target = table[i].select_target;
spinand->set_cont_read = table[i].set_cont_read;
+ spinand->read_retries = table[i].read_retries;
+ spinand->set_read_retry = table[i].set_read_retry;
op = spinand_select_op_variant(spinand,
info->op_variants.read_cache);