]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/onenand/onenand_base.c
Merge branch 'zynq' of git://www.denx.de/git/u-boot-microblaze
[karo-tx-uboot.git] / drivers / mtd / onenand / onenand_base.c
index d482437a462f254cb9ded756e97fb91a9f665c3a..03deabce102341881a7b430f35164eea9c455ce6 100644 (file)
@@ -9,14 +9,20 @@
  *      auto-placement support, read-while load support, various fixes
  *      Copyright (C) Nokia Corporation, 2007
  *
+ *      Rohit Hagargundgi <h.rohit at samsung.com>,
+ *      Amul Kumar Saha <amul.saha@samsung.com>:
+ *      Flex-OneNAND support
+ *      Copyright (C) Samsung Electronics, 2009
+ *
  * 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
  * published by the Free Software Foundation.
  */
 
 #include <common.h>
-#include <linux/mtd/compat.h>
+#include <linux/compat.h>
 #include <linux/mtd/mtd.h>
+#include "linux/mtd/flashchip.h"
 #include <linux/mtd/onenand.h>
 
 #include <asm/io.h>
@@ -24,7 +30,7 @@
 #include <malloc.h>
 
 /* It should access 16-bit instead of 8-bit */
-static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
+static void *memcpy_16(void *dst, const void *src, unsigned int len)
 {
        void *ret = dst;
        short *d = dst;
@@ -36,6 +42,27 @@ static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
        return ret;
 }
 
+/**
+ *  onenand_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 = {
+       .eccbytes       = 64,
+       .eccpos         = {
+               6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+               22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+               38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+               54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+               70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+               86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
+               102, 103, 104, 105
+               },
+       .oobfree        = {
+               {2, 4}, {18, 4}, {34, 4}, {50, 4},
+               {66, 4}, {82, 4}, {98, 4}, {114, 4}
+       }
+};
+
 /**
  * onenand_oob_64 - oob info for large (2KB) page
  */
@@ -65,7 +92,13 @@ static struct nand_ecclayout onenand_oob_32 = {
        .oobfree        = { {2, 3}, {14, 2}, {18, 3}, {30, 2} }
 };
 
-static const unsigned char ffchars[] = {
+/*
+ * Warning! This array is used with the memcpy_16() function, thus
+ * it must be aligned to 2 bytes. GCC can make this array unaligned
+ * as the array is made of unsigned char, which memcpy16() doesn't
+ * like and will cause unaligned access.
+ */
+static const unsigned char __aligned(2) ffchars[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 16 */
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
@@ -74,6 +107,14 @@ static const unsigned char ffchars[] = {
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */
 };
 
 /**
@@ -179,6 +220,85 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
        return ((bsa << ONENAND_BSA_SHIFT) | bsc);
 }
 
+/**
+ * flexonenand_block - Return block number for flash address
+ * @param this         - OneNAND device structure
+ * @param addr         - Address for which block number is needed
+ */
+static unsigned int flexonenand_block(struct onenand_chip *this, loff_t addr)
+{
+       unsigned int boundary, blk, die = 0;
+
+       if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) {
+               die = 1;
+               addr -= this->diesize[0];
+       }
+
+       boundary = this->boundary[die];
+
+       blk = addr >> (this->erase_shift - 1);
+       if (blk > boundary)
+               blk = (blk + boundary + 1) >> 1;
+
+       blk += die ? this->density_mask : 0;
+       return blk;
+}
+
+unsigned int onenand_block(struct onenand_chip *this, loff_t addr)
+{
+       if (!FLEXONENAND(this))
+               return addr >> this->erase_shift;
+       return flexonenand_block(this, addr);
+}
+
+/**
+ * flexonenand_addr - Return address of the block
+ * @this:              OneNAND device structure
+ * @block:             Block number on Flex-OneNAND
+ *
+ * Return address of the block
+ */
+static loff_t flexonenand_addr(struct onenand_chip *this, int block)
+{
+       loff_t ofs = 0;
+       int die = 0, boundary;
+
+       if (ONENAND_IS_DDP(this) && block >= this->density_mask) {
+               block -= this->density_mask;
+               die = 1;
+               ofs = this->diesize[0];
+       }
+
+       boundary = this->boundary[die];
+       ofs += (loff_t) block << (this->erase_shift - 1);
+       if (block > (boundary + 1))
+               ofs += (loff_t) (block - boundary - 1)
+                       << (this->erase_shift - 1);
+       return ofs;
+}
+
+loff_t onenand_addr(struct onenand_chip *this, int block)
+{
+       if (!FLEXONENAND(this))
+               return (loff_t) block << this->erase_shift;
+       return flexonenand_addr(this, block);
+}
+
+/**
+ * flexonenand_region - [Flex-OneNAND] Return erase region of addr
+ * @param mtd          MTD device structure
+ * @param addr         address whose erase region needs to be identified
+ */
+int flexonenand_region(struct mtd_info *mtd, loff_t addr)
+{
+       int i;
+
+       for (i = 0; i < mtd->numeraseregions; i++)
+               if (addr < mtd->eraseregions[i].offset)
+                       break;
+       return i - 1;
+}
+
 /**
  * onenand_get_density - [DEFAULT] Get OneNAND density
  * @param dev_id        OneNAND device ID
@@ -205,10 +325,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
                           size_t len)
 {
        struct onenand_chip *this = mtd->priv;
-       int value, readcmd = 0;
+       int value;
        int block, page;
+
        /* Now we use page size operation */
-       int sectors = 4, count = 4;
+       int sectors = 0, count = 0;
 
        /* Address translation */
        switch (cmd) {
@@ -220,15 +341,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
                page = -1;
                break;
 
+       case FLEXONENAND_CMD_PI_ACCESS:
+               /* addr contains die index */
+               block = addr * this->density_mask;
+               page = -1;
+               break;
+
        case ONENAND_CMD_ERASE:
        case ONENAND_CMD_BUFFERRAM:
-               block = (int)(addr >> this->erase_shift);
+               block = onenand_block(this, addr);
                page = -1;
                break;
 
+       case FLEXONENAND_CMD_READ_PI:
+               cmd = ONENAND_CMD_READ;
+               block = addr * this->density_mask;
+               page = 0;
+               break;
+
        default:
-               block = (int)(addr >> this->erase_shift);
-               page = (int)(addr >> this->page_shift);
+               block = onenand_block(this, addr);
+               page = (int) (addr
+                       - onenand_addr(this, block)) >> this->page_shift;
                page &= this->page_mask;
                break;
        }
@@ -240,8 +374,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
                this->write_word(value,
                                 this->base + ONENAND_REG_START_ADDRESS2);
 
-               /* Switch to the next data buffer */
-               ONENAND_SET_NEXT_BUFFERRAM(this);
+               if (ONENAND_IS_4KB_PAGE(this))
+                       ONENAND_SET_BUFFERRAM0(this);
+               else
+                       /* Switch to the next data buffer */
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
 
                return 0;
        }
