]> err.no Git - linux-2.6/commitdiff
[MTD] NAND modularize write function
authorThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Fri, 26 May 2006 16:52:08 +0000 (18:52 +0200)
committerThomas Gleixner <tglx@cruncher.tec.linutronix.de>
Fri, 26 May 2006 16:52:08 +0000 (18:52 +0200)
Modularize the write function and reorganaize the internal buffer
management. Remove obsolete chip options and fixup all affected
users.

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

index 83af6f05cd00075d80c4ef7b9d4df5c075c404f0..82262a4a4208636b08a6b6babb4392614d786ec9 100644 (file)
@@ -1666,7 +1666,7 @@ static int __init doc_probe(unsigned long physadr)
        nand->ecc.mode          = NAND_ECC_HW_SYNDROME;
        nand->ecc.size          = 512;
        nand->ecc.bytes         = 6;
-       nand->options           = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+       nand->options           = NAND_USE_FLASH_BBT;
 
        doc->physadr            = physadr;
        doc->virtadr            = virtadr;
index ba5a2174a4082c0ec09a6d6c76c17a67d5848127..516c0e5e564cb2b03a8a02a73e0c58a90798ced8 100644 (file)
@@ -198,9 +198,6 @@ static void __exit ep7312_cleanup(void)
        /* Release resources, unregister device */
        nand_release(ap7312_mtd);
 
-       /* Free internal data buffer */
-       kfree(this->data_buf);
-
        /* Free the MTD device structure */
        kfree(ep7312_mtd);
 }
index 2b29b47e2af46005a9de2be142d3573aa8103544..cead9fc4f99f72f98ad953582e8a16a929ef665b 100644 (file)
@@ -88,37 +88,8 @@ static uint8_t ffchars[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 };
 
-/*
- * NAND low-level MTD interface functions
- */
-static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
-static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len);
-static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len);
-
-static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
-                    size_t *retlen, uint8_t *buf);
-static int nand_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
-                        size_t *retlen, uint8_t *buf);
-static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
-                     size_t *retlen, const uint8_t *buf);
 static int nand_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
                          size_t *retlen, const uint8_t *buf);
-static int nand_erase(struct mtd_info *mtd, struct erase_info *instr);
-static void nand_sync(struct mtd_info *mtd);
-
-/* Some internal functions */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
-                          int page, uint8_t * oob_buf,
-                          struct nand_oobinfo *oobsel, int mode);
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip,
-                            int page, int numpages, uint8_t *oob_buf,
-                            struct nand_oobinfo *oobsel, int chipnr,
-                            int oobmode);
-#else
-#define nand_verify_pages(...) (0)
-#endif
-
 static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
                           int new_state);
 
@@ -262,7 +233,6 @@ static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
        for (i = 0; i < len; i++)
                if (buf[i] != readb(chip->IO_ADDR_R))
                        return -EFAULT;
-
        return 0;
 }
 
@@ -766,215 +736,6 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip, int state)
        return status;
 }
 
