diff mbox

[v1,3/4] nand/denali: Fixed handle ECC error bugs

Message ID 20100809162225.GC10968@intel.com
State New, archived
Headers show

Commit Message

Dong, Chuanxiao Aug. 9, 2010, 4:22 p.m. UTC
From 3e40d412afda3d5ec508b8ee66e468bcc79cb710 Mon Sep 17 00:00:00 2001
From: Chuanxiao Dong <chuanxiao.dong@intel.com>
Date: Tue, 10 Aug 2010 00:07:01 +0800
Subject: [PATCH 3/4] 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>
---
 drivers/mtd/nand/denali.c |   45 +++++++++++++++++++++++++++++----------------
 1 files changed, 29 insertions(+), 16 deletions(-)
diff mbox

Patch

diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c
index b255b41..0416fc7 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) {