[U-Boot-Users] [PATCH 1/2] NAND: Add support for transparent hardware ECC.

Scott Wood scottwood at freescale.com
Wed May 16 18:27:14 CEST 2007


Some NAND controllers (such as on the MPC831x chips) have hardware ECC,
but can only do it during a transfer (i.e. we can't implement
calculate_ecc()).  When NAND_ECC_TRANSPARENT is used, then ECC errors on
reads are reported through correct_data() (with no arguments other than
the mtd device), and on writes through waitfunc()'s return value.

Signed-off-by: Scott Wood <scottwood at freescale.com>
---
 drivers/nand/nand_base.c |   84 ++++++++++++++++++++++++++++++++--------------
 include/linux/mtd/nand.h |   10 +++++
 2 files changed, 69 insertions(+), 25 deletions(-)

diff --git a/drivers/nand/nand_base.c b/drivers/nand/nand_base.c
index 8495829..52c612c 100644
--- a/drivers/nand/nand_base.c
+++ b/drivers/nand/nand_base.c
@@ -911,6 +911,11 @@ static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int pa
 		this->write_buf(mtd, this->data_poi, mtd->oobblock);
 		break;
 
+	case NAND_ECC_TRANSPARENT:
+		this->enable_hwecc(mtd, NAND_ECC_WRITE);
+		this->write_buf(mtd, this->data_poi, mtd->oobblock);
+		break;
+
 	/* Software ecc 3/256, write all */
 	case NAND_ECC_SOFT:
 		for (; eccsteps; eccsteps--) {
@@ -992,10 +997,14 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
 	int	eccsteps = this->eccsteps;
 	int	hweccbytes;
 	u_char 	oobdata[64];
+	int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
 
 	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
 
 	/* Send command to read back the first page */
+	if (eccmode == NAND_ECC_TRANSPARENT)
+		this->enable_hwecc(mtd, NAND_ECC_READ);
+
 	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
 
 	for(;;) {
@@ -1019,7 +1028,30 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
 		/* check, if we must compare all data or if we just have to
 		 * compare the ecc bytes
 		 */
-		if (oobmode) {
+		if (eccmode == NAND_ECC_TRANSPARENT) {
+			if (oobmode) {
+				/* Compare everything *but* the ECC */
+				this->read_buf(mtd, oobdata, mtd->oobsize -
+				               hweccbytes * eccsteps);
+
+				for (i = 0, j = 0; i < mtd->oobsize; i++) {
+					if (j < oobsel->eccbytes &&
+					    i == oobsel->eccpos[j]) {
+						j++;
+						continue;
+					}
+
+					if (oobdata[i] != oob_buf[oobofs + i]) {
+						DEBUG (MTD_DEBUG_LEVEL0,
+					          "%s: Failed ECC write "
+						"verify, page 0x%08x, "
+						"%6i bytes were succesful\n",
+						__FUNCTION__, page, i);
+						goto out;
+					}
+				}
+			}
+		} else if (oobmode) {
 			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
 				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
 				goto out;
@@ -1062,10 +1094,13 @@ static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int
 		if (!numpages)
 			return 0;
 
-
 		/* Check, if the chip supports auto page increment */
-		if (!NAND_CANAUTOINCR(this))
+		if (!NAND_CANAUTOINCR(this)) {
+			if (eccmode == NAND_ECC_TRANSPARENT)
+				this->enable_hwecc(mtd, NAND_ECC_READ);
+
 			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+		}
 	}
 	/*
 	 * Terminate the read command. We come here in case of an error
@@ -1161,7 +1196,8 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
 	ecc = this->eccsize;
 	eccbytes = this->eccbytes;
 
-	if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
+	if (eccmode == NAND_ECC_NONE || eccmode == NAND_ECC_TRANSPARENT ||
+	    (this->options & NAND_HWECC_SYNDROME))
 		compareecc = 0;
 
 	oobreadlen = mtd->oobsize;
@@ -1195,6 +1231,9 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
 
 		/* Check, if we must send the read command */
 		if (sndcmd) {
+			if (eccmode == NAND_ECC_TRANSPARENT)
+				this->enable_hwecc(mtd, NAND_ECC_READ);
+
 			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
 			sndcmd = 0;
 		}
@@ -1222,6 +1261,18 @@ static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
 			break;
 		}
 
+		case NAND_ECC_TRANSPARENT:
+			this->read_buf(mtd, data_poi, end);
+
+			if (this->correct_data(mtd, NULL, NULL, NULL) == -1) {
+				DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: "
+				      "Failed ECC read, page 0x%08x on chip %d\n",
+				      page, chipnr);
+				ecc_failed++;
+			}
+
+			break;
+
 		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
 			this->read_buf(mtd, data_poi, end);
 			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
@@ -2530,7 +2581,9 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
 
 	case NAND_ECC_NONE:
 		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
-		this->eccmode = NAND_ECC_NONE;
+		/* fall-through */
+	case NAND_ECC_TRANSPARENT:
+		this->eccsize = mtd->oobblock;
 		break;
 
 	case NAND_ECC_SOFT:
@@ -2562,26 +2615,7 @@ int nand_scan (struct mtd_info *mtd, int maxchips)
 	}
 
 	mtd->eccsize = this->eccsize;
-
-	/* Set the number of read / write steps for one page to ensure ECC generation */
-	switch (this->eccmode) {
-	case NAND_ECC_HW12_2048:
-		this->eccsteps = mtd->oobblock / 2048;
-		break;
-	case NAND_ECC_HW3_512:
-	case NAND_ECC_HW6_512:
-	case NAND_ECC_HW8_512:
-		this->eccsteps = mtd->oobblock / 512;
-		break;
-	case NAND_ECC_HW3_256:
-	case NAND_ECC_SOFT:
-		this->eccsteps = mtd->oobblock / 256;
-		break;
-
-	case NAND_ECC_NONE:
-		this->eccsteps = 1;
-		break;
-	}
+	this->eccsteps = mtd->oobblock / this->eccsize;
 
 /* XXX U-BOOT XXX */
 #if 0
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 4b48564..1af0666 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -136,6 +136,16 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
 /* Hardware ECC 12 byte ECC per 2048 Byte data */
 #define NAND_ECC_HW12_2048	7
 
+/* The hardware does ECC transparently on transfers.  This is treated
+ * like NAND_ECC_NONE, except without lecturing the user that they really
+ * should use ECC.  Also, it bypasses verification of OOB data, as the
+ * ECC written by the hardware will differ from what the generic code
+ * expects.  The calculate_ecc method should not be called.  The
+ * correct_data method is used to return errors on read; only the mtd
+ * argument is used.
+ */
+#define NAND_ECC_TRANSPARENT 0x1000
+
 /*
  * Constants for Hardware ECC
 */
-- 
1.5.0.3





More information about the U-Boot mailing list