]> git.ipfire.org Git - people/ms/u-boot.git/blobdiff - drivers/mtd/nand/nand_base.c
nand: remove dead code and suspend/resume
[people/ms/u-boot.git] / drivers / mtd / nand / nand_base.c
index aeb179731d662925b4a99b6d964bdec9f3f584db..21cc5a39407d29f74ddb987f6d0373632fa6afba 100644 (file)
@@ -7,7 +7,7 @@
  *   Basic support for AG-AND chips is provided.
  *
  *     Additional technical information is available on
- *     http://www.linux-mtd.infradead.org/tech/nand.html
+ *     http://www.linux-mtd.infradead.org/doc/nand.html
  *
  *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *               2002-2006 Thomas Gleixner (tglx@linutronix.de)
@@ -24,6 +24,7 @@
  *     if we have HW ecc support.
  *     The AG-AND chips have nice features for speed improvement,
  *     which are not supported yet. Read / program 4 pages in one go.
+ *     BBT table is not serialized, has to be fixed
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  *
  */
 
-/* XXX U-BOOT XXX */
-#if 0
-#include <linux/module.h>
-#include <linux/delay.h>
-#include <linux/errno.h>
-#include <linux/err.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/mtd/mtd.h>
-#include <linux/mtd/nand.h>
-#include <linux/mtd/nand_ecc.h>
-#include <linux/mtd/compatmac.h>
-#include <linux/interrupt.h>
-#include <linux/bitops.h>
-#include <linux/leds.h>
-#include <asm/io.h>
-
-#ifdef CONFIG_MTD_PARTITIONS
-#include <linux/mtd/partitions.h>
-#endif
-
-#endif
-
 #include <common.h>
 
 #define ENOTSUPP       524     /* Operation is not supported */
 
-#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
-
 #include <malloc.h>
 #include <watchdog.h>
 #include <linux/err.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nand_ecc.h>
 
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
 #include <asm/io.h>
 #include <asm/errno.h>
 
-#ifdef CONFIG_JFFS2_NAND
-#include <jffs2/jffs2.h>
+/*
+ * CONFIG_SYS_NAND_RESET_CNT is used as a timeout mechanism when resetting
+ * a flash.  NAND flash is initialized prior to interrupts so standard timers
+ * can't be used.  CONFIG_SYS_NAND_RESET_CNT should be set to a value
+ * which is greater than (max NAND reset time / NAND status read time).
+ * A conservative default of 200000 (500 us / 25 ns) is used as a default.
+ */
+#ifndef CONFIG_SYS_NAND_RESET_CNT
+#define CONFIG_SYS_NAND_RESET_CNT 200000
 #endif
 
 /* Define default oob placement schemes for large and small page devices */
@@ -129,44 +115,17 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
 
-/*
- * For devices which display every fart in the system on a seperate LED. Is
- * compiled away when LED support is disabled.
- */
-/* XXX U-BOOT XXX */
-#if 0
-DEFINE_LED_TRIGGER(nand_led_trigger);
-#endif
-
 /**
  * nand_release_device - [GENERIC] release chip
  * @mtd:       MTD device structure
  *
  * Deselect, release chip lock and wake up anyone waiting on the device
  */
-/* XXX U-BOOT XXX */
-#if 0
-static void nand_release_device(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd->priv;
-
-       /* De-select the NAND device */
-       chip->select_chip(mtd, -1);
-
-       /* Release the controller and the chip */
-       spin_lock(&chip->controller->lock);
-       chip->controller->active = NULL;
-       chip->state = FL_READY;
-       wake_up(&chip->controller->wq);
-       spin_unlock(&chip->controller->lock);
-}
-#else
 static void nand_release_device (struct mtd_info *mtd)
 {
        struct nand_chip *this = mtd->priv;
        this->select_chip(mtd, -1);     /* De-select the NAND device */
 }
-#endif
 
 /**
  * nand_read_byte - [DEFAULT] read one byte from the chip
@@ -414,6 +373,7 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
                /* We write two bytes, so we dont have to mess with 16 bit
                 * access
                 */
+               nand_get_device(chip, mtd, FL_WRITING);
                ofs += mtd->oobsize;
                chip->ops.len = chip->ops.ooblen = 2;
                chip->ops.datbuf = NULL;
