diff options
author | Chuanxiao Dong <chuanxiao.dong@intel.com> | 2010-08-10 00:07:01 +0800 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-08-11 00:28:39 +0100 |
commit | 8ae61ebddba8a0cf96f61e592acaa12800e50727 (patch) | |
tree | f098bacb9c388ff44d6a9e62c4e87d72428ed65a | |
parent | 24c3fa36dedd12616c273bfa4adfed3bb652637f (diff) | |
download | linux-8ae61ebddba8a0cf96f61e592acaa12800e50727.tar.gz linux-8ae61ebddba8a0cf96f61e592acaa12800e50727.tar.bz2 linux-8ae61ebddba8a0cf96f61e592acaa12800e50727.zip |
nand/denali: Fixed handle ECC error bugs
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>
-rw-r--r-- | drivers/mtd/nand/denali.c | 45 |
1 files changed, 29 insertions, 16 deletions
diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 4ab9d89eae56..e4462c0740b2 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -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) { |