@@ -252,7 +389,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
                this->write_word(value,
                                 this->base + ONENAND_REG_START_ADDRESS1);
 
-               /* Write 'DFS, FBA' of Flash */
+               /* Select DataRAM for DDP */
                value = onenand_bufferram_address(this, block);
                this->write_word(value,
                                 this->base + ONENAND_REG_START_ADDRESS2);
@@ -262,10 +399,14 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
                int dataram;
 
                switch (cmd) {
+               case FLEXONENAND_CMD_RECOVER_LSB:
                case ONENAND_CMD_READ:
                case ONENAND_CMD_READOOB:
-                       dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
-                       readcmd = 1;
+                       if (ONENAND_IS_4KB_PAGE(this))
+                               dataram = ONENAND_SET_BUFFERRAM0(this);
+                       else
+                               dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
+
                        break;
 
                default:
@@ -291,6 +432,29 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
        return 0;
 }
 
+/**
+ * onenand_read_ecc - return ecc status
+ * @param this         onenand chip structure
+ */
+static int onenand_read_ecc(struct onenand_chip *this)
+{
+       int ecc, i;
+
+       if (!FLEXONENAND(this))
+               return this->read_word(this->base + ONENAND_REG_ECC_STATUS);
+
+       for (i = 0; i < 4; i++) {
+               ecc = this->read_word(this->base
+                               + ((ONENAND_REG_ECC_STATUS + i) << 1));
+               if (likely(!ecc))
+                       continue;
+               if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
+                       return ONENAND_ECC_2BIT_ALL;
+       }
+
+       return 0;
+}
+
 /**
  * onenand_wait - [DEFAULT] wait until the command is done
  * @param mtd          MTD device structure
@@ -305,7 +469,7 @@ static int onenand_wait(struct mtd_info *mtd, int state)
        struct onenand_chip *this = mtd->priv;
        unsigned int flags = ONENAND_INT_MASTER;
        unsigned int interrupt = 0;
-       unsigned int ctrl, ecc;
+       unsigned int ctrl;
 
        while (1) {
                interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
@@ -315,6 +479,14 @@ static int onenand_wait(struct mtd_info *mtd, int state)
 
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
+       if (interrupt & ONENAND_INT_READ) {
+               int ecc = onenand_read_ecc(this);
+               if (ecc & ONENAND_ECC_2BIT_ALL) {
+                       printk("onenand_wait: ECC error = 0x%04x\n", ecc);
+                       return -EBADMSG;
+               }
+       }
+
        if (ctrl & ONENAND_CTRL_ERROR) {
                printk("onenand_wait: controller error = 0x%04x\n", ctrl);
                if (ctrl & ONENAND_CTRL_LOCK)
@@ -324,14 +496,6 @@ static int onenand_wait(struct mtd_info *mtd, int state)
                return -EIO;
        }
 
-       if (interrupt & ONENAND_INT_READ) {
-               ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
-               if (ecc & ONENAND_ECC_2BIT_ALL) {
-                       MTDDEBUG (MTD_DEBUG_LEVEL0,
-                                 "onenand_wait: ECC error = 0x%04x\n", ecc);
-                       return -EBADMSG;
-               }
-       }
 
        return 0;
 }
@@ -475,10 +639,6 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
        int blockpage, found = 0;
        unsigned int i;
 
-#ifdef CONFIG_S3C64XX
-       return 0;
-#endif
-
        if (ONENAND_IS_2PLANE(this))
                blockpage = onenand_get_2x_blockpage(mtd, addr);
        else
@@ -499,7 +659,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr)
 
        if (found && ONENAND_IS_DDP(this)) {
                /* Select DataRAM for DDP */
-               int block = (int) (addr >> this->erase_shift);
+               int block = onenand_block(this, addr);
                int value = onenand_bufferram_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
        }
@@ -590,7 +750,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
@@ -608,7 +768,8 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
        uint8_t *oob_buf = this->oob_buf;
 
        free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
+            i++, free++) {
                if (readcol >= lastgap)
                        readcol += free->offset - lastgap;
                if (readend >= lastgap)
@@ -617,7 +778,8 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
        }
        this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize);
        free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
+            i++, free++) {
                int free_end = free->offset + free->length;
                if (free->offset < readend && free_end > readcol) {
                        int st = max_t(int,free->offset,readcol);
@@ -631,6 +793,45 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf,
        return 0;
 }
 
+/**
+ * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data
+ * @param mtd          MTD device structure
+ * @param addr         address to recover
+ * @param status       return value from onenand_wait
+ *
+ * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has
+ * lower page address and MSB page has higher page address in paired pages.
+ * If power off occurs during MSB page program, the paired LSB page data can
+ * become corrupt. LSB page recovery read is a way to read LSB page though page
+ * data are corrupted. When uncorrectable error occurs as a result of LSB page
+ * read after power up, issue LSB page recovery read.
+ */
+static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
+{
+       struct onenand_chip *this = mtd->priv;
+       int i;
+
+       /* Recovery is only for Flex-OneNAND */
+       if (!FLEXONENAND(this))
+               return status;
+
+       /* check if we failed due to uncorrectable error */
+       if (!mtd_is_eccerr(status) && status != ONENAND_BBT_READ_ECC_ERROR)
+               return status;
+
+       /* check if address lies in MLC region */
+       i = flexonenand_region(mtd, addr);
+       if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift))
+               return status;
+
+       printk("onenand_recover_lsb:"
+               "Attempting to recover from uncorrectable read\n");
+
+       /* Issue the LSB page recovery command */
+       this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize);
+       return this->wait(mtd, FL_READING);
+}
+
 /**
  * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band
  * @param mtd          MTD device structure
@@ -655,7 +856,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
 
        MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
 
-       if (ops->mode == MTD_OOB_AUTO)
+       if (ops->mode == MTD_OPS_AUTO_OOB)
                oobsize = this->ecclayout->oobavail;
        else
                oobsize = mtd->oobsize;
@@ -673,6 +874,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        stats = mtd->ecc_stats;
 
        /* Read-while-load method */
