]> nv-tegra.nvidia Code Review - linux-2.6.git/blobdiff - drivers/mtd/onenand/onenand_base.c
mtd: move mtd_oob_mode_t to shared kernel/user space
[linux-2.6.git] / drivers / mtd / onenand / onenand_base.c
index f749935f3cb53d7ff910a24b521eb02656f845b7..493901a59e6e606b0051d45a6ae4b7172eabd0b2 100644 (file)
@@ -65,11 +65,11 @@ MODULE_PARM_DESC(otp,       "Corresponding behaviour of OneNAND in OTP"
                        "          : 2 -> 1st Block lock"
                        "          : 3 -> BOTH OTP Block and 1st Block lock");
 
-/**
- *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
- *  For now, we expose only 64 out of 80 ecc bytes
+/*
+ * flexonenand_oob_128 - oob info for Flex-Onenand with 4KB page
+ * For now, we expose only 64 out of 80 ecc bytes
  */
-static struct nand_ecclayout onenand_oob_128 = {
+static struct nand_ecclayout flexonenand_oob_128 = {
        .eccbytes       = 64,
        .eccpos         = {
                6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
@@ -86,6 +86,35 @@ static struct nand_ecclayout onenand_oob_128 = {
        }
 };
 
+/*
+ * onenand_oob_128 - oob info for OneNAND with 4KB page
+ *
+ * Based on specification:
+ * 4Gb M-die OneNAND Flash (KFM4G16Q4M, KFN8G16Q4M). Rev. 1.3, Apr. 2010
+ *
+ * For eccpos we expose only 64 bytes out of 72 (see struct nand_ecclayout)
+ *
+ * oobfree uses the spare area fields marked as
+ * "Managed by internal ECC logic for Logical Sector Number area"
+ */
+static struct nand_ecclayout onenand_oob_128 = {
+       .eccbytes       = 64,
+       .eccpos         = {
+               7, 8, 9, 10, 11, 12, 13, 14, 15,
+               23, 24, 25, 26, 27, 28, 29, 30, 31,
+               39, 40, 41, 42, 43, 44, 45, 46, 47,
+               55, 56, 57, 58, 59, 60, 61, 62, 63,
+               71, 72, 73, 74, 75, 76, 77, 78, 79,
+               87, 88, 89, 90, 91, 92, 93, 94, 95,
+               103, 104, 105, 106, 107, 108, 109, 110, 111,
+               119
+       },
+       .oobfree        = {
+               {2, 3}, {18, 3}, {34, 3}, {50, 3},
+               {66, 3}, {82, 3}, {98, 3}, {114, 3}
+       }
+};
+
 /**
  * onenand_oob_64 - oob info for large (2KB) page
  */
@@ -400,8 +429,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
 
-               if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this) ||
-                   ONENAND_IS_4KB_PAGE(this))
+               if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this))
                        /* It is always BufferRAM0 */
                        ONENAND_SET_BUFFERRAM0(this);
                else
@@ -430,7 +458,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
                case FLEXONENAND_CMD_RECOVER_LSB:
                case ONENAND_CMD_READ:
                case ONENAND_CMD_READOOB:
-                       if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
+                       if (ONENAND_IS_4KB_PAGE(this))
                                /* It is always BufferRAM0 */
                                dataram = ONENAND_SET_BUFFERRAM0(this);
                        else
