nand/denali: Fixed handle ECC error bugs
authorChuanxiao Dong <chuanxiao.dong@intel.com>
Mon, 9 Aug 2010 16:07:01 +0000 (00:07 +0800)
committerDavid Woodhouse <David.Woodhouse@intel.com>
Tue, 10 Aug 2010 23:28:39 +0000 (00:28 +0100)
Once the last ECC error was handled, controller will triger an
interrupt. If this interrupt can not be clean on time, controller
may corrupt.

Signed-off-by: Chuanxiao Dong <chuanxiao.dong@intel.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
drivers/mtd/nand/denali.c

index 4ab9d89eae569c7270324f293afa5de5a1fefe9c..e4462c0740b2b726a7ecc3c65063d211c7538a95 100644 (file)
@@ -643,6 +643,7 @@ static void clear_interrupts(struct denali_nand_info *denali)
        spin_lock_irq(&denali->irq_lock);
 
        status = read_interrupt_status(denali);
+       clear_interrupt(denali, status);
 
 #if DEBUG_DENALI
        denali->irq_debug_array[denali->idx++] = 0x30000000 | status;
@@ -1015,12 +1016,12 @@ bool is_erased(uint8_t *buf, int len)
 #define ECC_SECTOR(x)  (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
 #define ECC_BYTE(x)    (((x) & ECC_ERROR_ADDRESS__OFFSET))
 #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
-#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO))
-#define ECC_ERR_DEVICE(x)      ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8)
+#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE))
+#define ECC_ERR_DEVICE(x)      (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
 #define ECC_LAST_ERR(x)                ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
 
 static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
-                       uint8_t *oobbuf, uint32_t irq_status)
+                                       uint32_t irq_status)
 {
        bool check_erased_page = false;
 
@@ -1029,6 +1030,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
                uint32_t err_address = 0, err_correction_info = 0;
                uint32_t err_byte = 0, err_sector = 0, err_device = 0;
                uint32_t err_correction_value = 0;
+               denali_set_intr_modes(denali, false);
 
                do {
                        err_address = ioread32(denali->flash_reg +
@@ -1036,7 +1038,6 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
                        err_sector = ECC_SECTOR(err_address);
                        err_byte = ECC_BYTE(err_address);
 
-
                        err_correction_info = ioread32(denali->flash_reg +
                                                ERR_CORRECTION_INFO);
                        err_correction_value =
@@ -1044,20 +1045,23 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
                        err_device = ECC_ERR_DEVICE(err_correction_info);
 
                        if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
-                               /* offset in our buffer is computed as:
-                                  sector number * sector size + offset in
-                                  sector
-                                */
-                               int offset = err_sector * ECC_SECTOR_SIZE +
-                                                               err_byte;
-                               if (offset < denali->mtd.writesize) {
+                               /* If err_byte is larger than ECC_SECTOR_SIZE,
+                                * means error happend in OOB, so we ignore
+                                * it. It's no need for us to correct it
+                                * err_device is represented the NAND error
+                                * bits are happened in if there are more
+                                * than one NAND connected.
+                                * */
+                               if (err_byte < ECC_SECTOR_SIZE) {
+                                       int offset;
+                                       offset = (err_sector *
+                                                       ECC_SECTOR_SIZE +
+                                                       err_byte) *
+                                                       denali->devnum +
+                                                       err_device;
                                        /* correct the ECC error */
                                        buf[offset] ^= err_correction_value;
                                        denali->mtd.ecc_stats.corrected++;
-                               } else {
-                                       /* bummer, couldn't correct the error */
-                                       printk(KERN_ERR "ECC offset invalid\n");
-                                       denali->mtd.ecc_stats.failed++;
                                }
                        } else {
                                /* if the error is not correctable, need to
@@ -1074,6 +1078,15 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
                                        err_correction_info);
 #endif
                } while (!ECC_LAST_ERR(err_correction_info));
+               /* Once handle all ecc errors, controller will triger
+                * a ECC_TRANSACTION_DONE interrupt, so here just wait
+                * for a while for this interrupt
+                * */
+               while (!(read_interrupt_status(denali) &
+                               INTR_STATUS0__ECC_TRANSACTION_DONE))
+                       cpu_relax();
+               clear_interrupts(denali);
+               denali_set_intr_modes(denali, true);
        }
        return check_erased_page;
 }
@@ -1237,7 +1250,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
 
        memcpy(buf, denali->buf.buf, mtd->writesize);
 
-       check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status);
+       check_erased_page = handle_ecc(denali, buf, irq_status);
        denali_enable_dma(denali, false);
 
        if (check_erased_page) {