@@ -421,9 +381,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
                chip->ops.ooboffs = chip->badblockpos & ~0x01;
 
                ret = nand_do_write_oob(mtd, ofs, &chip->ops);
+               nand_release_device(mtd);
        }
        if (!ret)
                mtd->ecc_stats.badblocks++;
+
        return ret;
 }
 
@@ -457,6 +419,11 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
 {
        struct nand_chip *chip = mtd->priv;
 
+       if (!(chip->options & NAND_BBT_SCANNED)) {
+               chip->options |= NAND_BBT_SCANNED;
+               chip->scan_bbt(mtd);
+       }
+
        if (!chip->bbt)
                return chip->block_bad(mtd, ofs, getchip);
 
@@ -468,30 +435,20 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
  * Wait for the ready pin, after a command
  * The timeout is catched later.
  */
-/* XXX U-BOOT XXX */
-#if 0
 void nand_wait_ready(struct mtd_info *mtd)
 {
        struct nand_chip *chip = mtd->priv;
-       unsigned long timeo = jiffies + 2;
+       u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
+
+       reset_timer();
 
-       led_trigger_event(nand_led_trigger, LED_FULL);
        /* wait until command is processed or timeout occures */
-       do {
-               if (chip->dev_ready(mtd))
-                       break;
-               touch_softlockup_watchdog();
-       } while (time_before(jiffies, timeo));
-       led_trigger_event(nand_led_trigger, LED_OFF);
-}
-EXPORT_SYMBOL_GPL(nand_wait_ready);
-#else
-void nand_wait_ready(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd->priv;
-       nand_wait(mtd, chip);
+       while (get_timer(0) < timeo) {
+               if (chip->dev_ready)
+                       if (chip->dev_ready(mtd))
+                               break;
+       }
 }
-#endif
 
 /**
  * nand_command - [DEFAULT] Send command to NAND device
@@ -508,6 +465,7 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
 {
        register struct nand_chip *chip = mtd->priv;
        int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+       uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT;
 
        /*
         * Write out the command to the device.
@@ -574,7 +532,8 @@ static void nand_command(struct mtd_info *mtd, unsigned int command,
                               NAND_CTRL_CLE | NAND_CTRL_CHANGE);
                chip->cmd_ctrl(mtd,
                               NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
-               while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
+               while (!(chip->read_byte(mtd) & NAND_STATUS_READY) &&
+                       (rst_sts_cnt--));
                return;
 
                /* This applies to read commands */
@@ -610,6 +569,7 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
                            int column, int page_addr)
 {
        register struct nand_chip *chip = mtd->priv;
+       uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT;
 
        /* Emulate NAND_CMD_READOOB */
        if (command == NAND_CMD_READOOB) {
@@ -680,7 +640,8 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
                               NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
                chip->cmd_ctrl(mtd, NAND_CMD_NONE,
                               NAND_NCE | NAND_CTRL_CHANGE);
-               while (!(chip->read_byte(mtd) & NAND_STATUS_READY)) ;
+               while (!(chip->read_byte(mtd) & NAND_STATUS_READY) &&
+                       (rst_sts_cnt--));
                return;
 
        case NAND_CMD_RNDOUT:
@@ -724,44 +685,11 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
  *
  * Get the device and lock it for exclusive access
  */
-/* XXX U-BOOT XXX */
-#if 0
-static int
-nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state)
-{
-       spinlock_t *lock = &chip->controller->lock;
-       wait_queue_head_t *wq = &chip->controller->wq;
-       DECLARE_WAITQUEUE(wait, current);
- retry:
-       spin_lock(lock);
-
-       /* Hardware controller shared among independend devices */
-       /* Hardware controller shared among independend devices */
-       if (!chip->controller->active)
-               chip->controller->active = chip;
-
-       if (chip->controller->active == chip && chip->state == FL_READY) {
-               chip->state = new_state;
-               spin_unlock(lock);
-               return 0;
-       }
-       if (new_state == FL_PM_SUSPENDED) {
-               spin_unlock(lock);
-               return (chip->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
-       }
-       set_current_state(TASK_UNINTERRUPTIBLE);
-       add_wait_queue(wq, &wait);
-       spin_unlock(lock);
-       schedule();
-       remove_wait_queue(wq, &wait);
-       goto retry;
-}
-#else
 static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
 {
+       this->state = new_state;
        return 0;
 }
-#endif
 
 /**
  * nand_wait - [DEFAULT]  wait until the command is done
@@ -772,55 +700,15 @@ static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int ne
  * Erase can take up to 400ms and program up to 20ms according to
  * general NAND and SmartMedia specs
  */
-/* XXX U-BOOT XXX */
-#if 0
-static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
-{
-
-       unsigned long timeo = jiffies;
-       int status, state = chip->state;
-
-       if (state == FL_ERASING)
-               timeo += (HZ * 400) / 1000;
-       else
-               timeo += (HZ * 20) / 1000;
-
-       led_trigger_event(nand_led_trigger, LED_FULL);
-
-       /* Apply this short delay always to ensure that we do wait tWB in
-        * any case on any machine. */
-       ndelay(100);
-
-       if ((state == FL_ERASING) && (chip->options & NAND_IS_AND))
-               chip->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
-       else
-               chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
-
-       while (time_before(jiffies, timeo)) {
-               if (chip->dev_ready) {
-                       if (chip->dev_ready(mtd))
-                               break;
-               } else {
-                       if (chip->read_byte(mtd) & NAND_STATUS_READY)
-                               break;
-               }
-               cond_resched();
-       }
-       led_trigger_event(nand_led_trigger, LED_OFF);
-
-       status = (int)chip->read_byte(mtd);
-       return status;
-}
-#else
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
 {
        unsigned long   timeo;
        int state = this->state;
 
        if (state == FL_ERASING)
-               timeo = (CFG_HZ * 400) / 1000;
+               timeo = (CONFIG_SYS_HZ * 400) / 1000;
        else
-               timeo = (CFG_HZ * 20) / 1000;
+               timeo = (CONFIG_SYS_HZ * 20) / 1000;
 
        if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
                this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
@@ -850,30 +738,75 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
 
        return this->read_byte(mtd);
 }
-#endif
 
 /**
  * nand_read_page_raw - [Intern] read raw page data without ecc
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       buffer to store read data
+ * @page:      page number to read
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
  */
 static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
-                             uint8_t *buf)
+                             uint8_t *buf, int page)
 {
        chip->read_buf(mtd, buf, mtd->writesize);
        chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
        return 0;
 }
 