-/**
- * nand_write_page - [GENERIC] write one page
- * @mtd:       MTD device structure
- * @this:      NAND chip structure
- * @page:      startpage inside the chip, must be called with (page & chip->pagemask)
- * @oob_buf:   out of band data buffer
- * @oobsel:    out of band selecttion structre
- * @cached:    1 = enable cached programming if supported by chip
- *
- * Nand_page_program function is used for write and writev !
- * This function will always program a full page of data
- * If you call it with a non page aligned buffer, you're lost :)
- *
- * Cached programming is not supported yet.
- */
-static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, int page,
-                          uint8_t *oob_buf, struct nand_oobinfo *oobsel, int cached)
-{
-       int i, status;
-       uint8_t ecc_code[32];
-       int eccmode = oobsel->useecc ? chip->ecc.mode : NAND_ECC_NONE;
-       int *oob_config = oobsel->eccpos;
-       int datidx = 0, eccidx = 0, eccsteps = chip->ecc.steps;
-       int eccbytes = 0;
-
-       /* FIXME: Enable cached programming */
-       cached = 0;
-
-       /* Send command to begin auto page programming */
-       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
-
-       /* Write out complete page of data, take care of eccmode */
-       switch (eccmode) {
-               /* No ecc, write all */
-       case NAND_ECC_NONE:
-               printk(KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
-               chip->write_buf(mtd, chip->data_poi, mtd->writesize);
-               break;
-
-               /* Software ecc 3/256, write all */
-       case NAND_ECC_SOFT:
-               for (; eccsteps; eccsteps--) {
-                       chip->ecc.calculate(mtd, &chip->data_poi[datidx], ecc_code);
-                       for (i = 0; i < 3; i++, eccidx++)
-                               oob_buf[oob_config[eccidx]] = ecc_code[i];
-                       datidx += chip->ecc.size;
-               }
-               chip->write_buf(mtd, chip->data_poi, mtd->writesize);
-               break;
-       default:
-               eccbytes = chip->ecc.bytes;
-               for (; eccsteps; eccsteps--) {
-                       /* enable hardware ecc logic for write */
-                       chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
-                       chip->write_buf(mtd, &chip->data_poi[datidx], chip->ecc.size);
-                       chip->ecc.calculate(mtd, &chip->data_poi[datidx], ecc_code);
-                       for (i = 0; i < eccbytes; i++, eccidx++)
-                               oob_buf[oob_config[eccidx]] = ecc_code[i];
-                       /* If the hardware ecc provides syndromes then
-                        * the ecc code must be written immidiately after
-                        * the data bytes (words) */
-                       if (chip->options & NAND_HWECC_SYNDROME)
-                               chip->write_buf(mtd, ecc_code, eccbytes);
-                       datidx += chip->ecc.size;
-               }
-               break;
-       }
-
-       /* Write out OOB data */
-       if (chip->options & NAND_HWECC_SYNDROME)
-               chip->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
-       else
-               chip->write_buf(mtd, oob_buf, mtd->oobsize);
-
-       /* Send command to actually program the data */
-       chip->cmdfunc(mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
-
-       if (!cached) {
-               /* call wait ready function */
-               status = chip->waitfunc(mtd, chip, FL_WRITING);
-
-               /* See if operation failed and additional status checks are available */
-               if ((status & NAND_STATUS_FAIL) && (chip->errstat)) {
-                       status = chip->errstat(mtd, chip, FL_WRITING, status, page);
-               }
-
-               /* See if device thinks it succeeded */
-               if (status & NAND_STATUS_FAIL) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
-                       return -EIO;
-               }
-       } else {
-               /* FIXME: Implement cached programming ! */
-               /* wait until cache is ready */
-               // status = chip->waitfunc (mtd, this, FL_CACHEDRPG);
-       }
-       return 0;
-}
-
-#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
-/**
- * nand_verify_pages - [GENERIC] verify the chip contents after a write
- * @mtd:       MTD device structure
- * @this:      NAND chip structure
- * @page:      startpage inside the chip, must be called with (page & chip->pagemask)
- * @numpages:  number of pages to verify
- * @oob_buf:   out of band data buffer
- * @oobsel:    out of band selecttion structre
- * @chipnr:    number of the current chip
- * @oobmode:   1 = full buffer verify, 0 = ecc only
- *
- * The NAND device assumes that it is always writing to a cleanly erased page.
- * Hence, it performs its internal write verification only on bits that
- * transitioned from 1 to 0. The device does NOT verify the whole page on a
- * byte by byte basis. It is possible that the page was not completely erased
- * or the page is becoming unusable due to wear. The read with ECC would catch
- * the error later when the ECC page check fails, but we would rather catch
- * it early in the page write stage. Better to write no data than invalid data.
- */
-static int nand_verify_pages(struct mtd_info *mtd, struct nand_chip *chip, int page, int numpages,
-                            uint8_t *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
-{
-       int i, j, datidx = 0, oobofs = 0, res = -EIO;
-       int eccsteps = chip->ecc.steps;
-       int hweccbytes;
-       uint8_t oobdata[64];
-
-       hweccbytes = (chip->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
-
-       /* Send command to read back the first page */
-       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
-
-       for (;;) {
-               for (j = 0; j < eccsteps; j++) {
-                       /* Loop through and verify the data */
-                       if (chip->verify_buf(mtd, &chip->data_poi[datidx], mtd->eccsize)) {
-                               DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-                               goto out;
-                       }
-                       datidx += mtd->eccsize;
-                       /* Have we a hw generator layout ? */
-                       if (!hweccbytes)
-                               continue;
-                       if (chip->verify_buf(mtd, &chip->oob_buf[oobofs], hweccbytes)) {
-                               DEBUG(MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-                               goto out;
-                       }
-                       oobofs += hweccbytes;
-               }
-
-               /* check, if we must compare all data or if we just have to
-                * compare the ecc bytes
-                */
-               if (oobmode) {
-                       if (chip->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;
-                       }
-               } else {
-                       /* Read always, else autoincrement fails */
-                       chip->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
-
-                       if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
-                               int ecccnt = oobsel->eccbytes;
-
-                               for (i = 0; i < ecccnt; i++) {
-                                       int idx = oobsel->eccpos[i];
-                                       if (oobdata[idx] != oob_buf[oobofs + idx]) {
-                                               DEBUG(MTD_DEBUG_LEVEL0, "%s: Failed ECC write verify, page 0x%08x, %6i bytes were succesful\n",
-                                                     __FUNCTION__, page, i);
-                                               goto out;
-                                       }
-                               }
-                       }
-               }
-               oobofs += mtd->oobsize - hweccbytes * eccsteps;
-               page++;
-               numpages--;
-
-               /* 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.
-                * Do this also before returning, so the chip is
-                * ready for the next command.
-                */
-               if (!chip->dev_ready)
-                       udelay(chip->chip_delay);
-               else
-                       nand_wait_ready(mtd);
-
-               /* All done, return happy */
-               if (!numpages)
-                       return 0;
-
-               /* Check, if the chip supports auto page increment */
-               if (!NAND_CANAUTOINCR(chip))
-                       chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
-       }
-       /*
-        * Terminate the read command. We come here in case of an error
-        * So we must issue a reset command.
-        */
- out:
-       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-       return res;
-}
-#endif
-
 /**
  * nand_read_page_swecc - {REPLACABLE] software ecc based page read function
  * @mtd:       mtd info structure
@@ -988,12 +749,12 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        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;
+       uint8_t *ecc_calc = chip->buffers.ecccalc;
+       uint8_t *ecc_code = chip->buffers.ecccode;
        int *eccpos = chip->autooob->eccpos;
 
        chip->read_buf(mtd, buf, mtd->writesize);
-       chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
        if (chip->ecc.mode == NAND_ECC_NONE)
                return 0;
@@ -1002,7 +763,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
        for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_buf[eccpos[i]];
+               ecc_code[i] = chip->oob_poi[eccpos[i]];
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1034,8 +795,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
        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;
+       uint8_t *ecc_calc = chip->buffers.ecccalc;
+       uint8_t *ecc_code = chip->buffers.ecccode;
        int *eccpos = chip->autooob->eccpos;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
@@ -1043,10 +804,10 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                chip->read_buf(mtd, p, eccsize);
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
        }
-       chip->read_buf(mtd, chip->oob_buf, mtd->oobsize);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
 
        for (i = 0; i < chip->ecc.total; i++)
-               ecc_code[i] = chip->oob_buf[eccpos[i]];
+               ecc_code[i] = chip->oob_poi[eccpos[i]];
 
        eccsteps = chip->ecc.steps;
        p = buf;
@@ -1070,7 +831,7 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
  * @buf:       buffer to store read data
  *
  * The hw generator calculates the error syndrome automatically. Therefor
- * we need a special oob layout and .
+ * we need a special oob layout and handling.
  */
 static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
                                   uint8_t *buf)
@@ -1079,7 +840,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
        int eccbytes = chip->ecc.bytes;
        int eccsteps = chip->ecc.steps;
        uint8_t *p = buf;
-       uint8_t *oob = chip->oob_buf;
+       uint8_t *oob = chip->oob_poi;
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
                int stat;
@@ -1110,7 +871,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
        }
 
        /* Calculate remaining oob bytes */
-       i = oob - chip->oob_buf;
+       i = oob - chip->oob_poi;
        if (i)
                chip->read_buf(mtd, oob, i);
 
@@ -1149,6 +910,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
        page = realpage & chip->pagemask;
 
        col = (int)(from & (mtd->writesize - 1));
+       chip->oob_poi = chip->buffers.oobrbuf;
 
        while(1) {
                bytes = min(mtd->writesize - col, readlen);
@@ -1156,7 +918,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
 
                /* Is the current page in the buffer ? */
                if (realpage != chip->pagebuf) {
-                       bufpoi = aligned ? buf : chip->data_buf;
+                       bufpoi = aligned ? buf : chip->buffers.databuf;
 
                        if (likely(sndcmd)) {
                                chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
@@ -1171,7 +933,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
                        /* Transfer not aligned data */
                        if (!aligned) {
                                chip->pagebuf = realpage;
-                               memcpy(buf, chip->data_buf + col, bytes);
+                               memcpy(buf, chip->buffers.databuf + col, bytes);
                        }
 
                        if (!(chip->options & NAND_NO_READRDY)) {
@@ -1188,7 +950,7 @@ int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
                                        nand_wait_ready(mtd);
                        }
                } else
-                       memcpy(buf, chip->data_buf + col, bytes);
+                       memcpy(buf, chip->buffers.databuf + col, bytes);
 
                buf += bytes;
                readlen -= bytes;
@@ -1392,10 +1154,11 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
        blockcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
 
        while (len) {
-               if (sndcmd)
+               if (likely(sndcmd)) {
                        chip->cmdfunc(mtd, NAND_CMD_READ0, 0,
                                      page & chip->pagemask);
-               sndcmd = 0;
+                       sndcmd = 0;
+               }
 
                chip->read_buf(mtd, &buf[cnt], pagesize);
 
@@ -1403,10 +1166,12 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
                cnt += pagesize;
                page++;
 
-               if (!chip->dev_ready)
-                       udelay(chip->chip_delay);
-               else
-                       nand_wait_ready(mtd);
+               if (!(chip->options & NAND_NO_READRDY)) {
+                       if (!chip->dev_ready)
+                               udelay(chip->chip_delay);
+                       else
+                               nand_wait_ready(mtd);
+               }
 
                /*
                 * Check, if the chip supports auto page increment or if we
@@ -1422,112 +1187,156 @@ int nand_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len,
 }
 
 /**
- * nand_write_raw - [GENERIC] Write raw data including oob
- * @mtd:       MTD device structure
- * @buf:       source buffer
- * @to:                offset to write to
- * @len:       number of bytes to write
- * @buf:       source buffer
- * @oob:       oob buffer
- *
- * Write raw data including oob
+ * nand_write_page_swecc - {REPLACABLE] software ecc based page write function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
  */
-int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
-                  uint8_t *buf, uint8_t *oob)
+static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+                                 const uint8_t *buf)
 {
-       struct nand_chip *chip = mtd->priv;
-       int page = (int)(to >> chip->page_shift);
-       int chipnr = (int)(to >> chip->chip_shift);
-       int ret;
+       int i, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *ecc_calc = chip->buffers.ecccalc;
+       const uint8_t *p = buf;
+       int *eccpos = chip->autooob->eccpos;
 
-       *retlen = 0;
+       if (chip->ecc.mode != NAND_ECC_NONE) {
+               /* Software ecc calculation */
+               for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+                       chip->ecc.calculate(mtd, p, &ecc_calc[i]);
 
-       /* Do not allow writes past end of device */
-       if ((to + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write "
-                     "beyond end of device\n");
-               return -EINVAL;
+               for (i = 0; i < chip->ecc.total; i++)
+                       chip->oob_poi[eccpos[i]] = ecc_calc[i];
        }
 
-       /* Grab the lock and see if the device is available */
-       nand_get_device(chip, mtd, FL_WRITING);
+       chip->write_buf(mtd, buf, mtd->writesize);
+       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
 
-       chip->select_chip(mtd, chipnr);
-       chip->data_poi = buf;
+/**
+ * nand_write_page_hwecc - {REPLACABLE] hardware ecc based page write function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+                                 const uint8_t *buf)
+{
+       int i, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *ecc_calc = chip->buffers.ecccalc;
+       const uint8_t *p = buf;
+       int *eccpos = chip->autooob->eccpos;
 
-       while (len != *retlen) {
-               ret = nand_write_page(mtd, chip, page, oob, &mtd->oobinfo, 0);
-               if (ret)
-                       return ret;
-               page++;
-               *retlen += mtd->writesize;
-               chip->data_poi += mtd->writesize;
-               oob += mtd->oobsize;
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+               chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+               chip->write_buf(mtd, p, mtd->writesize);
+               chip->ecc.calculate(mtd, p, &ecc_calc[i]);
        }
 
-       /* Deselect and wake up anyone waiting on the device */
-       nand_release_device(mtd);
-       return 0;
+       for (i = 0; i < chip->ecc.total; i++)
+               chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+       chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 }
-EXPORT_SYMBOL_GPL(nand_write_raw);
 
 /**
- * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
- * @mtd:       MTD device structure
- * @fsbuf:     buffer given by fs driver
- * @oobsel:    out of band selection structre
- * @autoplace: 1 = place given buffer into the oob bytes
- * @numpages:  number of pages to prepare
- *
- * Return:
- * 1. Filesystem buffer available and autoplacement is off,
- *    return filesystem buffer
- * 2. No filesystem buffer or autoplace is off, return internal
- *    buffer
- * 3. Filesystem buffer is given and autoplace selected
- *    put data from fs buffer into internal buffer and
- *    retrun internal buffer
- *
- * Note: The internal buffer is filled with 0xff. This must
- * be done only once, when no autoplacement happens
- * Autoplacement sets the buffer dirty flag, which
- * forces the 0xff fill before using the buffer again.
+ * nand_write_page_syndrome - {REPLACABLE] hardware ecc syndrom based page write
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
  *
-*/
-static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct nand_oobinfo *oobsel,
-                                  int autoplace, int numpages)
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static void nand_write_page_syndrome(struct mtd_info *mtd,
+                                   struct nand_chip *chip, const uint8_t *buf)
 {
-       struct nand_chip *chip = mtd->priv;
-       int i, len, ofs;
+       int i, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       const uint8_t *p = buf;
+       uint8_t *oob = chip->oob_poi;
 
-       /* Zero copy fs supplied buffer */
-       if (fsbuf && !autoplace)
-               return fsbuf;
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
 
-       /* Check, if the buffer must be filled with ff again */
-       if (chip->oobdirty) {
-               memset(chip->oob_buf, 0xff, mtd->oobsize << (chip->phys_erase_shift - chip->page_shift));
-               chip->oobdirty = 0;
-       }
+               chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+               chip->write_buf(mtd, p, eccsize);
 
-       /* If we have no autoplacement or no fs buffer use the internal one */
-       if (!autoplace || !fsbuf)
-               return chip->oob_buf;
-
-       /* Walk through the pages and place the data */
-       chip->oobdirty = 1;
-       ofs = 0;
-       while (numpages--) {
-               for (i = 0, len = 0; len < mtd->oobavail; i++) {
-                       int to = ofs + oobsel->oobfree[i][0];
-                       int num = oobsel->oobfree[i][1];
-                       memcpy(&chip->oob_buf[to], fsbuf, num);
-                       len += num;
-                       fsbuf += num;
+               if (chip->ecc.prepad) {
+                       chip->write_buf(mtd, oob, chip->ecc.prepad);
+                       oob += chip->ecc.prepad;
+               }
+
+               chip->ecc.calculate(mtd, p, oob);
+               chip->write_buf(mtd, oob, eccbytes);
+               oob += eccbytes;
+
+               if (chip->ecc.postpad) {
+                       chip->write_buf(mtd, oob, chip->ecc.postpad);
+                       oob += chip->ecc.postpad;
                }
-               ofs += mtd->oobavail;
        }
-       return chip->oob_buf;
+
+       /* Calculate remaining oob bytes */
+       i = oob - chip->oob_poi;
+       if (i)
+               chip->write_buf(mtd, oob, i);
+}
+
+/**
+ * nand_write_page - [INTERNAL] write one page
+ * @mtd:       MTD device structure
+ * @chip:      NAND chip descriptor
+ * @buf:       the data to write
+ * @page:      page number to write
+ * @cached:    cached programming
+ */
+static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+                          const uint8_t *buf, int page, int cached)
+{
+       int status;
+
+       chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+       chip->ecc.write_page(mtd, chip, buf);
+
+       /*
+        * Cached progamming disabled for now, Not sure if its worth the
+        * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+        */
+       cached = 0;
+
+       if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+               chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+               status = chip->waitfunc(mtd, chip, FL_WRITING);
+               /*
+                * See if operation failed and additional status checks are
+                * available
+                */
+               if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+                       status = chip->errstat(mtd, chip, FL_WRITING, status,
+                                              page);
+
+               if (status & NAND_STATUS_FAIL)
+                       return -EIO;
+       } else {
+               chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+               status = chip->waitfunc(mtd, chip, FL_WRITING);
+       }
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+       /* Send command to read back the data */
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       if (chip->verify_buf(mtd, buf, mtd->writesize))
+               return -EIO;
+#endif
+       return 0;
 }
 
 #define NOTALIGNED(x) (x & (mtd->writesize-1)) != 0
@@ -1545,137 +1354,128 @@ static uint8_t *nand_prepare_oobbuf(struct mtd_info *mtd, uint8_t *fsbuf, struct
 static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
                          size_t *retlen, const uint8_t *buf)
 {
-       int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
-       int autoplace = 0, numpages, totalpages;
+       int chipnr, realpage, page, blockmask;
        struct nand_chip *chip = mtd->priv;
-       uint8_t *oobbuf, *bufstart, *eccbuf = NULL;
-       int ppblock = (1 << (chip->phys_erase_shift - chip->page_shift));
-       struct nand_oobinfo *oobsel = &mtd->oobinfo;
-
-       DEBUG(MTD_DEBUG_LEVEL3, "nand_write: to = 0x%08x, len = %i\n", (unsigned int)to, (int)len);
+       uint32_t writelen = len;
+       int bytes = mtd->writesize;
+       int ret = -EIO;
 
-       /* Initialize retlen, in case of early exit */
        *retlen = 0;
 
        /* Do not allow write past end of device */
        if ((to + len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write: Attempt to write past end of page\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_write: "
+                     "Attempt to write past end of page\n");
                return -EINVAL;
        }
 
        /* reject writes, which are not page aligned */
        if (NOTALIGNED(to) || NOTALIGNED(len)) {
-               printk(KERN_NOTICE "nand_write: Attempt to write not page aligned data\n");
+               printk(KERN_NOTICE "nand_write: "
+                      "Attempt to write not page aligned data\n");
                return -EINVAL;
        }
 
-       /* Grab the lock and see if the device is available */
-       nand_get_device(chip, mtd, FL_WRITING);
+       if (!len)
+               return 0;
 
-       /* Calculate chipnr */
-       chipnr = (int)(to >> chip->chip_shift);
-       /* Select the NAND device */
-       chip->select_chip(mtd, chipnr);
+       nand_get_device(chip, mtd, FL_WRITING);
 
        /* Check, if it is write protected */
        if (nand_check_wp(mtd))
                goto out;
 
-       /* Autoplace of oob data ? Use the default placement scheme */
-       if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
-               oobsel = chip->autooob;
-               autoplace = 1;
-       }
-       if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
-               autoplace = 1;
+       chipnr = (int)(to >> chip->chip_shift);
+       chip->select_chip(mtd, chipnr);
 
-       /* Setup variables and oob buffer */
-       totalpages = len >> chip->page_shift;
-       page = (int)(to >> chip->page_shift);
-       /* Invalidate the page cache, if we write to the cached page */
-       if (page <= chip->pagebuf && chip->pagebuf < (page + totalpages))
+       realpage = (int)(to >> chip->page_shift);
+       page = realpage & chip->pagemask;
+       blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
+       /* Invalidate the page cache, when we write to the cached page */
+       if (to <= (chip->pagebuf << chip->page_shift) &&
+           (chip->pagebuf << chip->page_shift) < (to + len))
                chip->pagebuf = -1;
 
-       /* Set it relative to chip */
-       page &= chip->pagemask;
-       startpage = page;
-       /* Calc number of pages we can write in one go */
-       numpages = min(ppblock - (startpage & (ppblock - 1)), totalpages);
-       oobbuf = nand_prepare_oobbuf(mtd, eccbuf, oobsel, autoplace, numpages);
-       bufstart = (uint8_t *) buf;
-
-       /* Loop until all data is written */
-       while (written < len) {
-
-               chip->data_poi = (uint8_t *) &buf[written];
-               /* Write one page. If this is the last page to write
-                * or the last page in this block, then use the
-                * real pageprogram command, else select cached programming
-                * if supported by the chip.
-                */
-               ret = nand_write_page(mtd, chip, page, &oobbuf[oob], oobsel, (--numpages > 0));
-               if (ret) {
-                       DEBUG(MTD_DEBUG_LEVEL0, "nand_write: write_page failed %d\n", ret);
-                       goto out;
-               }
-               /* Next oob page */
-               oob += mtd->oobsize;
-               /* Update written bytes count */
-               written += mtd->writesize;
-               if (written == len)
-                       goto cmp;
+       chip->oob_poi = chip->buffers.oobwbuf;
 
-               /* Increment page address */
-               page++;
+       while(1) {
+               int cached = writelen > bytes && page != blockmask;
 
-               /* Have we hit a block boundary ? Then we have to verify and
-                * if verify is ok, we have to setup the oob buffer for
-                * the next pages.
-                */
-               if (!(page & (ppblock - 1))) {
-                       int ofs;
-                       chip->data_poi = bufstart;
-                       ret = nand_verify_pages(mtd, chip, startpage, page - startpage,
-                                               oobbuf, oobsel, chipnr, (eccbuf != NULL));
-                       if (ret) {
-                               DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret);
-                               goto out;
-                       }
-                       *retlen = written;
-
-                       ofs = autoplace ? mtd->oobavail : mtd->oobsize;
-                       if (eccbuf)
-                               eccbuf += (page - startpage) * ofs;
-                       totalpages -= page - startpage;
-                       numpages = min(totalpages, ppblock);
-                       page &= chip->pagemask;
-                       startpage = page;
-                       oobbuf = nand_prepare_oobbuf(mtd, eccbuf, oobsel, autoplace, numpages);
-                       oob = 0;
-                       /* Check, if we cross a chip boundary */
-                       if (!page) {
-                               chipnr++;
-                               chip->select_chip(mtd, -1);
-                               chip->select_chip(mtd, chipnr);
-                       }
+               ret = nand_write_page(mtd, chip, buf, page, cached);
+               if (ret)
+                       break;
+
+               writelen -= bytes;
+               if (!writelen)
+                       break;
+
+               buf += bytes;
+               realpage++;
+
+               page = realpage & chip->pagemask;
+               /* Check, if we cross a chip boundary */
+               if (!page) {
+                       chipnr++;
+                       chip->select_chip(mtd, -1);
+                       chip->select_chip(mtd, chipnr);
                }
        }
-       /* Verify the remaining pages */
- cmp:
-       chip->data_poi = bufstart;
-       ret = nand_verify_pages(mtd, chip, startpage, totalpages, oobbuf, oobsel, chipnr, (eccbuf != NULL));
-       if (!ret)
-               *retlen = written;
-       else
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_write: verify_pages failed %d\n", ret);
-
  out:
-       /* Deselect and wake up anyone waiting on the device */
+       *retlen = len - writelen;
        nand_release_device(mtd);
-
        return ret;
 }
 
+/**
+ * nand_write_raw - [GENERIC] Write raw data including oob
+ * @mtd:       MTD device structure
+ * @buf:       source buffer
+ * @to:                offset to write to
+ * @len:       number of bytes to write
+ * @buf:       source buffer
+ * @oob:       oob buffer
+ *
+ * Write raw data including oob
+ */
+int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen,
+                  const uint8_t *buf, uint8_t *oob)
+{
+       struct nand_chip *chip = mtd->priv;
+       int page = (int)(to >> chip->page_shift);
+       int chipnr = (int)(to >> chip->chip_shift);
+       int ret;
+
+       *retlen = 0;
+
+       /* Do not allow writes past end of device */
+       if ((to + len) > mtd->size) {
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt write "
+                     "beyond end of device\n");
+               return -EINVAL;
+       }
+
+       /* Grab the lock and see if the device is available */
+       nand_get_device(chip, mtd, FL_WRITING);
+
+       chip->select_chip(mtd, chipnr);
+       chip->oob_poi = oob;
+
+       while (len != *retlen) {
+               ret = nand_write_page(mtd, chip, buf, page, 0);
+               if (ret)
+                       return ret;
+               page++;
+               *retlen += mtd->writesize;
+               buf += mtd->writesize;
+               chip->oob_poi += mtd->oobsize;
+       }
+
+       /* Deselect and wake up anyone waiting on the device */
+       nand_release_device(mtd);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nand_write_raw);
 
 /**
  * nand_write_oob - [MTD Interface] NAND write out-of-band
@@ -2081,64 +1881,6 @@ static void nand_resume(struct mtd_info *mtd)
                       "in suspended state\n");
 }
 
-/*
- * Free allocated data structures
- */
-static void nand_free_kmem(struct nand_chip *chip)
-{
-       /* Buffer allocated by nand_scan ? */
-       if (chip->options & NAND_OOBBUF_ALLOC)
-               kfree(chip->oob_buf);
-       /* Buffer allocated by nand_scan ? */
-       if (chip->options & NAND_DATABUF_ALLOC)
-               kfree(chip->data_buf);
-       /* Controller allocated by nand_scan ? */
-       if (chip->options & NAND_CONTROLLER_ALLOC)
-               kfree(chip->controller);
-}
-
-/*
- * Allocate buffers and data structures
- */
-static int nand_allocate_kmem(struct mtd_info *mtd, struct nand_chip *chip)
-{
-       size_t len;
-
-       if (!chip->oob_buf) {
-               len = mtd->oobsize <<
-                       (chip->phys_erase_shift - chip->page_shift);
-               chip->oob_buf = kmalloc(len, GFP_KERNEL);
-               if (!chip->oob_buf)
-                       goto outerr;
-               chip->options |= NAND_OOBBUF_ALLOC;
-       }
-
-       if (!chip->data_buf) {
-               len = mtd->writesize + mtd->oobsize;
-               chip->data_buf = kmalloc(len, GFP_KERNEL);
-               if (!chip->data_buf)
-                       goto outerr;
-               chip->options |= NAND_DATABUF_ALLOC;
-       }
-
-       if (!chip->controller) {
-               chip->controller = kzalloc(sizeof(struct nand_hw_control),
-                                          GFP_KERNEL);
-               if (!chip->controller)
-                       goto outerr;
-
-               spin_lock_init(&chip->controller->lock);
-               init_waitqueue_head(&chip->controller->wq);
-               chip->options |= NAND_CONTROLLER_ALLOC;
-       }
-       return 0;
-
- outerr:
-       printk(KERN_ERR "nand_scan(): Cannot allocate buffers\n");
-       nand_free_kmem(chip);
-       return -ENOMEM;
-}
-
 /*
  * Set default functions
  */
