[MTD] NAND Modularize read function

Linux-MTD Mailing List linux-mtd at lists.infradead.org
Thu May 25 08:59:07 EDT 2006


Commit:     f5bbdacc41939f89d8ccb18dd79cd9b21c0cb75d
Parent:     9577f44a899cf4acb9e381c8946307b72153cd15
Author:     Thomas Gleixner <tglx at cruncher.tec.linutronix.de>
AuthorDate: Thu May 25 10:07:16 2006 +0200
Commit:     David Woodhouse <dwmw2 at infradead.org>
CommitDate: Thu May 25 12:45:27 2006 +0100

    [MTD] NAND Modularize read function
    
    Split the core of the read function out and implement
    seperate handling functions for software and hardware
    ECC.
    
    Signed-off-by: Thomas Gleixner <tglx at linutronix.de>

 drivers/mtd/nand/diskonchip.c |    4 
 drivers/mtd/nand/nand_base.c  |  448 +++++++++++++++++++++--------------------
 drivers/mtd/nand/rtc_from4.c  |   62 +++---
 include/linux/mtd/nand.h      |   16 +
 4 files changed, 275 insertions(+), 255 deletions(-)

diff --git a/drivers/mtd/nand/diskonchip.c b/drivers/mtd/nand/diskonchip.c
index 2ec9080..83af6f0 100644
--- a/drivers/mtd/nand/diskonchip.c
+++ b/drivers/mtd/nand/diskonchip.c
@@ -968,12 +968,14 @@ #endif
 	return 0;
 }
 
-static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
+				u_char *read_ecc, u_char *isnull)
 {
 	int i, ret = 0;
 	struct nand_chip *this = mtd->priv;
 	struct doc_priv *doc = this->priv;
 	void __iomem *docptr = doc->virtadr;
+	uint8_t calc_ecc[6];
 	volatile u_char dummy;
 	int emptymatch = 1;
 
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 49bca24..21fce2b 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_
 #endif
 
 /**
- * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
- * @mtd:	MTD device structure
- * @from:	offset to read from
- * @len:	number of bytes to read
- * @retlen:	pointer to variable to store the number of read bytes
- * @buf:	the databuffer to put data
- *
- * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
- * and flags = 0xff
+ * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
  */
-static int nand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, uint8_t *buf)
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf)
 {
-	return nand_do_read_ecc(mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize;
+	uint8_t *ecc_code = ecc_calc + mtd->oobsize;
+	int *eccpos = chip->autooob->eccpos;
+
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
+
+	if (chip->ecc.mode == NAND_ECC_NONE)
+		return 0;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_buf[eccpos[i]];
+
+	eccsteps = chip->ecc.steps;
+	p = buf;
+
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
 }
 
 /**
- * nand_do_read_ecc - [MTD Interface] Read data with ECC
- * @mtd:	MTD device structure
- * @from:	offset to read from
- * @len:	number of bytes to read
- * @retlen:	pointer to variable to store the number of read bytes
- * @buf:	the databuffer to put data
- * @oob_buf:	filesystem supplied oob data buffer (can be NULL)
- * @oobsel:	oob selection structure
- * @flags:	flag to indicate if nand_get_device/nand_release_device should be preformed
- *		and how many corrected error bits are acceptable:
- *		  bits 0..7 - number of tolerable errors
- *		  bit  8    - 0 == do not get/release chip, 1 == get/release chip
+ * nand_read_page_hwecc - {REPLACABLE] hardware ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
  *
- * NAND read with ECC
+ * Not for syndrome calculating ecc controllers which need a special oob layout
  */
-int nand_do_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
-		     size_t *retlen, uint8_t *buf, uint8_t *oob_buf, struct nand_oobinfo *oobsel, int flags)
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf)
 {
-
-	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
-	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
-	struct nand_chip *chip = mtd->priv;
-	uint8_t *data_poi, *oob_data = oob_buf;
-	uint8_t ecc_calc[32];
-	uint8_t ecc_code[32];
-	int eccmode, eccsteps;
-	int *oob_config, datidx;
-	int blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
-	int eccbytes;
-	int compareecc = 1;
-	int oobreadlen;
-
-	DEBUG(MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int)from, (int)len);
-
-	/* Do not allow reads past end of device */
-	if ((from + len) > mtd->size) {
-		DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
-		*retlen = 0;
-		return -EINVAL;
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->oob_buf + mtd->oobsize;
+	uint8_t *ecc_code = ecc_calc + mtd->oobsize;
+	int *eccpos = chip->autooob->eccpos;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 	}
+	chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
 
