[MTD] NAND Modularize read function
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Thu, 25 May 2006 08:07:16 +0000 (10:07 +0200)
committerDavid Woodhouse <dwmw2@infradead.org>
Thu, 25 May 2006 11:45:27 +0000 (12:45 +0100)
Split the core of the read function out and implement
seperate handling functions for software and hardware
ECC.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
drivers/mtd/nand/diskonchip.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/rtc_from4.c
include/linux/mtd/nand.h

index 2ec9080e2b14a52169ac55267edefbba4e95a921..83af6f05cd00075d80c4ef7b9d4df5c075c404f0 100644 (file)
@@ -968,12 +968,14 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsign
        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;
 
index 49bca242610bffc1df8e1862be46e1d83aeeaa37..21fce2bce4b2499172edd0990613af387d4c4ad8 100644 (file)
@@ -976,256 +976,224 @@ static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int p
 #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 *mtd, loff_t from, size_t len,
                        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 maxchips)
         */
        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 maxchips)
                               "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 maxchips)
        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 maxchips)
        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 maxchips)
                printk(KERN_WARNING "Invalid ecc parameters\n");
                BUG();
        }
+       chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
 
        /* Initialize state */
        chip->state = FL_READY;
index 6c97bfaea19ac163501e54907e3c124a8a4e1fb6..b7083104a05bad717af724acc0774ab21bd5df68 100644 (file)
@@ -444,7 +444,8 @@ static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_cha
  * 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_info *mtd, struct nand_chip *this, int s
        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;
index daacde5132fea7b4452c5639eac32ca6e4cf3895..00916498ea55e6de4c65b076cbb04f51222ff364 100644 (file)
@@ -479,14 +479,14 @@ struct nand_bbt_descr {
 /* 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