@@ -2174,6 +1916,13 @@ static void nand_set_defaults(struct nand_chip *chip, int busw)
                chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
        if (!chip->scan_bbt)
                chip->scan_bbt = nand_default_bbt;
+
+       if (!chip->controller) {
+               chip->controller = &chip->hwcontrol;
+               spin_lock_init(&chip->controller->lock);
+               init_waitqueue_head(&chip->controller->wq);
+       }
+
 }
 
 /*
@@ -2321,8 +2070,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
  * This fills out all the uninitialized function pointers
  * with the defaults.
  * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values. Buffers are allocated if
- * they are not provided by the board driver
+ * filled with the appropriate values.
  * The mtd->owner field must be set to the module of the caller
  *
  */
@@ -2369,13 +2117,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
        chip->numchips = i;
        mtd->size = i * chip->chipsize;
 
-       /* Allocate buffers and data structures */
-       if (nand_allocate_kmem(mtd, chip))
-               return -ENOMEM;
-
-       /* Preset the internal oob buffer */
-       memset(chip->oob_buf, 0xff,
-              mtd->oobsize << (chip->phys_erase_shift - chip->page_shift));
+       /* Preset the internal oob write buffer */
+       memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize);
 
        /*
         * If no default placement scheme is given, select an appropriate one
@@ -2415,6 +2158,8 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                /* Use standard hwecc read page function ? */
                if (!chip->ecc.read_page)
                        chip->ecc.read_page = nand_read_page_hwecc;