@@ -949,6 +977,8 @@ static int onenand_get_device(struct mtd_info *mtd, int new_state)
                if (this->state == FL_READY) {
                        this->state = new_state;
                        spin_unlock(&this->chip_lock);
+                       if (new_state != FL_PM_SUSPENDED && this->enable)
+                               this->enable(mtd);
                        break;
                }
                if (new_state == FL_PM_SUSPENDED) {
@@ -975,6 +1005,8 @@ static void onenand_release_device(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
 
+       if (this->state != FL_PM_SUSPENDED && this->disable)
+               this->disable(mtd);
        /* Release the chip */
        spin_lock(&this->chip_lock);
        this->state = FL_READY;
@@ -983,7 +1015,7 @@ static void onenand_release_device(struct mtd_info *mtd)
 }
 
 /**
- * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_transfer_auto_oob - [INTERN] oob auto-placement transfer
  * @param mtd          MTD device structure
  * @param buf          destination address
  * @param column       oob offset to read from
@@ -1090,8 +1122,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        int ret = 0;
        int writesize = this->writesize;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
-             __func__, (unsigned int) from, (int) len);
+       pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+                       (int)len);
 
        if (ops->mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
@@ -1129,6 +1161,8 @@ static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        onenand_update_bufferram(mtd, from, !ret);
                        if (ret == -EBADMSG)
                                ret = 0;
+                       if (ret)
+                               break;
                }
 
                this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
@@ -1192,8 +1226,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        int ret = 0, boundary = 0;
        int writesize = this->writesize;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
-                       __func__, (unsigned int) from, (int) len);
+       pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+                       (int)len);
 
        if (ops->mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
@@ -1317,14 +1351,14 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
        struct mtd_ecc_stats stats;
        int read = 0, thislen, column, oobsize;
        size_t len = ops->ooblen;
-       mtd_oob_mode_t mode = ops->mode;
+       unsigned int mode = ops->mode;
        u_char *buf = ops->oobbuf;
        int ret = 0, readcmd;
 
        from += ops->ooboffs;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %i\n",
-               __func__, (unsigned int) from, (int) len);
+       pr_debug("%s: from = 0x%08x, len = %i\n", __func__, (unsigned int)from,
+                       (int)len);
 
        /* Initialize return length value */
        ops->oobretlen = 0;
@@ -1353,7 +1387,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 
        stats = mtd->ecc_stats;
 
-       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+       readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
 
        while (read < len) {
                cond_resched();
@@ -1429,7 +1463,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
        int ret;
 
        onenand_get_device(mtd, FL_READING);
-       ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
+       ret = ONENAND_IS_4KB_PAGE(this) ?
                onenand_mlc_read_ops_nolock(mtd, from, &ops) :
                onenand_read_ops_nolock(mtd, from, &ops);
        onenand_release_device(mtd);
@@ -1464,7 +1498,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
 
        onenand_get_device(mtd, FL_READING);
        if (ops->datbuf)
-               ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
+               ret = ONENAND_IS_4KB_PAGE(this) ?
                        onenand_mlc_read_ops_nolock(mtd, from, ops) :
                        onenand_read_ops_nolock(mtd, from, ops);
        else
@@ -1485,8 +1519,7 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
 {
        struct onenand_chip *this = mtd->priv;
        unsigned long timeout;
-       unsigned int interrupt;
-       unsigned int ctrl;
+       unsigned int interrupt, ctrl, ecc, addr1, addr8;
 
        /* The 20 msec is enough */
        timeout = jiffies + msecs_to_jiffies(20);
@@ -1498,25 +1531,28 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
        /* To get correct interrupt status in timeout case */
        interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
+       addr1 = this->read_word(this->base + ONENAND_REG_START_ADDRESS1);
+       addr8 = this->read_word(this->base + ONENAND_REG_START_ADDRESS8);
 
        if (interrupt & ONENAND_INT_READ) {
-               int ecc = onenand_read_ecc(this);
+               ecc = onenand_read_ecc(this);
                if (ecc & ONENAND_ECC_2BIT_ALL) {
-                       printk(KERN_WARNING "%s: ecc error = 0x%04x, "
-                               "controller error 0x%04x\n",
-                               __func__, ecc, ctrl);
+                       printk(KERN_DEBUG "%s: ecc 0x%04x ctrl 0x%04x "
+                              "intr 0x%04x addr1 %#x addr8 %#x\n",
+                              __func__, ecc, ctrl, interrupt, addr1, addr8);
                        return ONENAND_BBT_READ_ECC_ERROR;
                }
        } else {
-               printk(KERN_ERR "%s: read timeout! ctrl=0x%04x intr=0x%04x\n",
-                       __func__, ctrl, interrupt);
+               printk(KERN_ERR "%s: read timeout! ctrl 0x%04x "
+                      "intr 0x%04x addr1 %#x addr8 %#x\n",
+                      __func__, ctrl, interrupt, addr1, addr8);
                return ONENAND_BBT_READ_FATAL_ERROR;
        }
 
        /* Initial bad block case: 0x2400 or 0x0400 */
        if (ctrl & ONENAND_CTRL_ERROR) {
-               printk(KERN_DEBUG "%s: controller error = 0x%04x\n",
-                       __func__, ctrl);
+               printk(KERN_DEBUG "%s: ctrl 0x%04x intr 0x%04x addr1 %#x "
+                      "addr8 %#x\n", __func__, ctrl, interrupt, addr1, addr8);
                return ONENAND_BBT_READ_ERROR;
        }
 
@@ -1540,8 +1576,8 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
        size_t len = ops->ooblen;
        u_char *buf = ops->oobbuf;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08x, len = %zi\n",
-               __func__, (unsigned int) from, len);
+       pr_debug("%s: from = 0x%08x, len = %zi\n", __func__, (unsigned int)from,
+                       len);
 
        /* Initialize return value */
        ops->oobretlen = 0;
@@ -1558,7 +1594,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
 
        column = from & (mtd->oobsize - 1);
 
-       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+       readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
 
        while (read < len) {
                cond_resched();
@@ -1612,7 +1648,7 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
        u_char *oob_buf = this->oob_buf;
        int status, i, readcmd;
 
-       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+       readcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
 
        this->command(mtd, readcmd, to, mtd->oobsize);
        onenand_update_bufferram(mtd, to, 0);
@@ -1641,11 +1677,10 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
        int ret = 0;
        int thislen, column;
 
+       column = addr & (this->writesize - 1);
+
        while (len != 0) {
-               thislen = min_t(int, this->writesize, len);
-               column = addr & (this->writesize - 1);
-               if (column + thislen > this->writesize)
-                       thislen = this->writesize - column;
+               thislen = min_t(int, this->writesize - column, len);
 
                this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);
 
@@ -1659,12 +1694,13 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
 
                this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize);
 
-               if (memcmp(buf, this->verify_buf, thislen))
+               if (memcmp(buf, this->verify_buf + column, thislen))
                        return -EBADMSG;
 
                len -= thislen;
                buf += thislen;
                addr += thislen;
+               column = 0;
        }
 
        return 0;
@@ -1714,8 +1750,8 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
        /* Wait for any existing operation to clear */
        onenand_panic_wait(mtd);
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
-               __func__, (unsigned int) to, (int) len);
+       pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+                       (int)len);
 
        /* Initialize retlen, in case of early exit */
        *retlen = 0;