+       /* Note: We can't use this feature in MLC */
 
        /* Do first load to bufferRAM */
        if (read < len) {
@@ -680,6 +882,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        this->main_buf = buf;
                        this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        ret = this->wait(mtd, FL_READING);
+                       if (unlikely(ret))
+                               ret = onenand_recover_lsb(mtd, from, ret);
                        onenand_update_bufferram(mtd, from, !ret);
                        if (ret == -EBADMSG)
                                ret = 0;
@@ -694,7 +898,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        while (!ret) {
                /* If there is more to load then start next load */
                from += thislen;
-               if (read + thislen < len) {
+               if (!ONENAND_IS_4KB_PAGE(this) && read + thislen < len) {
                        this->main_buf = buf + thislen;
                        this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        /*
@@ -719,7 +923,7 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        thisooblen = oobsize - oobcolumn;
                        thisooblen = min_t(int, thisooblen, ooblen - oobread);
 
-                       if (ops->mode == MTD_OOB_AUTO)
+                       if (ops->mode == MTD_OPS_AUTO_OOB)
                                onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
                        else
                                this->read_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
@@ -728,6 +932,16 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                        oobcolumn = 0;
                }
 
+               if (ONENAND_IS_4KB_PAGE(this) && (read + thislen < len)) {
+                       this->command(mtd, ONENAND_CMD_READ, from, writesize);
+                       ret = this->wait(mtd, FL_READING);
+                       if (unlikely(ret))
+                               ret = onenand_recover_lsb(mtd, from, ret);
+                       onenand_update_bufferram(mtd, from, !ret);
+                       if (mtd_is_eccerr(ret))
+                               ret = 0;
+               }
+
                /* See if we are done */
                read += thislen;
                if (read == len)
@@ -735,16 +949,19 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
                /* Set up for next read from bufferRAM */
                if (unlikely(boundary))
                        this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2);
-               ONENAND_SET_NEXT_BUFFERRAM(this);
+               if (!ONENAND_IS_4KB_PAGE(this))
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
                buf += thislen;
                thislen = min_t(int, writesize, len - read);
                column = 0;
 
-               /* Now wait for load */
-               ret = this->wait(mtd, FL_READING);
-               onenand_update_bufferram(mtd, from, !ret);
-               if (ret == -EBADMSG)
-                       ret = 0;
+               if (!ONENAND_IS_4KB_PAGE(this)) {
+                       /* Now wait for load */
+                       ret = this->wait(mtd, FL_READING);
+                       onenand_update_bufferram(mtd, from, !ret);
+                       if (mtd_is_eccerr(ret))
+                               ret = 0;
+               }
        }
 
        /*
@@ -761,7 +978,8 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, loff_t from,
        if (mtd->ecc_stats.failed - stats.failed)
                return -EBADMSG;
 
-       return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+       /* return max bitflips per ecc step; ONENANDs correct 1 bit only */
+       return mtd->ecc_stats.corrected != stats.corrected ? 1 : 0;
 }
 
 /**
@@ -779,9 +997,9 @@ 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;
+       int ret = 0, readcmd;
 
        from += ops->ooboffs;
 
@@ -790,7 +1008,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
        /* Initialize return length value */
        ops->oobretlen = 0;
 
-       if (mode == MTD_OOB_AUTO)
+       if (mode == MTD_OPS_AUTO_OOB)
                oobsize = this->ecclayout->oobavail;
        else
                oobsize = mtd->oobsize;
@@ -812,22 +1030,28 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
 
        stats = mtd->ecc_stats;
 
+       readcmd = ONENAND_IS_4KB_PAGE(this) ?
+               ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
        while (read < len) {
                thislen = oobsize - column;
                thislen = min_t(int, thislen, len);
 
                this->spare_buf = buf;
-               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+               this->command(mtd, readcmd, from, mtd->oobsize);
 
                onenand_update_bufferram(mtd, from, 0);
 
                ret = this->wait(mtd, FL_READING);
+               if (unlikely(ret))
+                       ret = onenand_recover_lsb(mtd, from, ret);
+
                if (ret && ret != -EBADMSG) {
                        printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);
                        break;
                }
 
-               if (mode == MTD_OOB_AUTO)
+               if (mode == MTD_OPS_AUTO_OOB)
                        onenand_transfer_auto_oob(mtd, buf, column, thislen);
                else
                        this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
@@ -901,10 +1125,10 @@ int onenand_read_oob(struct mtd_info *mtd, loff_t from,
        int ret;
 
        switch (ops->mode) {
-       case MTD_OOB_PLACE:
-       case MTD_OOB_AUTO:
+       case MTD_OPS_PLACE_OOB:
+       case MTD_OPS_AUTO_OOB:
                break;
-       case MTD_OOB_RAW:
+       case MTD_OPS_RAW:
                /* Not implemented yet */
        default:
                return -EINVAL;
@@ -945,9 +1169,12 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state)
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
        if (interrupt & ONENAND_INT_READ) {
-               int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
-               if (ecc & ONENAND_ECC_2BIT_ALL)
+               int ecc = onenand_read_ecc(this);
+               if (ecc & ONENAND_ECC_2BIT_ALL) {
+                       printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
+                               ", controller = 0x%04x\n", ecc, ctrl);
                        return ONENAND_BBT_READ_ERROR;
+               }
        } else {
                printk(KERN_ERR "onenand_bbt_wait: read timeout!"
                                "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);
@@ -976,12 +1203,15 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
 {
        struct onenand_chip *this = mtd->priv;
        int read = 0, thislen, column;
-       int ret = 0;
+       int ret = 0, readcmd;
        size_t len = ops->ooblen;
        u_char *buf = ops->oobbuf;
 
        MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);
 
+       readcmd = ONENAND_IS_4KB_PAGE(this) ?
+               ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
        /* Initialize return value */
        ops->oobretlen = 0;
 
@@ -1002,15 +1232,18 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                thislen = min_t(int, thislen, len);
 
                this->spare_buf = buf;
-               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+               this->command(mtd, readcmd, from, mtd->oobsize);
 
                onenand_update_bufferram(mtd, from, 0);
 
                ret = this->bbt_wait(mtd, FL_READING);
+               if (unlikely(ret))
+                       ret = onenand_recover_lsb(mtd, from, ret);
+
                if (ret)
                        break;
 
-               this->read_spareram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
+               this->read_bufferram(mtd, 0, ONENAND_SPARERAM, buf, column, thislen);
                read += thislen;
                if (read == len)
                        break;
@@ -1044,9 +1277,12 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to
 {
        struct onenand_chip *this = mtd->priv;
        u_char *oob_buf = this->oob_buf;
-       int status, i;
+       int status, i, readcmd;
 
-       this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+       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);
        status = this->wait(mtd, FL_READING);
        if (status)
@@ -1111,7 +1347,7 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr,
 #define NOTALIGNED(x)  ((x & (this->subpagesize - 1)) != 0)
 
 /**
- * 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
@@ -1129,7 +1365,8 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
        unsigned int i;
 
        free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
+            i++, free++) {
                if (writecol >= lastgap)
                        writecol += free->offset - lastgap;
                if (writeend >= lastgap)
@@ -1137,7 +1374,8 @@ static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,
                lastgap = free->offset + free->length;
        }
        free = this->ecclayout->oobfree;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE && free->length;
+            i++, free++) {
                int free_end = free->offset + free->length;
                if (free->offset < writeend && free_end > writecol) {
                        int st = max_t(int,free->offset,writecol);
@@ -1178,19 +1416,13 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
        ops->retlen = 0;
        ops->oobretlen = 0;
 
-       /* Do not allow writes past end of device */
-       if (unlikely((to + len) > mtd->size)) {
-               printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");
-               return -EINVAL;
-       }
-
        /* Reject writes, which are not page aligned */
        if (unlikely(NOTALIGNED(to) || NOTALIGNED(len))) {
                printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");
                return -EINVAL;
        }
 
-       if (ops->mode == MTD_OOB_AUTO)
+       if (ops->mode == MTD_OPS_AUTO_OOB)
                oobsize = this->ecclayout->oobavail;
        else
                oobsize = mtd->oobsize;
@@ -1224,7 +1456,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                        /* We send data to spare ram with oobsize
                         *                          * to prevent byte access */
                        memset(oobbuf, 0xff, mtd->oobsize);
-                       if (ops->mode == MTD_OOB_AUTO)
+                       if (ops->mode == MTD_OPS_AUTO_OOB)
                                onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
                        else
                                memcpy(oobbuf + oobcolumn, oob, thisooblen);
@@ -1276,7 +1508,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
@@ -1291,11 +1523,11 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 {
        struct onenand_chip *this = mtd->priv;
        int column, ret = 0, oobsize;
-       int written = 0;
+       int written = 0, oobcmd;
        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;
 
@@ -1304,7 +1536,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
        /* Initialize retlen, in case of early exit */
        ops->oobretlen = 0;
 
-       if (mode == MTD_OOB_AUTO)
+       if (mode == MTD_OPS_AUTO_OOB)
                oobsize = this->ecclayout->oobavail;
        else
                oobsize = mtd->oobsize;
@@ -1333,6 +1565,9 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
 
        oobbuf = this->oob_buf;
 
+       oobcmd = ONENAND_IS_4KB_PAGE(this) ?
+               ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB;
+
        /* Loop until all data write */
        while (written < len) {
                int thislen = min_t(int, oobsize, len - written);
@@ -1342,13 +1577,20 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
                /* We send data to spare ram with oobsize
                 * to prevent byte access */
                memset(oobbuf, 0xff, mtd->oobsize);
-               if (mode == MTD_OOB_AUTO)
+               if (mode == MTD_OPS_AUTO_OOB)
                        onenand_fill_auto_oob(mtd, oobbuf, buf, column, thislen);
                else
                        memcpy(oobbuf + column, buf, thislen);
                this->write_bufferram(mtd, 0, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
 
-               this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+               if (ONENAND_IS_4KB_PAGE(this)) {
+                       /* Set main area of DataRAM to 0xff*/
+                       memset(this->page_buf, 0xff, mtd->writesize);
+                       this->write_bufferram(mtd, 0, ONENAND_DATARAM,
+                               this->page_buf, 0, mtd->writesize);
+               }
+
+               this->command(mtd, oobcmd, to, mtd->oobsize);
 
                onenand_update_bufferram(mtd, to, 0);
                if (ONENAND_IS_2PLANE(this)) {
@@ -1425,10 +1667,10 @@ int onenand_write_oob(struct mtd_info *mtd, loff_t to,
        int ret;
 
        switch (ops->mode) {
-       case MTD_OOB_PLACE:
-       case MTD_OOB_AUTO:
+       case MTD_OPS_PLACE_OOB:
+       case MTD_OPS_AUTO_OOB:
                break;
-       case MTD_OOB_RAW:
+       case MTD_OPS_RAW:
                /* Not implemented yet */
        default:
                return -EINVAL;
@@ -1475,46 +1717,54 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
        struct onenand_chip *this = mtd->priv;
        unsigned int block_size;
-       loff_t addr;
-       int len;
-       int ret = 0;
-
-       MTDDEBUG (MTD_DEBUG_LEVEL3,
-                "onenand_erase: start = 0x%08x, len = %i\n",
-                (unsigned int)instr->addr, (unsigned int)instr->len);
-
-       block_size = (1 << this->erase_shift);
+       loff_t addr = instr->addr;
+       unsigned int len = instr->len;
+       int ret = 0, i;
+       struct mtd_erase_region_info *region = NULL;
+       unsigned int region_end = 0;
+
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
+                       (unsigned int) addr, len);
+
+       if (FLEXONENAND(this)) {
+               /* Find the eraseregion of this address */
+               i = flexonenand_region(mtd, addr);
+               region = &mtd->eraseregions[i];
+
+               block_size = region->erasesize;
+               region_end = region->offset
+                       + region->erasesize * region->numblocks;
+
+               /* Start address within region must align on block boundary.
+                * Erase region's start offset is always block start address.
+                */
+               if (unlikely((addr - region->offset) & (block_size - 1))) {
+                       MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
+                               " Unaligned address\n");
+                       return -EINVAL;
+               }
+       } else {
+               block_size = 1 << this->erase_shift;
 
-       /* Start address must align on block boundary */
-       if (unlikely(instr->addr & (block_size - 1))) {
-               MTDDEBUG (MTD_DEBUG_LEVEL0,
-                        "onenand_erase: Unaligned address\n");
-               return -EINVAL;
+               /* Start address must align on block boundary */
+               if (unlikely(addr & (block_size - 1))) {
+                       MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
+                                               "Unaligned address\n");
+                       return -EINVAL;
+               }
        }
 
        /* Length must align on block boundary */
-       if (unlikely(instr->len & (block_size - 1))) {
+       if (unlikely(len & (block_size - 1))) {
                MTDDEBUG (MTD_DEBUG_LEVEL0,
                         "onenand_erase: Length not block aligned\n");
                return -EINVAL;
        }
 
-       /* Do not allow erase past end of device */
-       if (unlikely((instr->len + instr->addr) > mtd->size)) {
-               MTDDEBUG (MTD_DEBUG_LEVEL0,
-                        "onenand_erase: Erase past end of device\n");
-               return -EINVAL;
-       }
-
-       instr->fail_addr = 0xffffffff;
-
        /* Grab the lock and see if the device is available */
        onenand_get_device(mtd, FL_ERASING);
 
        /* Loop throught the pages */
-       len = instr->len;
-       addr = instr->addr;
-
        instr->state = MTD_ERASING;
 
        while (len) {
@@ -1541,14 +1791,7 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
                        else
                                MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: "
                                          "Failed erase, block %d\n",
-                                         (unsigned)(addr >> this->erase_shift));
-                       if (ret == -EPERM)
-                               printk("onenand_erase: "
-                                         "Device is write protected!!!\n");
-                       else
-                               printk("onenand_erase: "
-                                         "Failed erase, block %d\n",
-                                         (unsigned)(addr >> this->erase_shift));
+                                       onenand_block(this, addr));
                        instr->state = MTD_ERASE_FAILED;
                        instr->fail_addr = addr;
 
@@ -1557,6 +1800,23 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
 
                len -= block_size;
                addr += block_size;
+
+               if (addr == region_end) {
+                       if (!len)
+                               break;
+                       region++;
+
+                       block_size = region->erasesize;
+                       region_end = region->offset
+                               + region->erasesize * region->numblocks;
+
+                       if (len & (block_size - 1)) {
+                               /* This has been checked at MTD
+                                * partitioning level. */
+                               printk("onenand_erase: Unaligned address\n");
+                               goto erase_exit;
+                       }
+               }
        }
 
        instr->state = MTD_ERASE_DONE;
@@ -1626,7 +1886,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        struct bbm_info *bbm = this->bbm;
        u_char buf[2] = {0, 0};
        struct mtd_oob_ops ops = {
-               .mode = MTD_OOB_PLACE,
+               .mode = MTD_OPS_PLACE_OOB,
                .ooblen = 2,
                .oobbuf = buf,
                .ooboffs = 0,
@@ -1634,7 +1894,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
        int block;
 
        /* Get block number */
-       block = ((int) ofs) >> bbm->bbt_erase_shift;
+       block = onenand_block(this, ofs);
        if (bbm->bbt)
                bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
 
@@ -1652,7 +1912,6 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
  */
 int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
 {
-       struct onenand_chip *this = mtd->priv;
        int ret;
 
        ret = onenand_block_isbad(mtd, ofs);
@@ -1663,7 +1922,7 @@ int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
                return ret;
        }
 
-       ret = this->block_markbad(mtd, ofs);
+       ret = mtd_block_markbad(mtd, ofs);
        return ret;
 }
 
@@ -1680,15 +1939,9 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
 {
        struct onenand_chip *this = mtd->priv;
        int start, end, block, value, status;
-       int wp_status_mask;
-
-       start = ofs >> this->erase_shift;
-       end = len >> this->erase_shift;
 
-       if (cmd == ONENAND_CMD_LOCK)
-               wp_status_mask = ONENAND_WP_LS;
-       else
-               wp_status_mask = ONENAND_WP_US;
+       start = onenand_block(this, ofs);
+       end = onenand_block(this, ofs + len);
 
        /* Continuous lock scheme */
        if (this->options & ONENAND_HAS_CONT_LOCK) {
@@ -1718,7 +1971,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
        }
 
        /* Block lock scheme */
-       for (block = start; block < start + end; block++) {
+       for (block = start; block < end; block++) {
                /* Set block address */
                value = onenand_block_address(this, block);
                this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
@@ -1831,7 +2084,7 @@ static void onenand_unlock_all(struct mtd_info *mtd)
 {
        struct onenand_chip *this = mtd->priv;
        loff_t ofs = 0;
-       size_t len = this->chipsize;
+       size_t len = mtd->size;
 
        if (this->options & ONENAND_HAS_UNLOCK_ALL) {
                /* Set start block address */
@@ -1847,14 +2100,12 @@ static void onenand_unlock_all(struct mtd_info *mtd)
                                & ONENAND_CTRL_ONGO)
                        continue;
 
-               return;
-
                /* Check lock status */
                if (onenand_check_lock_status(this))
                        return;
 
                /* Workaround for all block unlock in DDP */
-               if (ONENAND_IS_DDP(this)) {
+               if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) {
                        /* All blocks on another chip */
                        ofs = this->chipsize >> 1;
                        len = this->chipsize >> 1;
@@ -1885,7 +2136,10 @@ static void onenand_check_features(struct mtd_info *mtd)
        /* Lock scheme */
        switch (density) {
        case ONENAND_DEVICE_DENSITY_4Gb:
-               this->options |= ONENAND_HAS_2PLANE;
+               if (ONENAND_IS_DDP(this))
+                       this->options |= ONENAND_HAS_2PLANE;
+               else
+                       this->options |= ONENAND_HAS_4KB_PAGE;
 
        case ONENAND_DEVICE_DENSITY_2Gb:
                /* 2Gb DDP don't have 2 plane */
@@ -1906,12 +2160,26 @@ static void onenand_check_features(struct mtd_info *mtd)
                break;
        }
 
+       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)) {
+               this->options &= ~ONENAND_HAS_CONT_LOCK;
+               this->options |= ONENAND_HAS_UNLOCK_ALL;
+       }
+
        if (this->options & ONENAND_HAS_CONT_LOCK)
                printk(KERN_DEBUG "Lock scheme is Continuous Lock\n");
        if (this->options & ONENAND_HAS_UNLOCK_ALL)
                printk(KERN_DEBUG "Chip support all block unlock\n");
        if (this->options & ONENAND_HAS_2PLANE)
                printk(KERN_DEBUG "Chip has 2 plane\n");
+       if (this->options & ONENAND_HAS_4KB_PAGE)
+               printk(KERN_DEBUG "Chip has 4KiB pagesize\n");
+
 }
 
 /**
@@ -1922,16 +2190,18 @@ static void onenand_check_features(struct mtd_info *mtd)
  */
 char *onenand_print_device_info(int device, int version)
 {
-       int vcc, demuxed, ddp, density;
+       int vcc, demuxed, ddp, density, flexonenand;
        char *dev_info = malloc(80);
        char *p = dev_info;
 
        vcc = device & ONENAND_DEVICE_VCC_MASK;
        demuxed = device & ONENAND_DEVICE_IS_DEMUX;
        ddp = device & ONENAND_DEVICE_IS_DDP;
-       density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-       p += sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
+       density = onenand_get_density(device);
+       flexonenand = device & DEVICE_IS_FLEXONENAND;
+       p += sprintf(dev_info, "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
               demuxed ? "" : "Muxed ",
+              flexonenand ? "Flex-" : "",
               ddp ? "(DDP)" : "",
               (16 << density), vcc ? "2.65/3.3" : "1.8", device);
 
@@ -1942,6 +2212,7 @@ char *onenand_print_device_info(int device, int version)
 }
 
 static const struct onenand_manufacturers onenand_manuf_ids[] = {
+       {ONENAND_MFR_NUMONYX, "Numonyx"},
        {ONENAND_MFR_SAMSUNG, "Samsung"},
 };
 
@@ -1954,19 +2225,21 @@ static const struct onenand_manufacturers onenand_manuf_ids[] = {
 static int onenand_check_maf(int manuf)
 {
        int size = ARRAY_SIZE(onenand_manuf_ids);
-       char *name;
        int i;
+#ifdef ONENAND_DEBUG
+       char *name;
+#endif
 
-       for (i = 0; size; i++)
+       for (i = 0; i < size; i++)
                if (manuf == onenand_manuf_ids[i].id)
                        break;
 
+#ifdef ONENAND_DEBUG
        if (i < size)
                name = onenand_manuf_ids[i].name;
        else
                name = "Unknown";
 
-#ifdef ONENAND_DEBUG
        printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n", name, manuf);
 #endif
 
@@ -1974,23 +2247,283 @@ static int onenand_check_maf(int manuf)
 }
 
 /**
- * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+* flexonenand_get_boundary     - Reads the SLC boundary
+* @param onenand_info          - onenand info structure
+*
+* Fill up boundary[] field in onenand_chip
+**/
+static int flexonenand_get_boundary(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned int die, bdry;
+       int syscfg, locked;
+
+       /* Disable ECC */
+       syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+       this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1);
+
+       for (die = 0; die < this->dies; die++) {
+               this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+               this->wait(mtd, FL_SYNCING);
+
+               this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+               this->wait(mtd, FL_READING);
+
+               bdry = this->read_word(this->base + ONENAND_DATARAM);
+               if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3)
+                       locked = 0;
+               else
+                       locked = 1;
+               this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
+
+               this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+               this->wait(mtd, FL_RESETING);
+
+               printk(KERN_INFO "Die %d boundary: %d%s\n", die,
+                      this->boundary[die], locked ? "(Locked)" : "(Unlocked)");
+       }
+
+       /* Enable ECC */
+       this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+       return 0;
+}
+
+/**
+ * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info
+ *                       boundary[], diesize[], mtd->size, mtd->erasesize,
+ *                       mtd->eraseregions
+ * @param mtd          - MTD device structure
+ */
+static void flexonenand_get_size(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int die, i, eraseshift, density;
+       int blksperdie, maxbdry;
+       loff_t ofs;
+
+       density = onenand_get_density(this->device_id);
+       blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift);
+       blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+       maxbdry = blksperdie - 1;
+       eraseshift = this->erase_shift - 1;
+
+       mtd->numeraseregions = this->dies << 1;
+
+       /* This fills up the device boundary */
+       flexonenand_get_boundary(mtd);
+       die = 0;
+       ofs = 0;
+       i = -1;
+       for (; die < this->dies; die++) {
+               if (!die || this->boundary[die-1] != maxbdry) {
+                       i++;
+                       mtd->eraseregions[i].offset = ofs;
+                       mtd->eraseregions[i].erasesize = 1 << eraseshift;
+                       mtd->eraseregions[i].numblocks =
+                                                       this->boundary[die] + 1;
+                       ofs += mtd->eraseregions[i].numblocks << eraseshift;
+                       eraseshift++;
+               } else {
+                       mtd->numeraseregions -= 1;
+                       mtd->eraseregions[i].numblocks +=
+                                                       this->boundary[die] + 1;
+                       ofs += (this->boundary[die] + 1) << (eraseshift - 1);
+               }
+               if (this->boundary[die] != maxbdry) {
+                       i++;
+                       mtd->eraseregions[i].offset = ofs;
+                       mtd->eraseregions[i].erasesize = 1 << eraseshift;
+                       mtd->eraseregions[i].numblocks = maxbdry ^
+                                                        this->boundary[die];
+                       ofs += mtd->eraseregions[i].numblocks << eraseshift;
+                       eraseshift--;
+               } else
+                       mtd->numeraseregions -= 1;
+       }
+
+       /* Expose MLC erase size except when all blocks are SLC */
+       mtd->erasesize = 1 << this->erase_shift;
+       if (mtd->numeraseregions == 1)
+               mtd->erasesize >>= 1;
+
+       printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions);
+       for (i = 0; i < mtd->numeraseregions; i++)
+               printk(KERN_INFO "[offset: 0x%08llx, erasesize: 0x%05x,"
+                       " numblocks: %04u]\n", mtd->eraseregions[i].offset,
+                       mtd->eraseregions[i].erasesize,
+                       mtd->eraseregions[i].numblocks);
+
+       for (die = 0, mtd->size = 0; die < this->dies; die++) {
+               this->diesize[die] = (loff_t) (blksperdie << this->erase_shift);
+               this->diesize[die] -= (loff_t) (this->boundary[die] + 1)
+                                                << (this->erase_shift - 1);
+               mtd->size += this->diesize[die];
+       }
+}
+
+/**
+ * flexonenand_check_blocks_erased - Check if blocks are erased
+ * @param mtd_info     - mtd info structure
+ * @param start                - first erase block to check
+ * @param end          - last erase block to check
+ *
+ * Converting an unerased block from MLC to SLC
+ * causes byte values to change. Since both data and its ECC
+ * have changed, reads on the block give uncorrectable error.
+ * This might lead to the block being detected as bad.
+ *
+ * Avoid this by ensuring that the block to be converted is
+ * erased.
+ */
+static int flexonenand_check_blocks_erased(struct mtd_info *mtd,
+                                       int start, int end)
+{
+       struct onenand_chip *this = mtd->priv;
+       int i, ret;
+       int block;
+       struct mtd_oob_ops ops = {
+               .mode = MTD_OPS_PLACE_OOB,
+               .ooboffs = 0,
+               .ooblen = mtd->oobsize,
+               .datbuf = NULL,
+               .oobbuf = this->oob_buf,
+       };
+       loff_t addr;
+
+       printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end);
+
+       for (block = start; block <= end; block++) {
+               addr = flexonenand_addr(this, block);
+               if (onenand_block_isbad_nolock(mtd, addr, 0))
+                       continue;
+
+               /*
+                * Since main area write results in ECC write to spare,
+                * it is sufficient to check only ECC bytes for change.
+                */
+               ret = onenand_read_oob_nolock(mtd, addr, &ops);
+               if (ret)
+                       return ret;
+
+               for (i = 0; i < mtd->oobsize; i++)
+                       if (this->oob_buf[i] != 0xff)
+                               break;
+
+               if (i != mtd->oobsize) {
+                       printk(KERN_WARNING "Block %d not erased.\n", block);
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * flexonenand_set_boundary    - Writes the SLC boundary
+ * @param mtd                  - mtd info structure
+ */
+int flexonenand_set_boundary(struct mtd_info *mtd, int die,
+                                   int boundary, int lock)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ret, density, blksperdie, old, new, thisboundary;
+       loff_t addr;
+
+       if (die >= this->dies)
+               return -EINVAL;
+
+       if (boundary == this->boundary[die])
+               return 0;
+
+       density = onenand_get_density(this->device_id);
+       blksperdie = ((16 << density) << 20) >> this->erase_shift;
+       blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
+
+       if (boundary >= blksperdie) {
+               printk("flexonenand_set_boundary:"
+                       "Invalid boundary value. "
+                       "Boundary not changed.\n");
+               return -EINVAL;
+       }
+
+       /* Check if converting blocks are erased */
+       old = this->boundary[die] + (die * this->density_mask);
+       new = boundary + (die * this->density_mask);
+       ret = flexonenand_check_blocks_erased(mtd, min(old, new)
+                                               + 1, max(old, new));
+       if (ret) {
+               printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n");
+               return ret;
+       }
+
+       this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0);
+       this->wait(mtd, FL_SYNCING);
+
+       /* Check is boundary is locked */
+       this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0);
+       ret = this->wait(mtd, FL_READING);
+
+       thisboundary = this->read_word(this->base + ONENAND_DATARAM);
+       if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) {
+               printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n");
+               goto out;
+       }
+
+       printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n",
+                       die, boundary, lock ? "(Locked)" : "(Unlocked)");
+
+       boundary &= FLEXONENAND_PI_MASK;
+       boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
+
+       addr = die ? this->diesize[0] : 0;
+       this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
+       ret = this->wait(mtd, FL_ERASING);
+       if (ret) {
+               printk("flexonenand_set_boundary:"
+                       "Failed PI erase for Die %d\n", die);
+               goto out;
+       }
+
+       this->write_word(boundary, this->base + ONENAND_DATARAM);
+       this->command(mtd, ONENAND_CMD_PROG, addr, 0);
+       ret = this->wait(mtd, FL_WRITING);
+       if (ret) {
+               printk("flexonenand_set_boundary:"
+                       "Failed PI write for Die %d\n", die);
+               goto out;
+       }
+
+       this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0);
+       ret = this->wait(mtd, FL_WRITING);
+out:
+       this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND);
+       this->wait(mtd, FL_RESETING);
+       if (!ret)
+               /* Recalculate device size on boundary change*/
+               flexonenand_get_size(mtd);
+
+       return ret;
+}
+
+/**
+ * onenand_chip_probe - [OneNAND Interface] Probe the OneNAND chip
  * @param mtd          MTD device structure
  *
  * OneNAND detection method:
  *   Compare the 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 */
        syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+
        /* Clear Sync. Burst Read mode to read BootRAM */