+/**
+ * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ * @page:      page number to read
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                             uint8_t *buf, int page)
+{
+       int eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       uint8_t *oob = chip->oob_poi;
+       int steps, size;
+
+       for (steps = chip->ecc.steps; steps > 0; steps--) {
+               chip->read_buf(mtd, buf, eccsize);
+               buf += eccsize;
+
+               if (chip->ecc.prepad) {
+                       chip->read_buf(mtd, oob, chip->ecc.prepad);
+                       oob += chip->ecc.prepad;
+               }
+
+               chip->read_buf(mtd, oob, eccbytes);
+               oob += eccbytes;
+
+               if (chip->ecc.postpad) {
+                       chip->read_buf(mtd, oob, chip->ecc.postpad);
+                       oob += chip->ecc.postpad;
+               }
+       }
+
+       size = mtd->oobsize - (oob - chip->oob_poi);
+       if (size)
+               chip->read_buf(mtd, oob, size);
+
+       return 0;
+}
+
 /**
  * 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
+ * @page:      page number to read
  */
 static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf)
+                               uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -883,7 +816,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
        uint8_t *ecc_code = chip->buffers->ecccode;
        uint32_t *eccpos = chip->ecc.layout->eccpos;
 
-       chip->ecc.read_page_raw(mtd, chip, buf);
+       chip->ecc.read_page_raw(mtd, chip, buf, page);
 
        for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
                chip->ecc.calculate(mtd, p, &ecc_calc[i]);
@@ -898,6 +831,87 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
                int stat;
 
                stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+               if (stat < 0)
