|
@@ -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)
|
|
#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_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)
|
|
#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,
|
|
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,
|
|
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,
|
|
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:
|
|
|
|
- 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 */
|
|
/* 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,
|
|
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,
|
|
|
|
|
|
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) {
|