-       this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
+       this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ),
+                        this->base + ONENAND_REG_SYS_CFG1);
 
        /* Send the command for reading device ID from BootRAM */
        this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
@@ -2015,62 +2548,107 @@ 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);
 
        /* Check OneNAND device */
        if (maf_id != bram_maf_id || dev_id != bram_dev_id)
                return -ENXIO;
 
-       /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */
-       if (dev_id & (1 << 9)) {
-               printk("Not yet support Flex-OneNAND\n");
-               return -ENXIO;
-       }
+       return 0;
+}
+
+/**
+ * onenand_probe - [OneNAND Interface] Probe the OneNAND device
+ * @param mtd          MTD device structure
+ *
+ * OneNAND detection method:
+ *   Compare the the values from command with ones from register
+ */
+int onenand_probe(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int dev_id, ver_id;
+       int density;
+       int ret;
+
+       ret = this->chip_probe(mtd);
+       if (ret)
+               return ret;
+
+       /* Read device IDs from Register */
+       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 */
        mtd->name = onenand_print_device_info(dev_id, ver_id);
        this->device_id = dev_id;
        this->version_id = ver_id;
 
+       /* Check OneNAND features */
+       onenand_check_features(mtd);
+
        density = onenand_get_density(dev_id);
+       if (FLEXONENAND(this)) {
+               this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
+               /* Maximum possible erase regions */
+               mtd->numeraseregions = this->dies << 1;
+               mtd->eraseregions = malloc(sizeof(struct mtd_erase_region_info)
+                                       * (this->dies << 1));
+               if (!mtd->eraseregions)
+                       return -ENOMEM;
+       }
+
+       /*
+        * For Flex-OneNAND, chipsize represents maximum possible device size.
+        * mtd->size represents the actual device size.
+        */
        this->chipsize = (16 << density) << 20;
-       /* Set density mask. it is used for DDP */
-       if (ONENAND_IS_DDP(this))
-               this->density_mask = (1 << (density + 6));
-       else
-               this->density_mask = 0;
 
        /* OneNAND page size & block size */
        /* 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_4KB_PAGE(this))
+               mtd->writesize <<= 1;
+
        mtd->oobsize = mtd->writesize >> 5;
        /* Pagers per block is always 64 in OneNAND */
        mtd->erasesize = mtd->writesize << 6;
+       /*
+        * Flex-OneNAND SLC area has 64 pages per block.
+        * Flex-OneNAND MLC area has 128 pages per block.
+        * Expose MLC erase size to find erase_shift and page_mask.
+        */
+       if (FLEXONENAND(this))
+               mtd->erasesize <<= 1;
 
        this->erase_shift = ffs(mtd->erasesize) - 1;
        this->page_shift = ffs(mtd->writesize) - 1;
        this->ppb_shift = (this->erase_shift - this->page_shift);
        this->page_mask = (mtd->erasesize / mtd->writesize) - 1;
+       /* Set density mask. it is used for DDP */
+       if (ONENAND_IS_DDP(this))
+               this->density_mask = this->chipsize >> (this->erase_shift + 1);
        /* It's real page size */
        this->writesize = mtd->writesize;
 
        /* REVIST: Multichip handling */
 
-       mtd->size = this->chipsize;
-
-       /* Check OneNAND features */
-       onenand_check_features(mtd);
+       if (FLEXONENAND(this))
+               flexonenand_get_size(mtd);
+       else
+               mtd->size = this->chipsize;
 
        mtd->flags = MTD_CAP_NANDFLASH;
-       mtd->erase = onenand_erase;
-       mtd->read = onenand_read;
-       mtd->write = onenand_write;
-       mtd->read_oob = onenand_read_oob;
-       mtd->write_oob = onenand_write_oob;
-       mtd->sync = onenand_sync;
-       mtd->block_isbad = onenand_block_isbad;
-       mtd->block_markbad = onenand_block_markbad;
+       mtd->_erase = onenand_erase;
+       mtd->_read = onenand_read;
+       mtd->_write = onenand_write;
+       mtd->_read_oob = onenand_read_oob;
+       mtd->_write_oob = onenand_write_oob;
+       mtd->_sync = onenand_sync;
+       mtd->_block_isbad = onenand_block_isbad;
+       mtd->_block_markbad = onenand_block_markbad;
 
        return 0;
 }
@@ -2104,11 +2682,12 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
 
        if (!this->read_bufferram)
                this->read_bufferram = onenand_read_bufferram;
-       if (!this->read_spareram)
-               this->read_spareram = onenand_read_bufferram;
        if (!this->write_bufferram)
                this->write_bufferram = onenand_write_bufferram;
 
+       if (!this->chip_probe)
+               this->chip_probe = onenand_chip_probe;
+
        if (!this->block_markbad)
                this->block_markbad = onenand_default_block_markbad;
        if (!this->scan_bbt)
@@ -2151,6 +2730,11 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
         * Allow subpage writes up to oobsize.
         */
        switch (mtd->oobsize) {
+       case 128:
+               this->ecclayout = &onenand_oob_128;
+               mtd->subpage_sft = 0;
+               break;
+
        case 64:
                this->ecclayout = &onenand_oob_64;
                mtd->subpage_sft = 2;
@@ -2177,7 +2761,8 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
         * the out of band area
         */
        this->ecclayout->oobavail = 0;
-       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES &&
+
+       for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES_LARGE &&
            this->ecclayout->oobfree[i].length; i++)
                this->ecclayout->oobavail +=
                        this->ecclayout->oobfree[i].length;