diff options
author | Chuanxiao Dong <chuanxiao.dong@intel.com> | 2010-08-09 12:07:01 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2010-08-10 19:28:39 -0400 |
commit | 8ae61ebddba8a0cf96f61e592acaa12800e50727 (patch) | |
tree | f098bacb9c388ff44d6a9e62c4e87d72428ed65a /drivers/mtd/nand/denali.c | |
parent | 24c3fa36dedd12616c273bfa4adfed3bb652637f (diff) |
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>
Diffstat (limited to 'drivers/mtd/nand/denali.c')
-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 4ab9d89eae5..e4462c0740b 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) | |||
643 | spin_lock_irq(&denali->irq_lock); | 643 | spin_lock_irq(&denali->irq_lock); |
644 | 644 | ||
645 | status = read_interrupt_status(denali); | 645 | status = read_interrupt_status(denali); |
646 | clear_interrupt(denali, status); | ||
646 | 647 | ||
647 | #if DEBUG_DENALI | 648 | #if DEBUG_DENALI |
648 | denali->irq_debug_array[denali->idx++] = 0x30000000 | status; | 649 | denali->irq_debug_array[denali->idx++] = 0x30000000 | status; |
@@ -1015,12 +1016,12 @@ bool is_erased(uint8_t *buf, int len) | |||
1015 | #define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) | 1016 | #define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) |
1016 | #define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) | 1017 | #define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) |
1017 | #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) | 1018 | #define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) |
1018 | #define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO)) | 1019 | #define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE)) |
1019 | #define ECC_ERR_DEVICE(x) ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8) | 1020 | #define ECC_ERR_DEVICE(x) (((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8) |
1020 | #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) | 1021 | #define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) |
1021 | 1022 | ||
1022 | static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, | 1023 | static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, |
1023 | uint8_t *oobbuf, uint32_t irq_status) | 1024 | uint32_t irq_status) |
1024 | { | 1025 | { |
1025 | bool check_erased_page = false; | 1026 | bool check_erased_page = false; |
1026 | 1027 | ||
@@ -1029,6 +1030,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, | |||
1029 | uint32_t err_address = 0, err_correction_info = 0; | 1030 | uint32_t err_address = 0, err_correction_info = 0; |
1030 | uint32_t err_byte = 0, err_sector = 0, err_device = 0; | 1031 | uint32_t err_byte = 0, err_sector = 0, err_device = 0; |
1031 | uint32_t err_correction_value = 0; | 1032 | uint32_t err_correction_value = 0; |
1033 | denali_set_intr_modes(denali, false); | ||
1032 | 1034 | ||
1033 | do { | 1035 | do { |
1034 | err_address = ioread32(denali->flash_reg + | 1036 | err_address = ioread32(denali->flash_reg + |
@@ -1036,7 +1038,6 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, | |||
1036 | err_sector = ECC_SECTOR(err_address); | 1038 | err_sector = ECC_SECTOR(err_address); |
1037 | err_byte = ECC_BYTE(err_address); | 1039 | err_byte = ECC_BYTE(err_address); |
1038 | 1040 | ||
1039 | |||
1040 | err_correction_info = ioread32(denali->flash_reg + | 1041 | err_correction_info = ioread32(denali->flash_reg + |
1041 | ERR_CORRECTION_INFO); | 1042 | ERR_CORRECTION_INFO); |
1042 | err_correction_value = | 1043 | err_correction_value = |
@@ -1044,20 +1045,23 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, | |||
1044 | err_device = ECC_ERR_DEVICE(err_correction_info); | 1045 | err_device = ECC_ERR_DEVICE(err_correction_info); |
1045 | 1046 | ||
1046 | if (ECC_ERROR_CORRECTABLE(err_correction_info)) { | 1047 | if (ECC_ERROR_CORRECTABLE(err_correction_info)) { |
1047 | /* offset in our buffer is computed as: | 1048 | /* If err_byte is larger than ECC_SECTOR_SIZE, |
1048 | sector number * sector size + offset in | 1049 | * means error happend in OOB, so we ignore |
1049 | sector | 1050 | * it. It's no need for us to correct it |
1050 | */ | 1051 | * err_device is represented the NAND error |
1051 | int offset = err_sector * ECC_SECTOR_SIZE + | 1052 | * bits are happened in if there are more |
1052 | err_byte; | 1053 | * than one NAND connected. |
1053 | if (offset < denali->mtd.writesize) { | 1054 | * */ |
1055 | if (err_byte < ECC_SECTOR_SIZE) { | ||
1056 | int offset; | ||
1057 | offset = (err_sector * | ||
1058 | ECC_SECTOR_SIZE + | ||
1059 | err_byte) * | ||
1060 | denali->devnum + | ||
1061 | err_device; | ||
1054 | /* correct the ECC error */ | 1062 | /* correct the ECC error */ |
1055 | buf[offset] ^= err_correction_value; | 1063 | buf[offset] ^= err_correction_value; |
1056 | denali->mtd.ecc_stats.corrected++; | 1064 | denali->mtd.ecc_stats.corrected++; |
1057 | } else { | ||
1058 | /* bummer, couldn't correct the error */ | ||
1059 | printk(KERN_ERR "ECC offset invalid\n"); | ||
1060 | denali->mtd.ecc_stats.failed++; | ||
1061 | } | 1065 | } |
1062 | } else { | 1066 | } else { |
1063 | /* if the error is not correctable, need to | 1067 | /* 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 | err_correction_info); | 1078 | err_correction_info); |
1075 | #endif | 1079 | #endif |
1076 | } while (!ECC_LAST_ERR(err_correction_info)); | 1080 | } while (!ECC_LAST_ERR(err_correction_info)); |
1081 | /* Once handle all ecc errors, controller will triger | ||
1082 | * a ECC_TRANSACTION_DONE interrupt, so here just wait | ||
1083 | * for a while for this interrupt | ||
1084 | * */ | ||
1085 | while (!(read_interrupt_status(denali) & | ||
1086 | INTR_STATUS0__ECC_TRANSACTION_DONE)) | ||
1087 | cpu_relax(); | ||
1088 | clear_interrupts(denali); | ||
1089 | denali_set_intr_modes(denali, true); | ||
1077 | } | 1090 | } |
1078 | return check_erased_page; | 1091 | return check_erased_page; |
1079 | } | 1092 | } |
@@ -1237,7 +1250,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, | |||
1237 | 1250 | ||
1238 | memcpy(buf, denali->buf.buf, mtd->writesize); | 1251 | memcpy(buf, denali->buf.buf, mtd->writesize); |
1239 | 1252 | ||
1240 | check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); | 1253 | check_erased_page = handle_ecc(denali, buf, irq_status); |
1241 | denali_enable_dma(denali, false); | 1254 | denali_enable_dma(denali, false); |
1242 | 1255 | ||
1243 | if (check_erased_page) { | 1256 | if (check_erased_page) { |