+               if (!chip->ecc.write_page)
+                       chip->ecc.write_page = nand_write_page_hwecc;
 
        case NAND_ECC_HW_SYNDROME:
                if (!chip->ecc.calculate || !chip->ecc.correct ||
@@ -2423,9 +2168,11 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                               "Hardware ECC not possible\n");
                        BUG();
                }
-               /* Use standard syndrome read page function ? */
+               /* Use standard syndrome read/write page function ? */
                if (!chip->ecc.read_page)
                        chip->ecc.read_page = nand_read_page_syndrome;
+               if (!chip->ecc.write_page)
+                       chip->ecc.write_page = nand_write_page_syndrome;
 
                if (mtd->writesize >= chip->ecc.size)
                        break;
@@ -2438,6 +2185,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                chip->ecc.calculate = nand_calculate_ecc;
                chip->ecc.correct = nand_correct_data;
                chip->ecc.read_page = nand_read_page_swecc;
+               chip->ecc.write_page = nand_write_page_swecc;
                chip->ecc.size = 256;
                chip->ecc.bytes = 3;
                break;
@@ -2446,6 +2194,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
                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.write_page = nand_write_page_swecc;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
                break;
@@ -2522,8 +2271,6 @@ void nand_release(struct mtd_info *mtd)
 
        /* Free bad block table memory */
        kfree(chip->bbt);
