[PATCH v1] mtd: gpmi: Deal with bitflips in erased regions regions
Peter Korsgaard
peter at korsgaard.com
Tue Dec 10 04:37:08 EST 2013
>>>>> "Elie" == Elie De Brauwer <eliedebrauwer at gmail.com> writes:
> The BCH block typically used with a i.MX28 and GPMI block is only
> able to correct bitflips on data actually streamed through the block.
> When erasing a block the data does not stream through the BCH block
> and therefore not ECC data is written to the NAND chip. This causes
s/not/no/
> gpmi_ecc_read_page to return failure as soon as a single non-1-bit is
> found in an erased page. Typically causing problems at higher levels
> (ubifs corrupted empty space warnings).
> This patch configures the BCH block to mark a block as 'erased' if
> no more than ecc_strength bitflips are found. The result is that
> erased blocks are doublechecked by software for bitflips and that
> bitflips are corrected and reported, allowing the upper layers to
> take proper actions.
> Signed-off-by: Elie De Brauwer <eliedebrauwer at gmail.com>
Looks good otherwise.
Acked-by: Peter Korsgaard <peter at korsgaard.com>
> ---
> drivers/mtd/nand/gpmi-nand/bch-regs.h | 1 +
> drivers/mtd/nand/gpmi-nand/gpmi-lib.c | 7 ++++++
> drivers/mtd/nand/gpmi-nand/gpmi-nand.c | 40 +++++++++++++++++++++++++++++---
> 3 files changed, 45 insertions(+), 3 deletions(-)
> diff --git a/drivers/mtd/nand/gpmi-nand/bch-regs.h b/drivers/mtd/nand/gpmi-nand/bch-regs.h
> index 588f537..b2104de 100644
> --- a/drivers/mtd/nand/gpmi-nand/bch-regs.h
> +++ b/drivers/mtd/nand/gpmi-nand/bch-regs.h
> @@ -31,6 +31,7 @@
> #define HW_BCH_STATUS0 0x00000010
> #define HW_BCH_MODE 0x00000020
> +#define BM_BCH_MODE_ERASE_THRESHOLD_MASK 0xff
> #define HW_BCH_ENCODEPTR 0x00000030
> #define HW_BCH_DATAPTR 0x00000040
> #define HW_BCH_METAPTR 0x00000050
> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> index aaced29..4236739 100644
> --- a/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-lib.c
> @@ -286,6 +286,13 @@ int bch_set_geometry(struct gpmi_nand_data *this)
> | BF_BCH_FLASH0LAYOUT1_DATAN_SIZE(block_size, this),
r-> bch_regs + HW_BCH_FLASH0LAYOUT1);
> + /*
> + * Set the tolerance for bitflips when reading erased blocks
> + * equal to the ecc_strength.
> + */
> + writel(bch_geo->ecc_strength & BM_BCH_MODE_ERASE_THRESHOLD_MASK,
> + r->bch_regs + HW_BCH_MODE);
> +
> /* Set *all* chip selects to use layout 0. */
> writel(0, r->bch_regs + HW_BCH_LAYOUTSELECT);
> diff --git a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> index dabbc14..766261f 100644
> --- a/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> +++ b/drivers/mtd/nand/gpmi-nand/gpmi-nand.c
> @@ -1012,6 +1012,30 @@ static void block_mark_swapping(struct gpmi_nand_data *this,
> p[1] = (p[1] & mask) | (from_oob >> (8 - bit));
> }
> +/*
> + * Count the number of 0 bits in a supposed to be
> + * erased region and correct them. Return the number
> + * of bitflips or zero when the region was correct.
> + */
> +static unsigned int erased_sector_bitflips(unsigned char *data,
> + unsigned int chunk,
> + struct bch_geometry *geo)
> +{
> + unsigned int flip_bits = 0;
> + int i;
> + int base = geo->ecc_chunk_size * chunk;
> +
> + /* Count bitflips */
> + for (i = 0; i < geo->ecc_chunk_size; i++)
> + flip_bits += hweight8(~data[base + i]);
> +
> + /* Correct bitflips by 0xFF'ing this chunk. */
> + if (flip_bits)
> + memset(&data[base], 0xFF, geo->ecc_chunk_size);
> +
> + return flip_bits;
> +}
> +
> static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
> uint8_t *buf, int oob_required, int page)
> {
> @@ -1023,6 +1047,7 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
> dma_addr_t auxiliary_phys;
> unsigned int i;
> unsigned char *status;
> + unsigned int flips;
> unsigned int max_bitflips = 0;
> int ret;
> @@ -1057,15 +1082,24 @@ static int gpmi_ecc_read_page(struct mtd_info *mtd, struct nand_chip *chip,
> status = auxiliary_virt + nfc_geo->auxiliary_status_offset;
> for (i = 0; i < nfc_geo->ecc_chunk_count; i++, status++) {
> - if ((*status == STATUS_GOOD) || (*status == STATUS_ERASED))
> + if (*status == STATUS_GOOD)
> continue;
> if (*status == STATUS_UNCORRECTABLE) {
mtd-> ecc_stats.failed++;
> continue;
> }
> - mtd->ecc_stats.corrected += *status;
> - max_bitflips = max_t(unsigned int, max_bitflips, *status);
> +
> + if (*status == STATUS_ERASED)
> + /* Erased block, check/correct bitflips manually. */
> + flips = erased_sector_bitflips(payload_virt, i,
> + nfc_geo);
> + else
> + /* BCH block corrected some errors for us. */
> + flips = *status;
> +
> + mtd->ecc_stats.corrected += flips;
> + max_bitflips = max_t(unsigned int, max_bitflips, flips);
> }
> if (oob_required) {
> --
> 1.7.10.4
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/
--
Bye, Peter Korsgaard
More information about the linux-mtd
mailing list