
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@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 */