Commit ba84fb59 authored by Brian Norris's avatar Brian Norris

mtd: nand: add generic READ RETRY support

Modern MLC (and even SLC?) NAND can experience a large number of
bitflips (beyond the recommended correctability capacity) due to drifts
in the voltage threshold (Vt). These bitflips can cause ECC errors to
occur well within the expected lifetime of the flash. To account for
this, some manufacturers provide a mechanism for shifting the Vt
threshold after a corrupted read.

The generic pattern seems to be that a particular flash has N read retry
modes (where N = 0, traditionally), and after an ECC failure, the host
should reconfigure the flash to use the next available mode, then retry
the read operation. This process repeats until all bitfips can be
corrected or until the host has tried all available retry modes.

This patch adds the infrastructure support for a
vendor-specific/flash-specific callback, used for setting the read-retry
mode (i.e., voltage threshold).

For now, this patch always returns the flash to mode 0 (the default
mode) after a successful read-retry, according to the flowchart found in
Micron's datasheets. This may need to change in the future if it is
determined that eventually, mode 0 is insufficient for the majority of
the flash cells (and so for performance reasons, we should leave the
flash in mode 1, 2, etc.).
Signed-off-by: default avatarBrian Norris <computersforpeace@gmail.com>
Acked-by: default avatarHuang Shijie <b32955@freescale.com>
parent 6f0065b0
...@@ -1409,6 +1409,30 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob, ...@@ -1409,6 +1409,30 @@ static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
return NULL; return NULL;
} }
/**
* nand_setup_read_retry - [INTERN] Set the READ RETRY mode
* @mtd: MTD device structure
* @retry_mode: the retry mode to use
*
* Some vendors supply a special command to shift the Vt threshold, to be used
* when there are too many bitflips in a page (i.e., ECC error). After setting
* a new threshold, the host should retry reading the page.
*/
static int nand_setup_read_retry(struct mtd_info *mtd, int retry_mode)
{
struct nand_chip *chip = mtd->priv;
pr_debug("setting READ RETRY mode %d\n", retry_mode);
if (retry_mode >= chip->read_retries)
return -EINVAL;
if (!chip->setup_read_retry)
return -EOPNOTSUPP;
return chip->setup_read_retry(mtd, retry_mode);
}
/** /**
* nand_do_read_ops - [INTERN] Read data with ECC * nand_do_read_ops - [INTERN] Read data with ECC
* @mtd: MTD device structure * @mtd: MTD device structure
...@@ -1430,6 +1454,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1430,6 +1454,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
uint8_t *bufpoi, *oob, *buf; uint8_t *bufpoi, *oob, *buf;
unsigned int max_bitflips = 0; unsigned int max_bitflips = 0;
int retry_mode = 0;
bool ecc_fail = false; bool ecc_fail = false;
chipnr = (int)(from >> chip->chip_shift); chipnr = (int)(from >> chip->chip_shift);
...@@ -1454,6 +1479,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1454,6 +1479,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (realpage != chip->pagebuf || oob) { if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf; bufpoi = aligned ? buf : chip->buffers->databuf;
read_retry:
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
/* /*
...@@ -1494,8 +1520,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1494,8 +1520,6 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
} }
buf += bytes;
if (unlikely(oob)) { if (unlikely(oob)) {
int toread = min(oobreadlen, max_oobsize); int toread = min(oobreadlen, max_oobsize);
...@@ -1514,8 +1538,24 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1514,8 +1538,24 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
nand_wait_ready(mtd); nand_wait_ready(mtd);
} }
if (mtd->ecc_stats.failed - ecc_failures) if (mtd->ecc_stats.failed - ecc_failures) {
ecc_fail = true; if (retry_mode + 1 <= chip->read_retries) {
retry_mode++;
ret = nand_setup_read_retry(mtd,
retry_mode);
if (ret < 0)
break;
/* Reset failures; retry */
mtd->ecc_stats.failed = ecc_failures;
goto read_retry;
} else {
/* No more retry modes; real failure */
ecc_fail = true;
}
}
buf += bytes;
} else { } else {
memcpy(buf, chip->buffers->databuf + col, bytes); memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes; buf += bytes;
...@@ -1525,6 +1565,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, ...@@ -1525,6 +1565,14 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
readlen -= bytes; readlen -= bytes;
/* Reset to retry mode 0 */
if (retry_mode) {
ret = nand_setup_read_retry(mtd, 0);
if (ret < 0)
break;
retry_mode = 0;
}
if (!readlen) if (!readlen)
break; break;
......
...@@ -472,6 +472,8 @@ struct nand_buffers { ...@@ -472,6 +472,8 @@ struct nand_buffers {
* commands to the chip. * commands to the chip.
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on
* ready. * ready.
* @setup_read_retry: [FLASHSPECIFIC] flash (vendor) specific function for
* setting the read-retry mode. Mostly needed for MLC NAND.
* @ecc: [BOARDSPECIFIC] ECC control structure * @ecc: [BOARDSPECIFIC] ECC control structure
* @buffers: buffer structure for read/write * @buffers: buffer structure for read/write
* @hwcontrol: platform-specific hardware control structure * @hwcontrol: platform-specific hardware control structure
...@@ -518,6 +520,7 @@ struct nand_buffers { ...@@ -518,6 +520,7 @@ struct nand_buffers {
* non 0 if ONFI supported. * non 0 if ONFI supported.
* @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is * @onfi_params: [INTERN] holds the ONFI page parameter when ONFI is
* supported, 0 otherwise. * supported, 0 otherwise.
* @read_retries: [INTERN] the number of read retry modes supported
* @onfi_set_features: [REPLACEABLE] set the features for ONFI nand * @onfi_set_features: [REPLACEABLE] set the features for ONFI nand
* @onfi_get_features: [REPLACEABLE] get the features for ONFI nand * @onfi_get_features: [REPLACEABLE] get the features for ONFI nand
* @bbt: [INTERN] bad block table pointer * @bbt: [INTERN] bad block table pointer
...@@ -565,6 +568,7 @@ struct nand_chip { ...@@ -565,6 +568,7 @@ struct nand_chip {
int feature_addr, uint8_t *subfeature_para); int feature_addr, uint8_t *subfeature_para);
int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip, int (*onfi_get_features)(struct mtd_info *mtd, struct nand_chip *chip,
int feature_addr, uint8_t *subfeature_para); int feature_addr, uint8_t *subfeature_para);
int (*setup_read_retry)(struct mtd_info *mtd, int retry_mode);
int chip_delay; int chip_delay;
unsigned int options; unsigned int options;
...@@ -589,6 +593,8 @@ struct nand_chip { ...@@ -589,6 +593,8 @@ struct nand_chip {
int onfi_version; int onfi_version;
struct nand_onfi_params onfi_params; struct nand_onfi_params onfi_params;
int read_retries;
flstate_t state; flstate_t state;
uint8_t *oob_poi; uint8_t *oob_poi;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment