diff options
| -rw-r--r-- | Documentation/ABI/testing/sysfs-class-mtd | 7 | ||||
| -rw-r--r-- | drivers/mtd/devices/docg3.c | 6 | ||||
| -rw-r--r-- | drivers/mtd/mtdcore.c | 14 | ||||
| -rw-r--r-- | drivers/mtd/mtdpart.c | 12 | ||||
| -rw-r--r-- | drivers/mtd/nand/alauda.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/nand/nand_base.c | 18 | ||||
| -rw-r--r-- | drivers/mtd/onenand/onenand_base.c | 6 | ||||
| -rw-r--r-- | include/linux/mtd/nand.h | 3 |
8 files changed, 51 insertions, 19 deletions
diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd index 78835080856a..db1ad7e34fc3 100644 --- a/Documentation/ABI/testing/sysfs-class-mtd +++ b/Documentation/ABI/testing/sysfs-class-mtd | |||
| @@ -167,7 +167,10 @@ Description: | |||
| 167 | block degradation, but high enough to avoid the consequences of | 167 | block degradation, but high enough to avoid the consequences of |
| 168 | a persistent return value of -EUCLEAN on devices where sticky | 168 | a persistent return value of -EUCLEAN on devices where sticky |
| 169 | bitflips occur. Note that if bitflip_threshold exceeds | 169 | bitflips occur. Note that if bitflip_threshold exceeds |
| 170 | ecc_strength, -EUCLEAN is never returned by the read functions. | 170 | ecc_strength, -EUCLEAN is never returned by mtd_read(). |
| 171 | Conversely, if bitflip_threshold is zero, -EUCLEAN is always | ||
| 172 | returned, absent a hard error. | ||
| 171 | 173 | ||
| 172 | This is generally applicable only to NAND flash devices with ECC | 174 | This is generally applicable only to NAND flash devices with ECC |
| 173 | capability. It is ignored on devices lacking ECC capability. | 175 | capability. It is ignored on devices lacking ECC capability; |
| 176 | i.e., devices for which ecc_strength is zero. | ||
diff --git a/drivers/mtd/devices/docg3.c b/drivers/mtd/devices/docg3.c index 8272c02668d6..65d22a0439c6 100644 --- a/drivers/mtd/devices/docg3.c +++ b/drivers/mtd/devices/docg3.c | |||
| @@ -850,6 +850,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 850 | u8 *buf = ops->datbuf; | 850 | u8 *buf = ops->datbuf; |
| 851 | size_t len, ooblen, nbdata, nboob; | 851 | size_t len, ooblen, nbdata, nboob; |
| 852 | u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; | 852 | u8 hwecc[DOC_ECC_BCH_SIZE], eccconf1; |
| 853 | int max_bitflips = 0; | ||
| 853 | 854 | ||
| 854 | if (buf) | 855 | if (buf) |
| 855 | len = ops->len; | 856 | len = ops->len; |
| @@ -876,7 +877,7 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 876 | ret = 0; | 877 | ret = 0; |
| 877 | skip = from % DOC_LAYOUT_PAGE_SIZE; | 878 | skip = from % DOC_LAYOUT_PAGE_SIZE; |
| 878 | mutex_lock(&docg3->cascade->lock); | 879 | mutex_lock(&docg3->cascade->lock); |
| 879 | while (!ret && (len > 0 || ooblen > 0)) { | 880 | while (ret >= 0 && (len > 0 || ooblen > 0)) { |
| 880 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, | 881 | calc_block_sector(from - skip, &block0, &block1, &page, &ofs, |
| 881 | docg3->reliable); | 882 | docg3->reliable); |
| 882 | nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); | 883 | nbdata = min_t(size_t, len, DOC_LAYOUT_PAGE_SIZE - skip); |
| @@ -936,7 +937,8 @@ static int doc_read_oob(struct mtd_info *mtd, loff_t from, | |||
| 936 | } | 937 | } |
| 937 | if (ret > 0) { | 938 | if (ret > 0) { |
| 938 | mtd->ecc_stats.corrected += ret; | 939 | mtd->ecc_stats.corrected += ret; |
| 939 | ret = -EUCLEAN; | 940 | max_bitflips = max(max_bitflips, ret); |
| 941 | ret = max_bitflips; | ||
| 940 | } | 942 | } |
| 941 | } | 943 | } |
| 942 | 944 | ||
diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 6a7cba1e24e6..575730744fdb 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c | |||
| @@ -800,12 +800,24 @@ EXPORT_SYMBOL_GPL(mtd_get_unmapped_area); | |||
| 800 | int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, | 800 | int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, |
| 801 | u_char *buf) | 801 | u_char *buf) |
| 802 | { | 802 | { |
| 803 | int ret_code; | ||
| 803 | *retlen = 0; | 804 | *retlen = 0; |
| 804 | if (from < 0 || from > mtd->size || len > mtd->size - from) | 805 | if (from < 0 || from > mtd->size || len > mtd->size - from) |
| 805 | return -EINVAL; | 806 | return -EINVAL; |
| 806 | if (!len) | 807 | if (!len) |
| 807 | return 0; | 808 | return 0; |
| 808 | return mtd->_read(mtd, from, len, retlen, buf); | 809 | |
| 810 | /* | ||
| 811 | * In the absence of an error, drivers return a non-negative integer | ||
| 812 | * representing the maximum number of bitflips that were corrected on | ||
| 813 | * any one ecc region (if applicable; zero otherwise). | ||
| 814 | */ | ||
| 815 | ret_code = mtd->_read(mtd, from, len, retlen, buf); | ||
| 816 | if (unlikely(ret_code < 0)) | ||
| 817 | return ret_code; | ||
| 818 | if (mtd->ecc_strength == 0) | ||
| 819 | return 0; /* device lacks ecc */ | ||
| 820 | return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; | ||
| 809 | } | 821 | } |
| 810 | EXPORT_SYMBOL_GPL(mtd_read); | 822 | EXPORT_SYMBOL_GPL(mtd_read); |
| 811 | 823 | ||
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index ec75d44e0253..d518e4db8a0b 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
| @@ -67,12 +67,12 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 67 | stats = part->master->ecc_stats; | 67 | stats = part->master->ecc_stats; |
| 68 | res = part->master->_read(part->master, from + part->offset, len, | 68 | res = part->master->_read(part->master, from + part->offset, len, |
| 69 | retlen, buf); | 69 | retlen, buf); |
| 70 | if (unlikely(res)) { | 70 | if (unlikely(mtd_is_eccerr(res))) |
| 71 | if (mtd_is_bitflip(res)) | 71 | mtd->ecc_stats.failed += |
| 72 | mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected; | 72 | part->master->ecc_stats.failed - stats.failed; |
| 73 | if (mtd_is_eccerr(res)) | 73 | else |
| 74 | mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed; | 74 | mtd->ecc_stats.corrected += |
| 75 | } | 75 | part->master->ecc_stats.corrected - stats.corrected; |
| 76 | return res; | 76 | return res; |
| 77 | } | 77 | } |
| 78 | 78 | ||
diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 4f20e1d8bef1..60a0dfdb0808 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c | |||
| @@ -414,7 +414,7 @@ static int alauda_bounce_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 414 | } | 414 | } |
| 415 | err = 0; | 415 | err = 0; |
| 416 | if (corrected) | 416 | if (corrected) |
| 417 | err = -EUCLEAN; | 417 | err = 1; /* return max_bitflips per ecc step */ |
| 418 | if (uncorrected) | 418 | if (uncorrected) |
| 419 | err = -EBADMSG; | 419 | err = -EBADMSG; |
| 420 | out: | 420 | out: |
| @@ -446,7 +446,7 @@ static int alauda_read(struct mtd_info *mtd, loff_t from, size_t len, | |||
| 446 | } | 446 | } |
| 447 | err = 0; | 447 | err = 0; |
| 448 | if (corrected) | 448 | if (corrected) |
| 449 | err = -EUCLEAN; | 449 | err = 1; /* return max_bitflips per ecc step */ |
| 450 | if (uncorrected) | 450 | if (uncorrected) |
| 451 | err = -EBADMSG; | 451 | err = -EBADMSG; |
| 452 | return err; | 452 | return err; |
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 9f5d339a3610..640f1f8159e8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c | |||
| @@ -1486,6 +1486,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1486 | mtd->oobavail : mtd->oobsize; | 1486 | mtd->oobavail : mtd->oobsize; |
| 1487 | 1487 | ||
| 1488 | uint8_t *bufpoi, *oob, *buf; | 1488 | uint8_t *bufpoi, *oob, *buf; |
| 1489 | unsigned int max_bitflips = 0; | ||
| 1489 | 1490 | ||
| 1490 | stats = mtd->ecc_stats; | 1491 | stats = mtd->ecc_stats; |
| 1491 | 1492 | ||
| @@ -1513,7 +1514,10 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1513 | sndcmd = 0; | 1514 | sndcmd = 0; |
| 1514 | } | 1515 | } |
| 1515 | 1516 | ||
| 1516 | /* Now read the page into the buffer */ | 1517 | /* |
| 1518 | * Now read the page into the buffer. Absent an error, | ||
| 1519 | * the read methods return max bitflips per ecc step. | ||
| 1520 | */ | ||
| 1517 | if (unlikely(ops->mode == MTD_OPS_RAW)) | 1521 | if (unlikely(ops->mode == MTD_OPS_RAW)) |
| 1518 | ret = chip->ecc.read_page_raw(mtd, chip, | 1522 | ret = chip->ecc.read_page_raw(mtd, chip, |
| 1519 | bufpoi, page); | 1523 | bufpoi, page); |
| @@ -1530,15 +1534,19 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1530 | break; | 1534 | break; |
| 1531 | } | 1535 | } |
| 1532 | 1536 | ||
| 1537 | max_bitflips = max_t(unsigned int, max_bitflips, ret); | ||
| 1538 | |||
| 1533 | /* Transfer not aligned data */ | 1539 | /* Transfer not aligned data */ |
| 1534 | if (!aligned) { | 1540 | if (!aligned) { |
| 1535 | if (!NAND_SUBPAGE_READ(chip) && !oob && | 1541 | if (!NAND_SUBPAGE_READ(chip) && !oob && |
| 1536 | !(mtd->ecc_stats.failed - stats.failed) && | 1542 | !(mtd->ecc_stats.failed - stats.failed) && |
| 1537 | (ops->mode != MTD_OPS_RAW)) | 1543 | (ops->mode != MTD_OPS_RAW)) { |
| 1538 | chip->pagebuf = realpage; | 1544 | chip->pagebuf = realpage; |
| 1539 | else | 1545 | chip->pagebuf_bitflips = ret; |
| 1546 | } else { | ||
| 1540 | /* Invalidate page cache */ | 1547 | /* Invalidate page cache */ |
| 1541 | chip->pagebuf = -1; | 1548 | chip->pagebuf = -1; |
| 1549 | } | ||
| 1542 | memcpy(buf, chip->buffers->databuf + col, bytes); | 1550 | memcpy(buf, chip->buffers->databuf + col, bytes); |
| 1543 | } | 1551 | } |
| 1544 | 1552 | ||
| @@ -1571,6 +1579,8 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1571 | } else { | 1579 | } else { |
| 1572 | memcpy(buf, chip->buffers->databuf + col, bytes); | 1580 | memcpy(buf, chip->buffers->databuf + col, bytes); |
| 1573 | buf += bytes; | 1581 | buf += bytes; |
| 1582 | max_bitflips = max_t(unsigned int, max_bitflips, | ||
| 1583 | chip->pagebuf_bitflips); | ||
| 1574 | } | 1584 | } |
| 1575 | 1585 | ||
| 1576 | readlen -= bytes; | 1586 | readlen -= bytes; |
| @@ -1609,7 +1619,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, | |||
| 1609 | if (mtd->ecc_stats.failed - stats.failed) | 1619 | if (mtd->ecc_stats.failed - stats.failed) |
| 1610 | return -EBADMSG; | 1620 | return -EBADMSG; |
| 1611 | 1621 | ||
| 1612 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | 1622 | return max_bitflips; |
| 1613 | } | 1623 | } |
| 1614 | 1624 | ||
| 1615 | /** | 1625 | /** |
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index b3ce12ef359e..7153e0d27101 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c | |||
| @@ -1201,7 +1201,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, | |||
| 1201 | if (mtd->ecc_stats.failed - stats.failed) | 1201 | if (mtd->ecc_stats.failed - stats.failed) |
| 1202 | return -EBADMSG; | 1202 | return -EBADMSG; |
| 1203 | 1203 | ||
| 1204 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | 1204 | /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ |
| 1205 | return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; | ||
| 1205 | } | 1206 | } |
| 1206 | 1207 | ||
| 1207 | /** | 1208 | /** |
| @@ -1333,7 +1334,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from, | |||
| 1333 | if (mtd->ecc_stats.failed - stats.failed) | 1334 | if (mtd->ecc_stats.failed - stats.failed) |
| 1334 | return -EBADMSG; | 1335 | return -EBADMSG; |
| 1335 | 1336 | ||
| 1336 | return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; | 1337 | /* return max bitflips per ecc step; ONENANDs correct 1 bit only */ |
| 1338 | return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0; | ||
| 1337 | } | 1339 | } |
| 1338 | 1340 | ||
| 1339 | /** | 1341 | /** |
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1482340d3d9f..2829e8be3a62 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h | |||
| @@ -459,6 +459,8 @@ struct nand_buffers { | |||
| 459 | * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 | 459 | * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 |
| 460 | * @pagebuf: [INTERN] holds the pagenumber which is currently in | 460 | * @pagebuf: [INTERN] holds the pagenumber which is currently in |
| 461 | * data_buf. | 461 | * data_buf. |
| 462 | * @pagebuf_bitflips: [INTERN] holds the bitflip count for the page which is | ||
| 463 | * currently in data_buf. | ||
| 462 | * @subpagesize: [INTERN] holds the subpagesize | 464 | * @subpagesize: [INTERN] holds the subpagesize |
| 463 | * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), | 465 | * @onfi_version: [INTERN] holds the chip ONFI version (BCD encoded), |
| 464 | * non 0 if ONFI supported. | 466 | * non 0 if ONFI supported. |
| @@ -519,6 +521,7 @@ struct nand_chip { | |||
| 519 | uint64_t chipsize; | 521 | uint64_t chipsize; |
| 520 | int pagemask; | 522 | int pagemask; |
| 521 | int pagebuf; | 523 | int pagebuf; |
| 524 | unsigned int pagebuf_bitflips; | ||
| 522 | int subpagesize; | 525 | int subpagesize; |
| 523 | uint8_t cellinfo; | 526 | uint8_t cellinfo; |
| 524 | int badblockpos; | 527 | int badblockpos; |
