Skip to content

Commit f2cb43c

Browse files
zmlin1998miquelraynal
authored andcommitted
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. Signed-off-by: Cheng Ming Lin <[email protected]> Signed-off-by: Miquel Raynal <[email protected]>
1 parent 34684bb commit f2cb43c

File tree

2 files changed

+48
-2
lines changed

2 files changed

+48
-2
lines changed

drivers/mtd/nand/spi/core.c

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,11 +700,15 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
700700
{
701701
struct spinand_device *spinand = mtd_to_spinand(mtd);
702702
struct nand_device *nand = mtd_to_nanddev(mtd);
703+
struct mtd_ecc_stats old_stats;
703704
struct nand_io_iter iter;
704705
bool disable_ecc = false;
705706
bool ecc_failed = false;
707+
unsigned int retry_mode = 0;
706708
int ret;
707709

710+
old_stats = mtd->ecc_stats;
711+
708712
if (ops->mode == MTD_OPS_RAW || !mtd->ooblayout)
709713
disable_ecc = true;
710714

@@ -716,18 +720,43 @@ static int spinand_mtd_regular_page_read(struct mtd_info *mtd, loff_t from,
716720
if (ret)
717721
break;
718722

723+
read_retry:
719724
ret = spinand_read_page(spinand, &iter.req);
720725
if (ret < 0 && ret != -EBADMSG)
721726
break;
722727

723-
if (ret == -EBADMSG)
728+
if (ret == -EBADMSG && spinand->set_read_retry) {
729+
if (spinand->read_retries && (++retry_mode <= spinand->read_retries)) {
730+
ret = spinand->set_read_retry(spinand, retry_mode);
731+
if (ret < 0) {
732+
spinand->set_read_retry(spinand, 0);
733+
return ret;
734+
}
735+
736+
/* Reset ecc_stats; retry */
737+
mtd->ecc_stats = old_stats;
738+
goto read_retry;
739+
} else {
740+
/* No more retry modes; real failure */
741+
ecc_failed = true;
742+
}
743+
} else if (ret == -EBADMSG) {
724744
ecc_failed = true;
725-
else
745+
} else {
726746
*max_bitflips = max_t(unsigned int, *max_bitflips, ret);
747+
}
727748

728749
ret = 0;
729750
ops->retlen += iter.req.datalen;
730751
ops->oobretlen += iter.req.ooblen;
752+
753+
/* Reset to retry mode 0 */
754+
if (retry_mode) {
755+
retry_mode = 0;
756+
ret = spinand->set_read_retry(spinand, retry_mode);
757+
if (ret < 0)
758+
return ret;
759+
}
731760
}
732761

733762
if (ecc_failed && !ret)
@@ -1320,6 +1349,8 @@ int spinand_match_and_init(struct spinand_device *spinand,
13201349
spinand->set_cont_read = table[i].set_cont_read;
13211350
spinand->fact_otp = &table[i].fact_otp;
13221351
spinand->user_otp = &table[i].user_otp;
1352+
spinand->read_retries = table[i].read_retries;
1353+
spinand->set_read_retry = table[i].set_read_retry;
13231354

13241355
op = spinand_select_op_variant(spinand,
13251356
info->op_variants.read_cache);

include/linux/mtd/spinand.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ struct spinand_user_otp {
452452
* @set_cont_read: enable/disable continuous cached reads
453453
* @fact_otp: SPI NAND factory OTP info.
454454
* @user_otp: SPI NAND user OTP info.
455+
* @read_retries: the number of read retry modes supported
456+
* @set_read_retry: enable/disable read retry for data recovery
455457
*
456458
* Each SPI NAND manufacturer driver should have a spinand_info table
457459
* describing all the chips supported by the driver.
@@ -474,6 +476,9 @@ struct spinand_info {
474476
bool enable);
475477
struct spinand_fact_otp fact_otp;
476478
struct spinand_user_otp user_otp;
479+
unsigned int read_retries;
480+
int (*set_read_retry)(struct spinand_device *spinand,
481+
unsigned int read_retry);
477482
};
478483

479484
#define SPINAND_ID(__method, ...) \
@@ -520,6 +525,10 @@ struct spinand_info {
520525
.ops = __ops, \
521526
}
522527

528+
#define SPINAND_READ_RETRY(__read_retries, __set_read_retry) \
529+
.read_retries = __read_retries, \
530+
.set_read_retry = __set_read_retry,
531+
523532
#define SPINAND_INFO(__model, __id, __memorg, __eccreq, __op_variants, \
524533
__flags, ...) \
525534
{ \
@@ -572,6 +581,8 @@ struct spinand_dirmap {
572581
* @priv: manufacturer private data
573582
* @fact_otp: SPI NAND factory OTP info.
574583
* @user_otp: SPI NAND user OTP info.
584+
* @read_retries: the number of read retry modes supported
585+
* @set_read_retry: Enable/disable the read retry feature
575586
*/
576587
struct spinand_device {
577588
struct nand_device base;
@@ -607,6 +618,10 @@ struct spinand_device {
607618

608619
const struct spinand_fact_otp *fact_otp;
609620
const struct spinand_user_otp *user_otp;
621+
622+
unsigned int read_retries;
623+
int (*set_read_retry)(struct spinand_device *spinand,
624+
unsigned int retry_mode);
610625
};
611626

612627
/**

0 commit comments

Comments
 (0)