+                       mtd->ecc_stats.failed++;
+               else
+                       mtd->ecc_stats.corrected += stat;
+       }
+       return 0;
+}
+
+/**
+ * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen:   data length
+ * @bufpoi:    buffer to store read data
+ */
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+{
+       int start_step, end_step, num_steps;
+       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       uint8_t *p;
+       int data_col_addr, i, gaps = 0;
+       int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+       int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+
+       /* Column address wihin the page aligned to ECC size (256bytes). */
+       start_step = data_offs / chip->ecc.size;
+       end_step = (data_offs + readlen - 1) / chip->ecc.size;
+       num_steps = end_step - start_step + 1;
+
+       /* Data size aligned to ECC ecc.size*/
+       datafrag_len = num_steps * chip->ecc.size;
+       eccfrag_len = num_steps * chip->ecc.bytes;
+
+       data_col_addr = start_step * chip->ecc.size;
+       /* If we read not a page aligned data */
+       if (data_col_addr != 0)
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+
+       p = bufpoi + data_col_addr;
+       chip->read_buf(mtd, p, datafrag_len);
+
+       /* Calculate  ECC */
+       for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+               chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+       /* The performance is faster if to position offsets
+          according to ecc.pos. Let make sure here that
+          there are no gaps in ecc positions */
+       for (i = 0; i < eccfrag_len - 1; i++) {
+               if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
+                       eccpos[i + start_step * chip->ecc.bytes + 1]) {
+                       gaps = 1;
+                       break;
+               }
+       }
+       if (gaps) {
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+               chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       } else {
+               /* send the command to read the particular ecc bytes */
+               /* take care about buswidth alignment in read_buf */
+               aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
+               aligned_len = eccfrag_len;
+               if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
+                       aligned_len++;
+               if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
+                       aligned_len++;
+
+               chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
+               chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+       }
+
+       for (i = 0; i < eccfrag_len; i++)
+               chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+
+       p = bufpoi + data_col_addr;
+       for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+               int stat;
+
+               stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
                if (stat == -1)
                        mtd->ecc_stats.failed++;
                else
@@ -911,11 +925,12 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       buffer to store read data
+ * @page:      page number to read
  *
  * Not for syndrome calculating ecc controllers which need a special oob layout
  */
 static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
-                               uint8_t *buf)
+                               uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -942,7 +957,56 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
                int stat;
 
                stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
-               if (stat == -1)
+               if (stat < 0)
+                       mtd->ecc_stats.failed++;
+               else
+                       mtd->ecc_stats.corrected += stat;
+       }
+       return 0;
+}
+
+/**
+ * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ * @page:      page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first.
+ * For this ECC mode, the write_page method is re-used from ECC_HW.
+ * These methods read/write ECC from the OOB area, unlike the
+ * ECC_HW_SYNDROME support with multiple ECC steps, follows the
+ * "infix ECC" scheme and reads/writes ECC from the data area, by
+ * overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+       struct nand_chip *chip, uint8_t *buf, int page)
+{
+       int i, eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       int eccsteps = chip->ecc.steps;
+       uint8_t *p = buf;
+       uint8_t *ecc_code = chip->buffers->ecccode;
+       uint32_t *eccpos = chip->ecc.layout->eccpos;
+       uint8_t *ecc_calc = chip->buffers->ecccalc;
+
+       /* Read the OOB area first */
+       chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+       chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+       chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+       for (i = 0; i < chip->ecc.total; i++)
+               ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+       for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+               int stat;
+
+               chip->ecc.hwctl(mtd, NAND_ECC_READ);
+               chip->read_buf(mtd, p, eccsize);
+               chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+               stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+               if (stat < 0)
                        mtd->ecc_stats.failed++;
                else
                        mtd->ecc_stats.corrected += stat;
@@ -955,12 +1019,13 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       buffer to store read data
+ * @page:      page number to read
  *
  * The hw generator calculates the error syndrome automatically. Therefor
  * 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)
+                                  uint8_t *buf, int page)
 {
        int i, eccsize = chip->ecc.size;
        int eccbytes = chip->ecc.bytes;
@@ -983,7 +1048,7 @@ static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
                chip->read_buf(mtd, oob, eccbytes);
                stat = chip->ecc.correct(mtd, p, oob, NULL);
 
-               if (stat == -1)
+               if (stat < 0)
                        mtd->ecc_stats.failed++;
                else
                        mtd->ecc_stats.corrected += stat;
@@ -1102,15 +1167,20 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 
                        /* Now read the page into the buffer */
                        if (unlikely(ops->mode == MTD_OOB_RAW))
-                               ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+                               ret = chip->ecc.read_page_raw(mtd, chip,
+                                               bufpoi, page);
+                       else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
+                               ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
                        else
-                               ret = chip->ecc.read_page(mtd, chip, bufpoi);
+                               ret = chip->ecc.read_page(mtd, chip, bufpoi,
+                                               page);
                        if (ret < 0)
                                break;
 
                        /* Transfer not aligned data */
                        if (!aligned) {
-                               chip->pagebuf = realpage;
+                               if (!NAND_SUBPAGE_READ(chip) && !oob)
+                                       chip->pagebuf = realpage;
                                memcpy(buf, chip->buffers->databuf + col, bytes);
                        }
 
@@ -1507,6 +1577,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       data buffer
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
  */
 static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
                                const uint8_t *buf)
