@@ -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
@@ -256,6 +256,7 @@ int bch_set_geometry(struct gpmi_nand_data *this)
unsigned int ecc_strength;
unsigned int page_size;
unsigned int gf_len;
+ unsigned int erase_threshold;
int ret;
if (common_nfc_set_geometry(this))
@@ -298,6 +299,14 @@ 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. */
+ erase_threshold = gf_len / 2;
+ if (erase_threshold > bch_geo->ecc_strength)
+ erase_threshold = bch_geo->ecc_strength;
+
+ writel(erase_threshold & 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);
@@ -958,6 +958,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)
{
@@ -969,6 +993,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;
@@ -1003,15 +1028,27 @@ 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);
+
+ /*
+ * The number of bitflips are either counted in software
+ * in case of an erased chunk or otherwise reported by
+ * the BCH block.
+ */
+ if (*status == STATUS_ERASED)
+ flips = erased_sector_bitflips(payload_virt, i,
+ nfc_geo);
+ else
+ flips = *status;
+
+ mtd->ecc_stats.corrected += flips;
+ max_bitflips = max_t(unsigned int, max_bitflips, flips);
}
if (oob_required) {