|
@@ -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,
|
|
|
|
|
|
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,
|
|
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,
|
|
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,
|
|
nand_wait_ready(mtd);
|
|
nand_wait_ready(mtd);
|
|
}
|
|
}
|
|
|
|
|
|
- if (mtd->ecc_stats.failed - ecc_failures)
|
|
|
|
- ecc_fail = true;
|
|
|
|
|
|
+ if (mtd->ecc_stats.failed - ecc_failures) {
|
|
|
|
+ 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,
|
|
|
|
|
|
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;
|
|
|
|
|