-	/* Grab the lock and see if the device is available */
-	if (flags & NAND_GET_DEVICE)
-		nand_get_device(chip, mtd, FL_READING);
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_buf[eccpos[i]];
 
-	/* Autoplace of oob data ? Use the default placement scheme */
-	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
-		oobsel = chip->autooob;
+	eccsteps = chip->ecc.steps;
+	p = buf;
 
-	eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE;
-	oob_config = oobsel->eccpos;
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
 
-	/* Select the NAND device */
-	chipnr = (int)(from >> chip->chip_shift);
-	chip->select_chip(mtd, chipnr);
-
-	/* First we calculate the starting page */
-	realpage = (int)(from >> chip->page_shift);
-	page = realpage & chip->pagemask;
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
+}
 
-	/* Get raw starting column */
-	col = from & (mtd->writesize - 1);
+/**
+ * nand_read_page_syndrome - {REPLACABLE] hardware ecc syndrom based page read
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and .
+ */
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				   uint8_t *buf)
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *oob = chip->oob_buf;
 
-	end = mtd->writesize;
-	ecc = chip->ecc.size;
-	eccbytes = chip->ecc.bytes;
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
 
-	if ((eccmode == NAND_ECC_NONE) || (chip->options & NAND_HWECC_SYNDROME))
-		compareecc = 0;
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
 
-	oobreadlen = mtd->oobsize;
-	if (chip->options & NAND_HWECC_SYNDROME)
-		oobreadlen -= oobsel->eccbytes;
+		if (chip->ecc.prepad) {
+			chip->read_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
 
-	/* Loop until all data read */
-	while (read < len) {
+		chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+		chip->read_buf(mtd, oob, eccbytes);
+		stat = chip->ecc.correct(mtd, p, oob, NULL);
 
-		int aligned = (!col && (len - read) >= end);
-		/*
-		 * If the read is not page aligned, we have to read into data buffer
-		 * due to ecc, else we read into return buffer direct
-		 */
-		if (aligned)
-			data_poi = &buf[read];
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
 		else
-			data_poi = chip->data_buf;
+			mtd->ecc_stats.corrected += stat;
 
-		/* Check, if we have this page in the buffer
-		 *
-		 * FIXME: Make it work when we must provide oob data too,
-		 * check the usage of data_buf oob field
-		 */
-		if (realpage == chip->pagebuf && !oob_buf) {
-			/* aligned read ? */
-			if (aligned)
-				memcpy(data_poi, chip->data_buf, end);
-			goto readdata;
-		}
+		oob += eccbytes;
 
-		/* Check, if we must send the read command */
-		if (sndcmd) {
-			chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
-			sndcmd = 0;
+		if (chip->ecc.postpad) {
+			chip->read_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
 		}
+	}
 
-		/* get oob area, if we have no oob buffer from fs-driver */
-		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
-			oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
-			oob_data = &chip->data_buf[end];
-
-		eccsteps = chip->ecc.steps;
-
-		switch (eccmode) {
-		case NAND_ECC_NONE:{
-				/* No ECC, Read in a page */
-				static unsigned long lastwhinge = 0;
-				if ((lastwhinge / HZ) != (jiffies / HZ)) {
-					printk(KERN_WARNING
-					       "Reading data from NAND FLASH without ECC is not recommended\n");
-					lastwhinge = jiffies;
-				}
-				chip->read_buf(mtd, data_poi, end);
-				break;
-			}
+	/* Calculate remaining oob bytes */
+	i = oob - chip->oob_buf;
+	if (i)
+		chip->read_buf(mtd, oob, i);
 
-		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
-			chip->read_buf(mtd, data_poi, end);
-			for (i = 0, datidx = 0; eccsteps; eccsteps--, i += 3, datidx += ecc)
-				chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]);
-			break;
+	return 0;
+}
 