@@ -1515,6 +1587,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 }
 
+/**
+ * nand_write_page_raw_syndrome - [Intern] raw page write function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf)
+{
+       int eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       uint8_t *oob = chip->oob_poi;
+       int steps, size;
+
+       for (steps = chip->ecc.steps; steps > 0; steps--) {
+               chip->write_buf(mtd, buf, eccsize);
+               buf += eccsize;
+
+               if (chip->ecc.prepad) {
+                       chip->write_buf(mtd, oob, chip->ecc.prepad);
+                       oob += chip->ecc.prepad;
+               }
+
+               chip->read_buf(mtd, oob, eccbytes);
+               oob += eccbytes;
+
+               if (chip->ecc.postpad) {
+                       chip->write_buf(mtd, oob, chip->ecc.postpad);
+                       oob += chip->ecc.postpad;
+               }
+       }
+
+       size = mtd->oobsize - (oob - chip->oob_poi);
+       if (size)
+               chip->write_buf(mtd, oob, size);
+}
 /**
  * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
  * @mtd:       mtd info structure
@@ -1742,13 +1852,6 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
        if (!writelen)
                return 0;
 
-       /* reject writes, which are not page aligned */
-       if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
-               printk(KERN_NOTICE "nand_write: "
-                      "Attempt to write not page aligned data\n");
-               return -EINVAL;
-       }
-
        column = to & (mtd->writesize - 1);
        subpage = column || (writelen & (mtd->writesize - 1));
 
@@ -2041,13 +2144,15 @@ static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
 int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                    int allowbbt)
 {
-       int page, len, status, pages_per_block, ret, chipnr;
+       int page, status, pages_per_block, ret, chipnr;
        struct nand_chip *chip = mtd->priv;
-       int rewrite_bbt[NAND_MAX_CHIPS]={0};
+       loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0};
        unsigned int bbt_masked_page = 0xffffffff;
+       loff_t len;
 
-       MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%08x, len = %i\n",
-                 (unsigned int) instr->addr, (unsigned int) instr->len);
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, "
+                "len = %llu\n", (unsigned long long) instr->addr,
+                (unsigned long long) instr->len);
 
        /* Start address must align on block boundary */
        if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
@@ -2080,7 +2185,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
 
        /* Calculate pages in each block */
        pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
-       
+
        /* Select the NAND device */
        chip->select_chip(mtd, chipnr);
 
@@ -2143,7 +2248,7 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                        MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: "
                                  "Failed erase, page 0x%08x\n", page);
                        instr->state = MTD_ERASE_FAILED;
-                       instr->fail_addr = (page << chip->page_shift);
+                       instr->fail_addr = ((loff_t)page << chip->page_shift);
                        goto erase_exit;
                }
 
@@ -2153,7 +2258,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                 */
                if (bbt_masked_page != 0xffffffff &&
                    (page & BBT_PAGE_MASK) == bbt_masked_page)
-                           rewrite_bbt[chipnr] = (page << chip->page_shift);
+                       rewrite_bbt[chipnr] =
+                               ((loff_t)page << chip->page_shift);
 
                /* Increment page address and decrement length */
                len -= (1 << chip->phys_erase_shift);
@@ -2180,13 +2286,14 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
  erase_exit:
 
        ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
-       /* Do call back function */
-       if (!ret)
-               mtd_erase_callback(instr);
 
        /* Deselect and wake up anyone waiting on the device */
        nand_release_device(mtd);
 
+       /* Do call back function */
+       if (!ret)
+               mtd_erase_callback(instr);
+
        /*
         * If BBT requires refresh and erase was successful, rewrite any
         * selected bad block tables
@@ -2199,8 +2306,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
                        continue;
                /* update the BBT for chip */
                MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
