From 23584da2875972782b8a35a9a78c46e5f561ec67 Mon Sep 17 00:00:00 2001 From: David Regan Date: Wed, 2 Jul 2025 19:47:05 -0700 Subject: [PATCH] mtd: nand: brcmnand: fix mtd corrected bits stat Currently we attempt to get the amount of flipped bits from a hardware location which is reset on every subpage. Instead obtain total flipped bits stat from hardware accumulator. In addition identify the correct maximum subpage corrected bits. Signed-off-by: David Regan Reviewed-by: William Zhang Signed-off-by: Miquel Raynal --- drivers/mtd/nand/raw/brcmnand/brcmnand.c | 53 +++++++++++++++++------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/nand/raw/brcmnand/brcmnand.c b/drivers/mtd/nand/raw/brcmnand/brcmnand.c index dc836b1410246..835653bdd5abc 100644 --- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c +++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c @@ -360,6 +360,7 @@ enum brcmnand_reg { BRCMNAND_CORR_THRESHOLD_EXT, BRCMNAND_UNCORR_COUNT, BRCMNAND_CORR_COUNT, + BRCMNAND_READ_ERROR_COUNT, BRCMNAND_CORR_EXT_ADDR, BRCMNAND_CORR_ADDR, BRCMNAND_UNCORR_EXT_ADDR, @@ -390,6 +391,7 @@ static const u16 brcmnand_regs_v21[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0, [BRCMNAND_UNCORR_COUNT] = 0, [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_READ_ERROR_COUNT] = 0, [BRCMNAND_CORR_EXT_ADDR] = 0x60, [BRCMNAND_CORR_ADDR] = 0x64, [BRCMNAND_UNCORR_EXT_ADDR] = 0x68, @@ -420,6 +422,7 @@ static const u16 brcmnand_regs_v33[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0, [BRCMNAND_UNCORR_COUNT] = 0, [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_READ_ERROR_COUNT] = 0x80, [BRCMNAND_CORR_EXT_ADDR] = 0x70, [BRCMNAND_CORR_ADDR] = 0x74, [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, @@ -450,6 +453,7 @@ static const u16 brcmnand_regs_v50[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0, [BRCMNAND_UNCORR_COUNT] = 0, [BRCMNAND_CORR_COUNT] = 0, + [BRCMNAND_READ_ERROR_COUNT] = 0x80, [BRCMNAND_CORR_EXT_ADDR] = 0x70, [BRCMNAND_CORR_ADDR] = 0x74, [BRCMNAND_UNCORR_EXT_ADDR] = 0x78, @@ -480,6 +484,7 @@ static const u16 brcmnand_regs_v60[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0xc4, [BRCMNAND_UNCORR_COUNT] = 0xfc, [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_READ_ERROR_COUNT] = 0x104, [BRCMNAND_CORR_EXT_ADDR] = 0x10c, [BRCMNAND_CORR_ADDR] = 0x110, [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, @@ -510,6 +515,7 @@ static const u16 brcmnand_regs_v71[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, [BRCMNAND_UNCORR_COUNT] = 0xfc, [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_READ_ERROR_COUNT] = 0x104, [BRCMNAND_CORR_EXT_ADDR] = 0x10c, [BRCMNAND_CORR_ADDR] = 0x110, [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, @@ -540,6 +546,7 @@ static const u16 brcmnand_regs_v72[] = { [BRCMNAND_CORR_THRESHOLD_EXT] = 0xe0, [BRCMNAND_UNCORR_COUNT] = 0xfc, [BRCMNAND_CORR_COUNT] = 0x100, + [BRCMNAND_READ_ERROR_COUNT] = 0x104, [BRCMNAND_CORR_EXT_ADDR] = 0x10c, [BRCMNAND_CORR_ADDR] = 0x110, [BRCMNAND_UNCORR_EXT_ADDR] = 0x114, @@ -960,11 +967,11 @@ static inline u16 brcmnand_cs_offset(struct brcmnand_controller *ctrl, int cs, return offs_cs0 + cs * ctrl->reg_spacing + cs_offs; } -static inline u32 brcmnand_count_corrected(struct brcmnand_controller *ctrl) +static inline u32 brcmnand_corr_total(struct brcmnand_controller *ctrl) { - if (ctrl->nand_version < 0x0600) - return 1; - return brcmnand_read_reg(ctrl, BRCMNAND_CORR_COUNT); + if (ctrl->nand_version < 0x400) + return 0; + return brcmnand_read_reg(ctrl, BRCMNAND_READ_ERROR_COUNT); } static void brcmnand_wr_corr_thresh(struct brcmnand_host *host, u8 val) @@ -2067,15 +2074,20 @@ static int brcmnand_dma_trans(struct brcmnand_host *host, u64 addr, u32 *buf, */ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, u64 addr, unsigned int trans, u32 *buf, - u8 *oob, u64 *err_addr) + u8 *oob, u64 *err_addr, unsigned int *corr) { struct brcmnand_host *host = nand_get_controller_data(chip); struct brcmnand_controller *ctrl = host->ctrl; int i, ret = 0; + unsigned int prev_corr; + + if (corr) + *corr = 0; brcmnand_clear_ecc_addr(ctrl); for (i = 0; i < trans; i++, addr += FC_BYTES) { + prev_corr = brcmnand_corr_total(ctrl); brcmnand_set_cmd_addr(mtd, addr); /* SPARE_AREA_READ does not use ECC, so just use PAGE_READ */ brcmnand_send_cmd(host, CMD_PAGE_READ); @@ -2100,13 +2112,16 @@ static int brcmnand_read_by_pio(struct mtd_info *mtd, struct nand_chip *chip, if (*err_addr) ret = -EBADMSG; - } + else { + *err_addr = brcmnand_get_correcc_addr(ctrl); - if (!ret) { - *err_addr = brcmnand_get_correcc_addr(ctrl); + if (*err_addr) { + ret = -EUCLEAN; - if (*err_addr) - ret = -EUCLEAN; + if (corr && (brcmnand_corr_total(ctrl) - prev_corr) > *corr) + *corr = brcmnand_corr_total(ctrl) - prev_corr; + } + } } } @@ -2174,6 +2189,8 @@ static int brcmnand_read(struct mtd_info *mtd, struct nand_chip *chip, int err; bool retry = true; bool edu_err = false; + unsigned int corrected = 0; /* max corrected bits per subpage */ + unsigned int prev_tot = brcmnand_corr_total(ctrl); dev_dbg(ctrl->dev, "read %llx -> %p\n", (unsigned long long)addr, buf); @@ -2201,9 +2218,11 @@ try_dmaread: memset(oob, 0x99, mtd->oobsize); err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, - oob, &err_addr); + oob, &err_addr, &corrected); } + mtd->ecc_stats.corrected += brcmnand_corr_total(ctrl) - prev_tot; + if (mtd_is_eccerr(err)) { /* * On controller version and 7.0, 7.1 , DMA read after a @@ -2241,16 +2260,20 @@ try_dmaread: } if (mtd_is_bitflip(err)) { - unsigned int corrected = brcmnand_count_corrected(ctrl); - /* in case of EDU correctable error we read again using PIO */ if (edu_err) err = brcmnand_read_by_pio(mtd, chip, addr, trans, buf, - oob, &err_addr); + oob, &err_addr, &corrected); dev_dbg(ctrl->dev, "corrected error at 0x%llx\n", (unsigned long long)err_addr); - mtd->ecc_stats.corrected += corrected; + /* + * if flipped bits accumulator is not supported but we detected + * a correction, increase stat by 1 to match previous behavior. + */ + if (brcmnand_corr_total(ctrl) == prev_tot) + mtd->ecc_stats.corrected++; + /* Always exceed the software-imposed threshold */ return max(mtd->bitflip_threshold, corrected); } -- 2.47.2