diff options
author | Kamal Dasu <kdasu.kdev@gmail.com> | 2016-06-09 17:17:54 -0400 |
---|---|---|
committer | Boris Brezillon <boris.brezillon@free-electrons.com> | 2016-06-13 11:03:23 -0400 |
commit | 02b88eea9f9cab82f5f4be234c64466503021f82 (patch) | |
tree | b1e1f53c7a6d1605c19cd713079fb898e513a200 /drivers/mtd | |
parent | 1c7fe6b438433e16d5b1211380c305351ac38299 (diff) |
mtd: brcmnand: Add check for erased page bitflips
Check for erased page bitflips in a page. And if well within
threshold return data as all 0xff. Apply sw check for controller
version < 7.2. Controller vesion >= 7.2 has hw support.
Signed-off-by: Kamal Dasu <kdasu.kdev@gmail.com>
Signed-off-by: Boris Brezillon <boris.brezillon@free-electrons.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/brcmnand/brcmnand.c | 62 |
1 files changed, 62 insertions, 0 deletions
diff --git a/drivers/mtd/nand/brcmnand/brcmnand.c b/drivers/mtd/nand/brcmnand/brcmnand.c index 0a54a0f5a4b1..f0b067229f7f 100644 --- a/drivers/mtd/nand/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/brcmnand/brcmnand.c | |||
@@ -1602,6 +1602,56 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, | |||
1602 | return ret; | 1602 | return ret; |
1603 | } | 1603 | } |
1604 | 1604 | ||
1605 | /* | ||
1606 | * Check a page to see if it is erased (w/ bitflips) after an uncorrectable ECC | ||
1607 | * error | ||
1608 | * | ||
1609 | * Because the HW ECC signals an ECC error if an erase paged has even a single | ||
1610 | * bitflip, we must check each ECC error to see if it is actually an erased | ||
1611 | * page with bitflips, not a truly corrupted page. | ||
1612 | * | ||
1613 | * On a real error, return a negative error code (-EBADMSG for ECC error), and | ||
1614 | * buf will contain raw data. | ||
1615 | * Otherwise, buf gets filled with 0xffs and return the maximum number of | ||
1616 | * bitflips-per-ECC-sector to the caller. | ||
1617 | * | ||
1618 | */ | ||
1619 | static int brcmstb_nand_verify_erased_page(struct mtd_info *mtd, | ||
1620 | struct nand_chip *chip, void *buf, u64 addr) | ||
1621 | { | ||
1622 | int i, sas; | ||
1623 | void *oob = chip->oob_poi; | ||
1624 | int bitflips = 0; | ||
1625 | int page = addr >> chip->page_shift; | ||
1626 | int ret; | ||
1627 | |||
1628 | if (!buf) { | ||
1629 | buf = chip->buffers->databuf; | ||
1630 | /* Invalidate page cache */ | ||
1631 | chip->pagebuf = -1; | ||
1632 | } | ||
1633 | |||
1634 | sas = mtd->oobsize / chip->ecc.steps; | ||
1635 | |||
1636 | /* read without ecc for verification */ | ||
1637 | chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); | ||
1638 | ret = chip->ecc.read_page_raw(mtd, chip, buf, true, page); | ||
1639 | if (ret) | ||
1640 | return ret; | ||
1641 | |||
1642 | for (i = 0; i < chip->ecc.steps; i++, oob += sas) { | ||
1643 | ret = nand_check_erased_ecc_chunk(buf, chip->ecc.size, | ||
1644 | oob, sas, NULL, 0, | ||
1645 | chip->ecc.strength); | ||
1646 | if (ret < 0) | ||
1647 | return ret; | ||
1648 | |||
1649 | bitflips = max(bitflips, ret); | ||
1650 | } | ||
1651 | |||
1652 | return bitflips; | ||
1653 | } | ||
1654 | |||
1605 | static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, | 1655 | static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, |
1606 | u64 addr, unsigned int trans, u32 *buf, u8 *oob) | 1656 | u64 addr, unsigned int trans, u32 *buf, u8 *oob) |
1607 | { | 1657 | { |
@@ -1632,6 +1682,18 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, | |||
1632 | } | 1682 | } |
1633 | 1683 | ||
1634 | if (mtd_is_eccerr(err)) { | 1684 | if (mtd_is_eccerr(err)) { |
1685 | /* | ||
1686 | * Controller version 7.2 has hw encoder to detect erased page | ||
1687 | * bitflips, apply sw verification for older controllers only | ||
1688 | */ | ||
1689 | if (ctrl->nand_version < 0x0702) { | ||
1690 | err = brcmstb_nand_verify_erased_page(mtd, chip, buf, | ||
1691 | addr); | ||
1692 | /* erased page bitflips corrected */ | ||
1693 | if (err > 0) | ||
1694 | return err; | ||
1695 | } | ||
1696 | |||
1635 | dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", | 1697 | dev_dbg(ctrl->dev, "uncorrectable error at 0x%llx\n", |
1636 | (unsigned long long)err_addr); | 1698 | (unsigned long long)err_addr); |
1637 | mtd->ecc_stats.failed++; | 1699 | mtd->ecc_stats.failed++; |