]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
mtd: nand: brcmnand: fix mtd corrected bits stat
authorDavid Regan <dregan@broadcom.com>
Thu, 3 Jul 2025 02:47:05 +0000 (19:47 -0700)
committerMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 30 Jul 2025 09:27:30 +0000 (11:27 +0200)
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 <dregan@broadcom.com>
Reviewed-by: William Zhang <william.zhang@broadcom.com>
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
drivers/mtd/nand/raw/brcmnand/brcmnand.c

index dc836b14102466a6d38f1f6066decf6c86367926..835653bdd5abccc3f594234620238f21801a146b 100644 (file)
@@ -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);
        }