@@ -1785,7 +1821,7 @@ static int onenand_panic_write(struct mtd_info *mtd, loff_t to, size_t len,
 }
 
 /**
- * onenand_fill_auto_oob - [Internal] oob auto-placement transfer
+ * onenand_fill_auto_oob - [INTERN] oob auto-placement transfer
  * @param mtd          MTD device structure
  * @param oob_buf      oob buffer
  * @param buf          source address
@@ -1845,10 +1881,10 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
        const u_char *buf = ops->datbuf;
        const u_char *oob = ops->oobbuf;
        u_char *oobbuf;
-       int ret = 0;
+       int ret = 0, cmd;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
-               __func__, (unsigned int) to, (int) len);
+       pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+                       (int)len);
 
        /* Initialize retlen, in case of early exit */
        ops->retlen = 0;
@@ -1954,7 +1990,19 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                        ONENAND_SET_NEXT_BUFFERRAM(this);
                }
 
-               this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
+               this->ongoing = 0;
+               cmd = ONENAND_CMD_PROG;
+
+               /* Exclude 1st OTP and OTP blocks for cache program feature */
+               if (ONENAND_IS_CACHE_PROGRAM(this) &&
+                   likely(onenand_block(this, to) != 0) &&
+                   ONENAND_IS_4KB_PAGE(this) &&
+                   ((written + thislen) < len)) {
+                       cmd = ONENAND_CMD_2X_CACHE_PROG;
+                       this->ongoing = 1;
+               }
+
+               this->command(mtd, cmd, to, mtd->writesize);
 
                /*
                 * 2 PLANE, MLC, and Flex-OneNAND wait here
@@ -2007,7 +2055,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
 
 
 /**
- * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band
+ * onenand_write_oob_nolock - [INTERN] OneNAND write out-of-band
  * @param mtd          MTD device structure
  * @param to           offset to write to
  * @param len          number of bytes to write
@@ -2026,12 +2074,12 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
        u_char *oobbuf;
        size_t len = ops->ooblen;
        const u_char *buf = ops->oobbuf;
-       mtd_oob_mode_t mode = ops->mode;
+       unsigned int mode = ops->mode;
 
        to += ops->ooboffs;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
-               __func__, (unsigned int) to, (int) len);
+       pr_debug("%s: to = 0x%08x, len = %i\n", __func__, (unsigned int)to,
+                       (int)len);
 
        /* Initialize retlen, in case of early exit */
        ops->oobretlen = 0;