-                         "(%d:0x%0x 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
-                         chip->bbt_td->pages[chipnr]);
+                         "(%d:0x%0llx 0x%0x)\n", chipnr, rewrite_bbt[chipnr],
+                         chip->bbt_td->pages[chipnr]);
                nand_update_bbt(mtd, rewrite_bbt[chipnr]);
        }
 
@@ -2260,32 +2367,6 @@ static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return chip->block_markbad(mtd, ofs);
 }
 
-/**
- * nand_suspend - [MTD Interface] Suspend the NAND flash
- * @mtd:       MTD device structure
- */
-static int nand_suspend(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd->priv;
-
-       return nand_get_device(chip, mtd, FL_PM_SUSPENDED);
-}
-
-/**
- * nand_resume - [MTD Interface] Resume the NAND flash
- * @mtd:       MTD device structure
- */
-static void nand_resume(struct mtd_info *mtd)
-{
-       struct nand_chip *chip = mtd->priv;
-
-       if (chip->state == FL_PM_SUSPENDED)
-               nand_release_device(mtd);
-       else
-               printk(KERN_ERR "nand_resume() called for a chip which is not "
-                      "in suspended state\n");
-}
-
 /*
  * Set default functions
  */
@@ -2321,17 +2402,8 @@ 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) {
+       if (!chip->controller)
                chip->controller = &chip->hwcontrol;
-
-               /* XXX U-BOOT XXX */
-#if 0
-               spin_lock_init(&chip->controller->lock);
-               init_waitqueue_head(&chip->controller->wq);
-#endif
-       }
-
 }
 
 /*
@@ -2343,10 +2415,17 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 {
        struct nand_flash_dev *type = NULL;
        int i, dev_id, maf_idx;
+       int tmp_id, tmp_manf;
 
        /* Select the device */
        chip->select_chip(mtd, 0);
 
+       /*
+        * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+        * after power-up
+        */
+       chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
        /* Send the command for reading device ID */
        chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
 
@@ -2354,6 +2433,26 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        *maf_id = chip->read_byte(mtd);
        dev_id = chip->read_byte(mtd);
 
+       /* Try again to make sure, as some systems the bus-hold or other
+        * interface concerns can cause random data which looks like a
+        * possibly credible NAND flash to appear. If the two results do
+        * not match, ignore the device completely.
+        */
+
+       chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+       /* Read manufacturer and device IDs */
+
+       tmp_manf = chip->read_byte(mtd);
+       tmp_id = chip->read_byte(mtd);
+
+       if (tmp_manf != *maf_id || tmp_id != dev_id) {
+               printk(KERN_INFO "%s: second ID read did not match "
+                      "%02x,%02x against %02x,%02x\n", __func__,
+                      *maf_id, dev_id, tmp_manf, tmp_id);
+               return ERR_PTR(-ENODEV);
+       }
+
        /* Lookup the flash id */
        for (i = 0; nand_flash_ids[i].name != NULL; i++) {
                if (dev_id == nand_flash_ids[i].id) {
@@ -2362,13 +2461,20 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                }
        }
 
-       if (!type)
+       if (!type) {
+               /* supress warning if there is no nand */
+               if (*maf_id != 0x00 && *maf_id != 0xff &&
+                   dev_id  != 0x00 && dev_id  != 0xff)
+                       printk(KERN_INFO "%s: unknown NAND device: "
+                               "Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+                               __func__, *maf_id, dev_id);
                return ERR_PTR(-ENODEV);
+       }
 
        if (!mtd->name)
                mtd->name = type->name;
 
-       chip->chipsize = type->chipsize << 20;
+       chip->chipsize = (uint64_t)type->chipsize << 20;
 
        /* Newer devices have all the information in additional id bytes */
        if (!type->pagesize) {
@@ -2426,7 +2532,10 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 
        chip->bbt_erase_shift = chip->phys_erase_shift =
                ffs(mtd->erasesize) - 1;
-       chip->chip_shift = ffs(chip->chipsize) - 1;
+       if (chip->chipsize & 0xffffffff)
+               chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+       else
+               chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31;
 
        /* Set the bad block position */
        chip->badblockpos = mtd->writesize > 512 ?
@@ -2457,9 +2566,9 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
        if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
                chip->cmdfunc = nand_command_lp;
 
-       printk(KERN_INFO "NAND device: Manufacturer ID:"
-              " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
-              nand_manuf_ids[maf_idx].name, type->name);
+       MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
+                 " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, dev_id,
+                 nand_manuf_ids[maf_idx].name, type->name);
 
        return type;
 }