-		default:
-			for (i = 0, datidx = 0; eccsteps; eccsteps--, i += eccbytes, datidx += ecc) {
-				chip->ecc.hwctl(mtd, NAND_ECC_READ);
-				chip->read_buf(mtd, &data_poi[datidx], ecc);
-
-				/* HW ecc with syndrome calculation must read the
-				 * syndrome from flash immidiately after the data */
-				if (!compareecc) {
-					/* Some hw ecc generators need to know when the
-					 * syndrome is read from flash */
-					chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
-					chip->read_buf(mtd, &oob_data[i], eccbytes);
-					/* We calc error correction directly, it checks the hw
-					 * generator for an error, reads back the syndrome and
-					 * does the error correction on the fly */
-					ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
-					if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
-						DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: "
-						      "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
-						ecc_failed++;
-					}
-				} else {
-					chip->ecc.calculate(mtd, &data_poi[datidx], &ecc_calc[i]);
-				}
-			}
-			break;
-		}
+/**
+ * nand_do_read - [Internal] Read data with ECC
+ *
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * Internal function. Called with chip held.
+ */
+int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
+		 size_t *retlen, uint8_t *buf)
+{
+	int chipnr, page, realpage, col, bytes, aligned;
+	struct nand_chip *chip = mtd->priv;
+	struct mtd_ecc_stats stats;
+	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+	int sndcmd = 1;
+	int ret = 0;
+	uint32_t readlen = len;
+	uint8_t *bufpoi;
 
-		/* read oobdata */
-		chip->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
+	stats = mtd->ecc_stats;
 
-		/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
-		if (!compareecc)
-			goto readoob;
+	chipnr = (int)(from >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
 
-		/* Pick the ECC bytes out of the oob data */
-		for (j = 0; j < oobsel->eccbytes; j++)
-			ecc_code[j] = oob_data[oob_config[j]];
+	realpage = (int)(from >> chip->page_shift);
+	page = realpage & chip->pagemask;
 
-		/* correct data, if necessary */
-		for (i = 0, j = 0, datidx = 0; i < chip->ecc.steps; i++, datidx += ecc) {
-			ecc_status = chip->ecc.correct(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+	col = (int)(from & (mtd->writesize - 1));
 
-			/* Get next chunk of ecc bytes */
-			j += eccbytes;
+	while(1) {
+		bytes = min(mtd->writesize - col, readlen);
+		aligned = (bytes == mtd->writesize);
 
-			/* Check, if we have a fs supplied oob-buffer,
-			 * This is the legacy mode. Used by YAFFS1
-			 * Should go away some day
-			 */
-			if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
-				int *p = (int *)(&oob_data[mtd->oobsize]);
-				p[i] = ecc_status;
-			}
+		/* Is the current page in the buffer ? */
+		if (realpage != chip->pagebuf) {
+			bufpoi = aligned ? buf : chip->data_buf;
 
-			if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
-				DEBUG(MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
-				ecc_failed++;
+			if (likely(sndcmd)) {
+				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+				sndcmd = 0;
 			}
-		}
 
-	      readoob:
-		/* check, if we have a fs supplied oob-buffer */
-		if (oob_buf) {
-			/* without autoplace. Legacy mode used by YAFFS1 */
-			switch (oobsel->useecc) {
-			case MTD_NANDECC_AUTOPLACE:
-			case MTD_NANDECC_AUTOPL_USR:
-				/* Walk through the autoplace chunks */
-				for (i = 0; oobsel->oobfree[i][1]; i++) {
-					int from = oobsel->oobfree[i][0];
-					int num = oobsel->oobfree[i][1];
-					memcpy(&oob_buf[oob], &oob_data[from], num);
-					oob += num;
-				}
+			/* Now read the page into the buffer */
+			ret = chip->ecc.read_page(mtd, chip, bufpoi);
+			if (ret < 0)
 				break;
-			case MTD_NANDECC_PLACE:
-				/* YAFFS1 legacy mode */
-				oob_data += chip->ecc.steps * sizeof(int);
-			default:
-				oob_data += mtd->oobsize;
+
+			/* Transfer not aligned data */
+			if (!aligned) {
+				chip->pagebuf = realpage;
+				memcpy(buf, chip->data_buf + col, bytes);
+			}
+
+			if (!(chip->options & NAND_NO_READRDY)) {
+				/*
+				 * Apply delay or wait for ready/busy pin. Do
+				 * this before the AUTOINCR check, so no
+				 * problems arise if a chip which does auto
+				 * increment is marked as NOAUTOINCR by the
+				 * board driver.
+				 */
+				if (!chip->dev_ready)
+					udelay(chip->chip_delay);
+				else
+					nand_wait_ready(mtd);
 			}
-		}
-	readdata:
-		/* Partial page read, transfer data into fs buffer */
-		if (!aligned) {
-			for (j = col; j < end && read < len; j++)
-				buf[read++] = data_poi[j];
-			chip->pagebuf = realpage;
 		} else
-			read += mtd->writesize;
+			memcpy(buf, chip->data_buf + col, bytes);
 
-		/* Apply delay or wait for ready/busy pin
-		 * Do this before the AUTOINCR check, so no problems
-		 * arise if a chip which does auto increment
-		 * is marked as NOAUTOINCR by the board driver.
-		 */
-		if (!chip->dev_ready)
-			udelay(chip->chip_delay);
-		else
-			nand_wait_ready(mtd);
+		buf += bytes;
+		readlen -= bytes;
 
-		if (read == len)
+		if (!readlen)
 			break;
 
 		/* For subsequent reads align to page boundary. */
@@ -1240,24 +1208,51 @@ int nand_do_read_ecc(struct mtd_info *mt
 			chip->select_chip(mtd, -1);
 			chip->select_chip(mtd, chipnr);
 		}
+
 		/* Check, if the chip supports auto page increment
 		 * or if we have hit a block boundary.
 		 */
-		if (!NAND_CANAUTOINCR(chip) || !(page & blockcheck))
+		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
 			sndcmd = 1;
 	}
 
-	/* Deselect and wake up anyone waiting on the device */
-	if (flags & NAND_GET_DEVICE)
-		nand_release_device(mtd);
+	*retlen = len - (size_t) readlen;
 
-	/*
-	 * Return success, if no ECC failures, else -EBADMSG
-	 * fs driver will take care of that, because
-	 * retlen == desired len and result == -EBADMSG
-	 */
-	*retlen = read;
-	return ecc_failed ? -EBADMSG : 0;
+	if (ret)
+		return ret;
+
+	return mtd->ecc_stats.failed - stats.failed ? -EBADMSG : 0;
+}
+
+/**
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * Get hold of the chip and call nand_do_read
+ */
+static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+		     size_t *retlen, uint8_t *buf)
+{
+	int ret;
+
+	*retlen = 0;
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	nand_get_device(mtd->priv, mtd, FL_READING);
+
+	ret = nand_do_read(mtd, from, len, retlen, buf);
+
+	nand_release_device(mtd);
+
+	return ret;
 }
 
 /**
@@ -2417,6 +2412,10 @@ int nand_scan(struct mtd_info *mtd, int 
 	 */
 	switch (chip->ecc.mode) {
 	case NAND_ECC_HW:
+		/* Use standard hwecc read page function ? */
+		if (!chip->ecc.read_page)
+			chip->ecc.read_page = nand_read_page_hwecc;
+
 	case NAND_ECC_HW_SYNDROME:
 		if (!chip->ecc.calculate || !chip->ecc.correct ||
 		    !chip->ecc.hwctl) {
@@ -2424,6 +2423,10 @@ int nand_scan(struct mtd_info *mtd, int 
 			       "Hardware ECC not possible\n");
 			BUG();
 		}
+		/* Use standard syndrome read page function ? */
+		if (!chip->ecc.read_page)
+			chip->ecc.read_page = nand_read_page_syndrome;
+
 		if (mtd->writesize >= chip->ecc.size)
 			break;
 		printk(KERN_WARNING "%d byte HW ECC not possible on "
@@ -2434,6 +2437,7 @@ int nand_scan(struct mtd_info *mtd, int 
 	case NAND_ECC_SOFT:
 		chip->ecc.calculate = nand_calculate_ecc;
 		chip->ecc.correct = nand_correct_data;
+		chip->ecc.read_page = nand_read_page_swecc;
 		chip->ecc.size = 256;
 		chip->ecc.bytes = 3;
 		break;
@@ -2441,6 +2445,7 @@ int nand_scan(struct mtd_info *mtd, int 
 	case NAND_ECC_NONE:
 		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
 		       "This is not recommended !!\n");
+		chip->ecc.read_page = nand_read_page_swecc;
 		chip->ecc.size = mtd->writesize;
 		chip->ecc.bytes = 0;
 		break;
@@ -2459,6 +2464,7 @@ int nand_scan(struct mtd_info *mtd, int 
 		printk(KERN_WARNING "Invalid ecc parameters\n");
 		BUG();
 	}
+	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 
 	/* Initialize state */
 	chip->state = FL_READY;
diff --git a/drivers/mtd/nand/rtc_from4.c b/drivers/mtd/nand/rtc_from4.c
index 6c97bfa..b708310 100644
--- a/drivers/mtd/nand/rtc_from4.c
+++ b/drivers/mtd/nand/rtc_from4.c
@@ -444,7 +444,8 @@ static int rtc_from4_correct_data(struct
  * note: see pages 34..37 of data sheet for details.
  *
  */
-static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page)
+static int rtc_from4_errstat(struct mtd_info *mtd, struct nand_chip *this,
+			     int state, int status, int page)
 {
 	int er_stat = 0;
 	int rtn, retlen;
@@ -455,39 +456,50 @@ static int rtc_from4_errstat(struct mtd_
 	this->cmdfunc(mtd, NAND_CMD_STATUS_CLEAR, -1, -1);
 
 	if (state == FL_ERASING) {
+
 		for (i = 0; i < 4; i++) {
-			if (status & 1 << (i + 1)) {
-				this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1), -1, -1);
-				rtn = this->read_byte(mtd);
-				this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
-				if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
-					er_stat |= 1 << (i + 1);	/* err_ecc_not_avail */
-				}
-			}
+			if (!(status & 1 << (i + 1)))
+				continue;
+			this->cmdfunc(mtd, (NAND_CMD_STATUS_ERROR + i + 1),
+				      -1, -1);
+			rtn = this->read_byte(mtd);
+			this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
+
+			/* err_ecc_not_avail */
+			if (!(rtn & ERR_STAT_ECC_AVAILABLE))
+				er_stat |= 1 << (i + 1);
 		}
+
 	} else if (state == FL_WRITING) {
+
+		unsigned long corrected = mtd->ecc_stats.corrected;
+
 		/* single bank write logic */
 		this->cmdfunc(mtd, NAND_CMD_STATUS_ERROR, -1, -1);
 		rtn = this->read_byte(mtd);
 		this->cmdfunc(mtd, NAND_CMD_STATUS_RESET, -1, -1);
+
 		if (!(rtn & ERR_STAT_ECC_AVAILABLE)) {
-			er_stat |= 1 << 1;	/* err_ecc_not_avail */
-		} else {
-			len = mtd->writesize;
-			buf = kmalloc(len, GFP_KERNEL);
-			if (!buf) {
-				printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
-				er_stat = 1;	/* if we can't check, assume failed */
-			} else {
-				/* recovery read */
-				/* page read */
-				rtn = nand_do_read_ecc(mtd, page, len, &retlen, buf, NULL, this->autooob, 1);
-				if (rtn) {	/* if read failed or > 1-bit error corrected */
-					er_stat |= 1 << 1;	/* ECC read failed */
-				}
-				kfree(buf);
-			}
+			/* err_ecc_not_avail */
+			er_stat |= 1 << 1;
+			goto out;
 		}
+
+		len = mtd->writesize;
+		buf = kmalloc(len, GFP_KERNEL);
+		if (!buf) {
+			printk(KERN_ERR "rtc_from4_errstat: Out of memory!\n");
+			er_stat = 1;
+			goto out;
+		}
+
+		/* recovery read */
+		rtn = nand_do_read(mtd, page, len, &retlen, buf);
+
+		/* if read failed or > 1-bit error corrected */
+		if (rtn || (mtd->ecc_stats.corrected - corrected) > 1) {
+			er_stat |= 1 << 1;
+		kfree(buf);
 	}
 
 	rtn = status;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index daacde5..0091649 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -479,14 +479,14 @@ #define NAND_BBT_SCAN2NDPAGE	0x00004000
 /* The maximum number of blocks to scan for a bbt */
 #define NAND_BBT_SCAN_MAXBLOCKS	4
 
-extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
-extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
-extern int nand_default_bbt (struct mtd_info *mtd);
-extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
-extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
-extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
-			     size_t * retlen, uint8_t * buf, uint8_t * oob_buf,
-			     struct nand_oobinfo *oobsel, int flags);
+extern int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
+extern int nand_default_bbt(struct mtd_info *mtd);
+extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
+extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+			   int allowbbt);
+extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
+			size_t * retlen, uint8_t * buf);
 
 /*
 * Constants for oob configuration



More information about the linux-mtd-cvs mailing list