@@ -2067,7 +2115,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 
        oobbuf = this->oob_buf;
 
-       oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
+       oobcmd = ONENAND_IS_4KB_PAGE(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
 
        /* Loop until all data write */
        while (written < len) {
@@ -2086,7 +2134,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
                        memcpy(oobbuf + column, buf, thislen);
                this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
-               if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) {
+               if (ONENAND_IS_4KB_PAGE(this)) {
                        /* Set main area of DataRAM to 0xff*/
                        memset(this->page_buf, 0xff, mtd->writesize);
                        this->write_bufferram(mtd, ONENAND_DATARAM,
@@ -2233,7 +2281,7 @@ static int onenand_multiblock_erase_verify(struct mtd_info *mtd,
 }
 
 /**
- * onenand_multiblock_erase - [Internal] erase block(s) using multiblock erase
+ * onenand_multiblock_erase - [INTERN] erase block(s) using multiblock erase
  * @param mtd          MTD device structure
  * @param instr                erase instruction
  * @param region       erase region
@@ -2349,7 +2397,7 @@ static int onenand_multiblock_erase(struct mtd_info *mtd,
 
 
 /**
- * onenand_block_by_block_erase - [Internal] erase block(s) using regular erase
+ * onenand_block_by_block_erase - [INTERN] erase block(s) using regular erase
  * @param mtd          MTD device structure
  * @param instr                erase instruction
  * @param region       erase region
@@ -2405,7 +2453,7 @@ static int onenand_block_by_block_erase(struct mtd_info *mtd,
                len -= block_size;
                addr += block_size;
 
-               if (addr == region_end) {
+               if (region && addr == region_end) {
                        if (!len)
                                break;
                        region++;
@@ -2441,8 +2489,9 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
        struct mtd_erase_region_info *region = NULL;
        loff_t region_offset = 0;
 
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: start=0x%012llx, len=%llu\n", __func__,
-             (unsigned long long) instr->addr, (unsigned long long) instr->len);
+       pr_debug("%s: start=0x%012llx, len=%llu\n", __func__,
+                       (unsigned long long)instr->addr,
+                       (unsigned long long)instr->len);
 
        /* Do not allow erase past end of device */
        if (unlikely((len + addr) > mtd->size)) {
@@ -2481,7 +2530,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_ERASING);
 
-       if (region || instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
+       if (ONENAND_IS_4KB_PAGE(this) || region ||
+           instr->len < MB_ERASE_MIN_BLK_COUNT * block_size) {
                /* region is set for Flex-OneNAND (no mb erase) */
                ret = onenand_block_by_block_erase(mtd, instr,
                                                   region, block_size);
@@ -2509,7 +2559,7 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
  */
 static void onenand_sync(struct mtd_info *mtd)
 {
-       DEBUG(MTD_DEBUG_LEVEL3, "%s: called\n", __func__);
+       pr_debug("%s: called\n", __func__);
 
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_SYNCING);
@@ -2873,7 +2923,7 @@ static int onenand_otp_command(struct mtd_info *mtd, int cmd, loff_t addr,
 }
 
 /**
- * onenand_otp_write_oob_nolock - [Internal] OneNAND write out-of-band, specific to OTP
+ * onenand_otp_write_oob_nolock - [INTERN] OneNAND write out-of-band, specific to OTP
  * @param mtd          MTD device structure
  * @param to           offset to write to
  * @param len          number of bytes to write
@@ -3029,7 +3079,7 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
        this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
        this->wait(mtd, FL_OTPING);
 
-       ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ?
+       ret = ONENAND_IS_4KB_PAGE(this) ?
                onenand_mlc_read_ops_nolock(mtd, from, &ops) :
                onenand_read_ops_nolock(mtd, from, &ops);
 
@@ -3365,19 +3415,35 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
 static void onenand_check_features(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
-       unsigned int density, process;
+       unsigned int density, process, numbufs;
 
        /* Lock scheme depends on density and process */
        density = onenand_get_density(this->device_id);
        process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+       numbufs = this->read_word(this->base + ONENAND_REG_NUM_BUFFERS) >> 8;
 
        /* Lock scheme */
        switch (density) {
        case ONENAND_DEVICE_DENSITY_4Gb:
                if (ONENAND_IS_DDP(this))
                        this->options |= ONENAND_HAS_2PLANE;
-               else
+               else if (numbufs == 1) {
                        this->options |= ONENAND_HAS_4KB_PAGE;
+                       this->options |= ONENAND_HAS_CACHE_PROGRAM;
+                       /*
+                        * There are two different 4KiB pagesize chips
+                        * and no way to detect it by H/W config values.
+                        *
+                        * To detect the correct NOP for each chips,
+                        * It should check the version ID as workaround.
+                        *
+                        * Now it has as following
+                        * KFM4G16Q4M has NOP 4 with version ID 0x0131
+                        * KFM4G16Q5M has NOP 1 with versoin ID 0x013e
+                        */
+                       if ((this->version_id & 0xf) == 0xe)
+                               this->options |= ONENAND_HAS_NOP_1;
+               }
 
        case ONENAND_DEVICE_DENSITY_2Gb:
                /* 2Gb DDP does not have 2 plane */
@@ -3398,7 +3464,11 @@ static void onenand_check_features(struct mtd_info *mtd)
                break;
        }
 
-       if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
+       /* The MLC has 4KiB pagesize. */
+       if (ONENAND_IS_MLC(this))
+               this->options |= ONENAND_HAS_4KB_PAGE;
+
+       if (ONENAND_IS_4KB_PAGE(this))
                this->options &= ~ONENAND_HAS_2PLANE;
 
        if (FLEXONENAND(this)) {
@@ -3414,6 +3484,8 @@ static void onenand_check_features(struct mtd_info *mtd)
                printk(KERN_DEBUG "Chip has 2 plane\n");
        if (this->options & ONENAND_HAS_4KB_PAGE)
                printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
+       if (this->options & ONENAND_HAS_CACHE_PROGRAM)
+               printk(KERN_DEBUG "Chip has cache program feature\n");
 }
 
 /**
@@ -3733,17 +3805,16 @@ out:
 }
 
 /**
- * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+ * onenand_chip_probe - [OneNAND Interface] The generic chip probe
  * @param mtd          MTD device structure
  *
  * OneNAND detection method:
  *   Compare the values from command with ones from register
  */
-static int onenand_probe(struct mtd_info *mtd)
+static int onenand_chip_probe(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
-       int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
-       int density;
+       int bram_maf_id, bram_dev_id, maf_id, dev_id;
        int syscfg;
 
        /* Save system configuration 1 */
@@ -3766,12 +3837,6 @@ static int onenand_probe(struct mtd_info *mtd)
        /* Restore system configuration 1 */
        this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
 
-       /* Workaround */
-       if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) {
-               bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
-               bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
-       }
-
        /* Check manufacturer ID */
        if (onenand_check_maf(bram_maf_id))
                return -ENXIO;
@@ -3779,13 +3844,35 @@ static int onenand_probe(struct mtd_info *mtd)
        /* Read manufacturer and device IDs from Register */
        maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
        dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
-       ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
-       this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
 
        /* Check OneNAND device */
        if (maf_id != bram_maf_id || dev_id != bram_dev_id)
                return -ENXIO;
 
+       return 0;
+}
+
+/**
+ * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+ * @param mtd          MTD device structure
+ */
+static int onenand_probe(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int maf_id, dev_id, ver_id;
+       int density;
+       int ret;
+
+       ret = this->chip_probe(mtd);
+       if (ret)
+               return ret;
+
+       /* Read manufacturer and device IDs from Register */
+       maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
+       dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+       ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
+       this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
+
        /* Flash device information */
        onenand_print_device_info(dev_id, ver_id);
        this->device_id = dev_id;
@@ -3815,7 +3902,7 @@ static int onenand_probe(struct mtd_info *mtd)
        /* The data buffer size is equal to page size */
        mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE);
        /* We use the full BufferRAM */
-       if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this))
+       if (ONENAND_IS_4KB_PAGE(this))
                mtd->writesize <<= 1;
 
        mtd->oobsize = mtd->writesize >> 5;
@@ -3912,6 +3999,9 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        if (!this->unlock_all)
                this->unlock_all = onenand_unlock_all;
 
+       if (!this->chip_probe)
+               this->chip_probe = onenand_chip_probe;
+
        if (!this->read_bufferram)
                this->read_bufferram = onenand_read_bufferram;
        if (!this->write_bufferram)
@@ -3971,8 +4061,15 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
         */
        switch (mtd->oobsize) {
        case 128:
-               this->ecclayout = &onenand_oob_128;
-               mtd->subpage_sft = 0;
+               if (FLEXONENAND(this)) {
+                       this->ecclayout = &flexonenand_oob_128;
+                       mtd->subpage_sft = 0;
+               } else {
+                       this->ecclayout = &onenand_oob_128;
+                       mtd->subpage_sft = 2;
+               }
+               if (ONENAND_IS_NOP_1(this))
+                       mtd->subpage_sft = 0;
                break;
        case 64:
                this->ecclayout = &onenand_oob_64;
@@ -4009,7 +4106,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->ecclayout = this->ecclayout;
 
        /* Fill in remaining MTD driver data */
-       mtd->type = MTD_NANDFLASH;
+       mtd->type = ONENAND_IS_MLC(this) ? MTD_MLCNANDFLASH : MTD_NANDFLASH;
        mtd->flags = MTD_CAP_NANDFLASH;
        mtd->erase = onenand_erase;
        mtd->point = NULL;
@@ -4035,9 +4132,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
        mtd->block_isbad = onenand_block_isbad;
        mtd->block_markbad = onenand_block_markbad;
        mtd->owner = THIS_MODULE;
+       mtd->writebufsize = mtd->writesize;
 
        /* Unlock whole block */
-       this->unlock_all(mtd);
+       if (!(this->options & ONENAND_SKIP_INITIAL_UNLOCKING))
+               this->unlock_all(mtd);
 
        ret = this->scan_bbt(mtd);
        if ((!FLEXONENAND(this)) || ret)
@@ -4059,12 +4158,8 @@ void onenand_release(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
 
-#ifdef CONFIG_MTD_PARTITIONS
        /* Deregister partitions */
-       del_mtd_partitions (mtd);
-#endif
-       /* Deregister the device */
-       del_mtd_device (mtd);
+       mtd_device_unregister(mtd);
 
        /* Free bad block table memory, if allocated */
        if (this->bbm) {