Commit 8ae61ebd authored by Chuanxiao Dong's avatar Chuanxiao Dong Committed by David Woodhouse

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: default avatarChuanxiao Dong <chuanxiao.dong@intel.com>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent 24c3fa36
...@@ -643,6 +643,7 @@ static void clear_interrupts(struct denali_nand_info *denali) ...@@ -643,6 +643,7 @@ static void clear_interrupts(struct denali_nand_info *denali)
spin_lock_irq(&denali->irq_lock); spin_lock_irq(&denali->irq_lock);
status = read_interrupt_status(denali); status = read_interrupt_status(denali);
clear_interrupt(denali, status);
#if DEBUG_DENALI #if DEBUG_DENALI
denali->irq_debug_array[denali->idx++] = 0x30000000 | status; denali->irq_debug_array[denali->idx++] = 0x30000000 | status;
...@@ -1015,12 +1016,12 @@ bool is_erased(uint8_t *buf, int len) ...@@ -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_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) #define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET))
#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO)) #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_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, 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; bool check_erased_page = false;
...@@ -1029,6 +1030,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, ...@@ -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_address = 0, err_correction_info = 0;
uint32_t err_byte = 0, err_sector = 0, err_device = 0; uint32_t err_byte = 0, err_sector = 0, err_device = 0;
uint32_t err_correction_value = 0; uint32_t err_correction_value = 0;
denali_set_intr_modes(denali, false);
do { do {
err_address = ioread32(denali->flash_reg + err_address = ioread32(denali->flash_reg +
...@@ -1036,7 +1038,6 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, ...@@ -1036,7 +1038,6 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
err_sector = ECC_SECTOR(err_address); err_sector = ECC_SECTOR(err_address);
err_byte = ECC_BYTE(err_address); err_byte = ECC_BYTE(err_address);
err_correction_info = ioread32(denali->flash_reg + err_correction_info = ioread32(denali->flash_reg +
ERR_CORRECTION_INFO); ERR_CORRECTION_INFO);
err_correction_value = err_correction_value =
...@@ -1044,20 +1045,23 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, ...@@ -1044,20 +1045,23 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
err_device = ECC_ERR_DEVICE(err_correction_info); err_device = ECC_ERR_DEVICE(err_correction_info);
if (ECC_ERROR_CORRECTABLE(err_correction_info)) { if (ECC_ERROR_CORRECTABLE(err_correction_info)) {
/* offset in our buffer is computed as: /* If err_byte is larger than ECC_SECTOR_SIZE,
sector number * sector size + offset in * means error happend in OOB, so we ignore
sector * it. It's no need for us to correct it
*/ * err_device is represented the NAND error
int offset = err_sector * ECC_SECTOR_SIZE + * bits are happened in if there are more
err_byte; * than one NAND connected.
if (offset < denali->mtd.writesize) { * */
if (err_byte < ECC_SECTOR_SIZE) {
int offset;
offset = (err_sector *
ECC_SECTOR_SIZE +
err_byte) *
denali->devnum +
err_device;
/* correct the ECC error */ /* correct the ECC error */
buf[offset] ^= err_correction_value; buf[offset] ^= err_correction_value;
denali->mtd.ecc_stats.corrected++; 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 { } else {
/* if the error is not correctable, need to /* if the error is not correctable, need to
...@@ -1074,6 +1078,15 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, ...@@ -1074,6 +1078,15 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
err_correction_info); err_correction_info);
#endif #endif
} while (!ECC_LAST_ERR(err_correction_info)); } 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; return check_erased_page;
} }
...@@ -1237,7 +1250,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, ...@@ -1237,7 +1250,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
memcpy(buf, denali->buf.buf, mtd->writesize); 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); denali_enable_dma(denali, false);
if (check_erased_page) { if (check_erased_page) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment