]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/mtd/nand/denali.c
mtd: nand: denali: fix erased page checking
[karo-tx-linux.git] / drivers / mtd / nand / denali.c
index c5c150a95fb63202f583f1df64afcfc5a772da08..64a3bdcc6b04fe0d0c5bf6aa7754ccbe340aa415 100644 (file)
@@ -818,19 +818,44 @@ static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
        }
 }
 
-/*
- * this function examines buffers to see if they contain data that
- * indicate that the buffer is part of an erased region of flash.
- */
-static bool is_erased(uint8_t *buf, int len)
+static int denali_check_erased_page(struct mtd_info *mtd,
+                                   struct nand_chip *chip, uint8_t *buf,
+                                   unsigned long uncor_ecc_flags,
+                                   unsigned int max_bitflips)
 {
-       int i;
+       uint8_t *ecc_code = chip->buffers->ecccode;
+       int ecc_steps = chip->ecc.steps;
+       int ecc_size = chip->ecc.size;
+       int ecc_bytes = chip->ecc.bytes;
+       int i, ret, stat;
+
+       ret = mtd_ooblayout_get_eccbytes(mtd, ecc_code, chip->oob_poi, 0,
+                                        chip->ecc.total);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < ecc_steps; i++) {
+               if (!(uncor_ecc_flags & BIT(i)))
+                       continue;
+
+               stat = nand_check_erased_ecc_chunk(buf, ecc_size,
+                                                 ecc_code, ecc_bytes,
+                                                 NULL, 0,
+                                                 chip->ecc.strength);
+               if (stat < 0) {
+                       mtd->ecc_stats.failed++;
+               } else {
+                       mtd->ecc_stats.corrected += stat;
+                       max_bitflips = max_t(unsigned int, max_bitflips, stat);
+               }
 
-       for (i = 0; i < len; i++)
-               if (buf[i] != 0xFF)
-                       return false;
-       return true;
+               buf += ecc_size;
+               ecc_code += ecc_bytes;
+       }
+
+       return max_bitflips;
 }
+
 #define ECC_SECTOR_SIZE 512
 
 #define ECC_SECTOR(x)  (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
@@ -841,7 +866,7 @@ static bool is_erased(uint8_t *buf, int len)
 #define ECC_LAST_ERR(x)                ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
 
 static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
-                     uint8_t *buf, bool *check_erased_page)
+                     uint8_t *buf, unsigned long *uncor_ecc_flags)
 {
        unsigned int bitflips = 0;
        unsigned int max_bitflips = 0;
@@ -868,11 +893,10 @@ static int handle_ecc(struct mtd_info *mtd, struct denali_nand_info *denali,
 
                if (ECC_ERROR_UNCORRECTABLE(err_cor_info)) {
                        /*
-                        * if the error is not correctable, need to look at the
-                        * page to see if it is an erased page. if so, then
-                        * it's not a real ECC error
+                        * Check later if this is a real ECC error, or
+                        * an erased sector.
                         */
-                       *check_erased_page = true;
+                       *uncor_ecc_flags |= BIT(err_sector);
                } else if (err_byte < ECC_SECTOR_SIZE) {
                        /*
                         * If err_byte is larger than ECC_SECTOR_SIZE, means error
@@ -1045,7 +1069,6 @@ static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
 static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
                            uint8_t *buf, int oob_required, int page)
 {
-       unsigned int max_bitflips = 0;
        struct denali_nand_info *denali = mtd_to_denali(mtd);
 
        dma_addr_t addr = denali->buf.dma_buf;
@@ -1053,7 +1076,8 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 
        uint32_t irq_status;
        uint32_t irq_mask = INTR__ECC_TRANSACTION_DONE | INTR__ECC_ERR;
-       bool check_erased_page = false;
+       unsigned long uncor_ecc_flags = 0;
+       int stat = 0;
 
        if (page != denali->page) {
                dev_err(denali->dev,
@@ -1078,21 +1102,20 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
        memcpy(buf, denali->buf.buf, mtd->writesize);
 
        if (irq_status & INTR__ECC_ERR)
-               max_bitflips = handle_ecc(mtd, denali, buf, &check_erased_page);
+               stat = handle_ecc(mtd, denali, buf, &uncor_ecc_flags);
        denali_enable_dma(denali, false);
 
-       if (check_erased_page) {
+       if (stat < 0)
+               return stat;
+
+       if (uncor_ecc_flags) {
                read_oob_data(mtd, chip->oob_poi, denali->page);
 
-               /* check ECC failures that may have occurred on erased pages */
-               if (check_erased_page) {
-                       if (!is_erased(buf, mtd->writesize))
-                               mtd->ecc_stats.failed++;
-                       if (!is_erased(buf, mtd->oobsize))
-                               mtd->ecc_stats.failed++;
-               }
+               stat = denali_check_erased_page(mtd, chip, buf,
+                                               uncor_ecc_flags, stat);
        }
-       return max_bitflips;
+
+       return stat;
 }
 
 static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,