@@ -2489,7 +2598,9 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
        type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id);
 
        if (IS_ERR(type)) {
+#ifndef CONFIG_SYS_NAND_QUIET_TEST
                printk(KERN_WARNING "No NAND device found!!!\n");
+#endif
                chip->select_chip(mtd, -1);
                return PTR_ERR(type);
        }
@@ -2497,6 +2608,8 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
        /* Check for a chip array */
        for (i = 1; i < maxchips; i++) {
                chip->select_chip(mtd, i);
+               /* See comment in nand_get_flash_type for reset */
+               chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
                /* Send the command for reading device ID */
                chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
                /* Read manufacturer and device IDs */
@@ -2504,8 +2617,10 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
                    type->id != chip->read_byte(mtd))
                        break;
        }
+#ifdef DEBUG
        if (i > 1)
                printk(KERN_INFO "%d NAND chips detected\n", i);
+#endif
 
        /* Store the number of chips and calc total size for mtd */
        chip->numchips = i;
@@ -2518,7 +2633,6 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips)
 /**
  * nand_scan_tail - [NAND Interface] Scan for the NAND device
  * @mtd:           MTD device structure
- * @maxchips:      Number of chips to scan for
  *
  * This is the second phase of the normal nand_scan() function. It
  * fills out all the uninitialized function pointers with the defaults
@@ -2557,7 +2671,6 @@ int nand_scan_tail(struct mtd_info *mtd)
                default:
                        printk(KERN_WARNING "No oob scheme defined for "
                               "oobsize %d\n", mtd->oobsize);
-//                     BUG();
                }
        }
 
@@ -2568,26 +2681,41 @@ int nand_scan_tail(struct mtd_info *mtd)
         * check ECC mode, default to software if 3byte/512byte hardware ECC is
         * selected and we have 256 byte pagesize fallback to software ECC
         */
-       if (!chip->ecc.read_page_raw)
-               chip->ecc.read_page_raw = nand_read_page_raw;
-       if (!chip->ecc.write_page_raw)
-               chip->ecc.write_page_raw = nand_write_page_raw;
 
        switch (chip->ecc.mode) {
+       case NAND_ECC_HW_OOB_FIRST:
+               /* Similar to NAND_ECC_HW, but a separate read_page handle */
+               if (!chip->ecc.calculate || !chip->ecc.correct ||
+                    !chip->ecc.hwctl) {
+                       printk(KERN_WARNING "No ECC functions supplied, "
+                              "Hardware ECC not possible\n");
+                       BUG();
+               }
+               if (!chip->ecc.read_page)
+                       chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+
        case NAND_ECC_HW:
                /* 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;
+               if (!chip->ecc.read_page_raw)
+                       chip->ecc.read_page_raw = nand_read_page_raw;
+               if (!chip->ecc.write_page_raw)
+                       chip->ecc.write_page_raw = nand_write_page_raw;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_std;
                if (!chip->ecc.write_oob)
                        chip->ecc.write_oob = nand_write_oob_std;
 
        case NAND_ECC_HW_SYNDROME:
-               if (!chip->ecc.calculate || !chip->ecc.correct ||
-                   !chip->ecc.hwctl) {
+               if ((!chip->ecc.calculate || !chip->ecc.correct ||
+                    !chip->ecc.hwctl) &&
+                   (!chip->ecc.read_page ||
+                    chip->ecc.read_page == nand_read_page_hwecc ||
+                    !chip->ecc.write_page ||
+                    chip->ecc.write_page == nand_write_page_hwecc)) {
                        printk(KERN_WARNING "No ECC functions supplied, "
                               "Hardware ECC not possible\n");
                        BUG();
@@ -2597,6 +2725,10 @@ int nand_scan_tail(struct mtd_info *mtd)
                        chip->ecc.read_page = nand_read_page_syndrome;
                if (!chip->ecc.write_page)
                        chip->ecc.write_page = nand_write_page_syndrome;
+               if (!chip->ecc.read_page_raw)
+                       chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
+               if (!chip->ecc.write_page_raw)
+                       chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_syndrome;
                if (!chip->ecc.write_oob)
@@ -2613,7 +2745,10 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.calculate = nand_calculate_ecc;
                chip->ecc.correct = nand_correct_data;
                chip->ecc.read_page = nand_read_page_swecc;
+               chip->ecc.read_subpage = nand_read_subpage;
                chip->ecc.write_page = nand_write_page_swecc;
+               chip->ecc.read_page_raw = nand_read_page_raw;
+               chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = 256;
@@ -2626,6 +2761,8 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.read_page = nand_read_page_raw;
                chip->ecc.write_page = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
+               chip->ecc.read_page_raw = nand_read_page_raw;
+               chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
@@ -2642,7 +2779,8 @@ int nand_scan_tail(struct mtd_info *mtd)
         * the out of band area
         */
        chip->ecc.layout->oobavail = 0;
-       for (i = 0; chip->ecc.layout->oobfree[i].length; i++)
+       for (i = 0; chip->ecc.layout->oobfree[i].length
+                       && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++)
                chip->ecc.layout->oobavail +=
                        chip->ecc.layout->oobfree[i].length;
        mtd->oobavail = chip->ecc.layout->oobavail;
@@ -2670,6 +2808,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                        break;
                case 4:
                case 8:
+               case 16:
                        mtd->subpage_sft = 2;
                        break;
                }