-       /* Free buffers */
-       nand_free_kmem(chip);
 }
 
 EXPORT_SYMBOL_GPL(nand_scan);
index ecaaca18d1e0122e080a0974a935c5a7b3bda3c3..40f99304df7675e548078ce37f33d9a0b7cf5b28 100644 (file)
@@ -666,7 +666,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
        struct nand_chip *this = mtd->priv;
 
        bd->options &= ~NAND_BBT_SCANEMPTY;
-       return create_bbt(mtd, this->data_buf, bd, -1);
+       return create_bbt(mtd, this->buffers.databuf, bd, -1);
 }
 
 /**
index b7083104a05bad717af724acc0774ab21bd5df68..de6de91fbad94ab10033b29cf42bd8ac265c14d8 100644 (file)
@@ -571,7 +571,6 @@ static int __init rtc_from4_init(void)
        this->ecc.mode = NAND_ECC_HW_SYNDROME;
        this->ecc.size = 512;
        this->ecc.bytes = 8;
-       this->options |= NAND_HWECC_SYNDROME;
        /* return the status of extra status and ECC checks */
        this->errstat = rtc_from4_errstat;
        /* set the nand_oobinfo to support FPGA H/W error detection */
index a9cf0190c27a6303c79aea4bdb8dfa163446fb48..f9e2d4a0ab8c661bba8dec6e46f1e194433eecfd 100644 (file)
@@ -175,8 +175,6 @@ static int __init toto_init(void)
 
        goto out;
 