@@ -2698,8 +2837,6 @@ int nand_scan_tail(struct mtd_info *mtd)
        mtd->sync = nand_sync;
        mtd->lock = NULL;
        mtd->unlock = NULL;
-       mtd->suspend = nand_suspend;
-       mtd->resume = nand_resume;
        mtd->block_isbad = nand_block_isbad;
        mtd->block_markbad = nand_block_markbad;
 
@@ -2708,22 +2845,11 @@ int nand_scan_tail(struct mtd_info *mtd)
 
        /* Check, if we should skip the bad block table scan */
        if (chip->options & NAND_SKIP_BBTSCAN)
-               return 0;
+               chip->options |= NAND_BBT_SCANNED;
 
-       /* Build bad block table */
-       return chip->scan_bbt(mtd);
+       return 0;
 }
 
-/* module_text_address() isn't exported, and it's mostly a pointless
-   test if this is a module _anyway_ -- they'd have to try _really_ hard
-   to call us from in-kernel code if the core NAND support is modular. */
-#ifdef MODULE
-#define caller_is_module() (1)
-#else
-#define caller_is_module() \
-       module_text_address((unsigned long)__builtin_return_address(0))
-#endif
-
 /**
  * nand_scan - [NAND Interface] Scan for the NAND device
  * @mtd:       MTD device structure
@@ -2740,15 +2866,6 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
 {
        int ret;
 
-       /* Many callers got this wrong, so check for it for a while... */
-       /* XXX U-BOOT XXX */
-#if 0
-       if (!mtd->owner && caller_is_module()) {
-               printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
-               BUG();
-       }
-#endif
-       
        ret = nand_scan_ident(mtd, maxchips);
        if (!ret)
                ret = nand_scan_tail(mtd);
@@ -2767,43 +2884,9 @@ void nand_release(struct mtd_info *mtd)
        /* Deregister partitions */
        del_mtd_partitions(mtd);
 #endif
-       /* Deregister the device */
-       /* XXX U-BOOT XXX */
-#if 0
-       del_mtd_device(mtd);
-#endif
 
        /* Free bad block table memory */
        kfree(chip->bbt);
        if (!(chip->options & NAND_OWN_BUFFERS))
                kfree(chip->buffers);
 }
-
-/* XXX U-BOOT XXX */
-#if 0
-EXPORT_SYMBOL_GPL(nand_scan);
-EXPORT_SYMBOL_GPL(nand_scan_ident);
-EXPORT_SYMBOL_GPL(nand_scan_tail);
-EXPORT_SYMBOL_GPL(nand_release);
-
-static int __init nand_base_init(void)
-{
-       led_trigger_register_simple("nand-disk", &nand_led_trigger);
-       return 0;
-}
-
-static void __exit nand_base_exit(void)
-{
-       led_trigger_unregister_simple(nand_led_trigger);
-}
-
-module_init(nand_base_init);
-module_exit(nand_base_exit);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
-MODULE_DESCRIPTION("Generic NAND flash driver code");
-#endif
-
-#endif
-