- out_buf:
-       kfree(this->data_buf);
  out_mtd:
        kfree(toto_mtd);
  out:
index 00916498ea55e6de4c65b076cbb04f51222ff364..1a749ba6130fedd6e1e24d2db56079ab49e4083f 100644 (file)
@@ -37,7 +37,7 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from,
 
 
 extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len,
-                         size_t *retlen, uint8_t *buf, uint8_t *oob);
+                         size_t *retlen, const uint8_t *buf, uint8_t *oob);
 
 /* The maximum number of NAND chips in an array */
 #define NAND_MAX_CHIPS         8
@@ -47,6 +47,7 @@ extern int nand_write_raw(struct mtd_info *mtd, loff_t to, size_t len,
  * adjust this accordingly.
  */
 #define NAND_MAX_OOBSIZE       64
+#define NAND_MAX_PAGESIZE      2048
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
@@ -181,20 +182,12 @@ typedef enum {
 /* Use a flash based bad block table. This option is passed to the
  * default bad block table function. */
 #define NAND_USE_FLASH_BBT     0x00010000
-/* The hw ecc generator provides a syndrome instead a ecc value on read
- * This can only work if we have the ecc bytes directly behind the
- * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
-#define NAND_HWECC_SYNDROME    0x00020000
 /* This option skips the bbt scan during initialization. */
-#define NAND_SKIP_BBTSCAN      0x00040000
+#define NAND_SKIP_BBTSCAN      0x00020000
 
 /* Options set by nand scan */
 /* Nand scan has allocated controller struct */
-#define NAND_CONTROLLER_ALLOC  0x20000000
-/* Nand scan has allocated oob_buf */
-#define NAND_OOBBUF_ALLOC      0x40000000
-/* Nand scan has allocated data_buf */
-#define NAND_DATABUF_ALLOC     0x80000000
+#define NAND_CONTROLLER_ALLOC  0x80000000
 
 
 /*
@@ -240,6 +233,7 @@ struct nand_hw_control {
  *             be provided if an hardware ECC is available
  * @calculate: function for ecc calculation or readback from ecc hardware
  * @correct:   function for ecc correction, matching to ecc generator (sw/hw)
+ * @read_page: function to read a page according to the ecc generator requirements
  * @write_page:        function to write a page according to the ecc generator requirements
  */
 struct nand_ecc_ctrl {
@@ -260,9 +254,28 @@ struct nand_ecc_ctrl {
        int                     (*read_page)(struct mtd_info *mtd,
                                             struct nand_chip *chip,
                                             uint8_t *buf);
-       int                     (*write_page)(struct mtd_info *mtd,
+       void                    (*write_page)(struct mtd_info *mtd,
                                              struct nand_chip *chip,
-                                             uint8_t *buf, int cached);
+                                             const uint8_t *buf);
+};
+
+/**
+ * struct nand_buffers - buffer structure for read/write
+ * @ecccalc:   buffer for calculated ecc
+ * @ecccode:   buffer for ecc read from flash
+ * @oobwbuf:   buffer for write oob data
+ * @databuf:   buffer for data - dynamically sized
+ * @oobrbuf:   buffer to read oob data
+ *
+ * Do not change the order of buffers. databuf and oobrbuf must be in
+ * consecutive order.
+ */
+struct nand_buffers {
+       uint8_t ecccalc[NAND_MAX_OOBSIZE];
+       uint8_t ecccode[NAND_MAX_OOBSIZE];
+       uint8_t oobwbuf[NAND_MAX_OOBSIZE];
+       uint8_t databuf[NAND_MAX_PAGESIZE];
+       uint8_t oobrbuf[NAND_MAX_OOBSIZE];
 };
 
 /**
@@ -294,8 +307,8 @@ struct nand_ecc_ctrl {
  * @phys_erase_shift:  [INTERN] number of address bits in a physical eraseblock
  * @bbt_erase_shift:   [INTERN] number of address bits in a bbt entry
  * @chip_shift:                [INTERN] number of address bits in one chip
- * @data_buf:          [INTERN] internal buffer for one page + oob
- * @oob_buf:           [INTERN] oob buffer for one eraseblock
+ * @datbuf:            [INTERN] internal buffer for one page + oob
+ * @oobbuf:            [INTERN] oob buffer for one eraseblock
  * @oobdirty:          [INTERN] indicates that oob_buf must be reinitialized
  * @data_poi:          [INTERN] pointer to a data buffer
  * @options:           [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
@@ -336,32 +349,38 @@ struct nand_chip {
        int             (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state);
        void            (*erase_cmd)(struct mtd_info *mtd, int page);
        int             (*scan_bbt)(struct mtd_info *mtd);
-       struct nand_ecc_ctrl ecc;
+       int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
+
        int             chip_delay;
-       wait_queue_head_t wq;
-       nand_state_t    state;
+       unsigned int    options;
+
        int             page_shift;
        int             phys_erase_shift;
        int             bbt_erase_shift;
        int             chip_shift;
-       uint8_t         *data_buf;
-       uint8_t         *oob_buf;
-       int             oobdirty;
-       uint8_t         *data_poi;
-       unsigned int    options;
-       int             badblockpos;
        int             numchips;
        unsigned long   chipsize;
        int             pagemask;
        int             pagebuf;
+       int             badblockpos;
+
+       nand_state_t    state;
+
+       uint8_t         *oob_poi;
+       struct nand_hw_control  *controller;
        struct nand_oobinfo     *autooob;
+
+       struct nand_ecc_ctrl ecc;
+       struct nand_buffers buffers;
+       struct nand_hw_control hwcontrol;
+
        uint8_t         *bbt;
        struct nand_bbt_descr   *bbt_td;
        struct nand_bbt_descr   *bbt_md;
+
        struct nand_bbt_descr   *badblock_pattern;
-       struct nand_hw_control  *controller;
+
        void            *priv;
-       int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
 };
 
 /*