Hi Scott,
Thanks for the comments.

Scott Wood wrote:
> On Mon, Sep 22, 2008 at 11:58:51AM +0530, apgmoorthy wrote:
>> Hi All,
>> This patch adds support for Samsung Flex-OneNAND devices.
>>
>> Flex-OneNAND combines SLC and MLC technologies into a single
>> device. SLC area provides increased reliability and speed, suitable
>> for storing code and data, such as bootloader, kernel
>> and root file system. MLC area provides high density and is best used
>> for storing user data. Users can configure the size of SLC and MLC
>> regions through 'onenand setboundary' command.
>>
>> Signed-off-by: Rohit Hagargundgi <[EMAIL PROTECTED]>
> 
> Sorry for the late reply...
> 
>>  extern struct mtd_info onenand_mtd;
>>  extern struct onenand_chip onenand_chip;
>> +loff_t flexonenand_get_addr(int block)
> 
> Space before function declarations.

Ok.

> 
>> +    for (block = start; block <= end; block++) {
>> +            if (FLEXONENAND(this))
>> +                    instr.addr = flexonenand_get_addr(block);
>> +            else
>> +                    instr.addr = block << onenand_chip.erase_shift;
>> +
>> +            if (FLEXONENAND(this) && (mtd->numeraseregions > 1)) {
>> +                    for (i = 0; i < mtd->numeraseregions &&
>> +                            mtd->eraseregions[i].offset <= instr.addr;
>> i++)
> 
> Patch is line-wrapped.

Sorry. Fixed it now.

> 
> Can some of this be abstracted through the driver interface, rather than
> putting a bunch of stuff into what should be a relatively straightforward
> command-line wrapper?

Ok. Now it is simplified.

> Perhaps the two regions should be exposed as separate devices.

On DDP Flex-OneNAND, regions can be 1, 2, 3 or 4 based on boundary setting.
Exposing as separate devices will be bit complex in these scenarios.

Also, comments from MTD mailing list have been included.

Thanks,
Rohit

Signed-off-by: Rohit Hagargundgi <[EMAIL PROTECTED]>
---
diff --git a/common/cmd_onenand.c b/common/cmd_onenand.c
index 8d87b78..59f047a 100644
--- a/common/cmd_onenand.c
+++ b/common/cmd_onenand.c
@@ -21,8 +21,71 @@
 extern struct mtd_info onenand_mtd;
 extern struct onenand_chip onenand_chip;
 
+static loff_t flexonenand_get_addr(int block)
+{
+       struct mtd_info *mtd = &onenand_mtd;
+       struct onenand_chip *this = mtd->priv;
+       loff_t ofs;
+       int die = 0, boundary;
+
+       ofs = 0;
+       if (this->dies == 2 && block >= this->density_mask) {
+               block -= this->density_mask;
+               die = 1;
+               ofs = this->diesize[0];
+       }
+       boundary = this->boundary[die];
+       ofs += block << (this->erase_shift - 1);
+       if (block > (boundary + 1))
+               ofs += (block - boundary - 1) << (this->erase_shift - 1);
+       return ofs;
+}
+
+static inline loff_t onenand_get_addr(int block)
+{
+       struct mtd_info *mtd = &onenand_mtd;
+       struct onenand_chip *this = mtd->priv;
+
+       if (!FLEXONENAND(this))
+               return block << onenand_chip.erase_shift;
+       return flexonenand_get_addr(block);
+}
+
+static int do_erase(ulong start, ulong end)
+{
+       struct mtd_info *mtd = &onenand_mtd;
+       struct erase_info instr = {
+               .callback       = NULL,
+       };
+       int i, ret;
+       ulong block;
+
+       printf("Erase block from %lu to %lu\n", start, end);
+
+       for (block = start; block <= end; block++) {
+               instr.addr = onenand_get_addr(block);
+               if (mtd->numeraseregions > 1) {
+                       i = flexonenand_region(mtd, instr.addr);
+                       instr.len = mtd->eraseregions[i].erasesize;
+               } else
+                       instr.len = mtd->erasesize;
+
+               if (mtd->block_isbad(mtd, instr.addr)) {
+                       printf("Skipping bad block %lu\n", block);
+                       continue;
+               }
+
+               ret = mtd->erase(&onenand_mtd, &instr);
+               if (ret)
+                       printf("erase failed %lu\n", block);
+       }
+       return 0;
+}
+
 int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
+       struct mtd_info *mtd = &onenand_mtd;
+       struct onenand_chip *this = mtd->priv;
        int ret = 0;
 
        switch (argc) {
@@ -42,11 +105,7 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char 
*argv[])
        default:
                /* At least 4 args */
                if (strncmp(argv[1], "erase", 5) == 0) {
-                       struct erase_info instr = {
-                               .callback       = NULL,
-                       };
                        ulong start, end;
-                       ulong block;
                        char *endtail;
 
                        if (strncmp(argv[2], "block", 5) == 0) {
@@ -57,28 +116,18 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char 
*argv[])
                                start = simple_strtoul(argv[2], NULL, 10);
                                end = simple_strtoul(argv[3], NULL, 10);
 
-                               start >>= onenand_chip.erase_shift;
-                               end >>= onenand_chip.erase_shift;
+                               start = onenand_get_block(&onenand_mtd,
+                                               start, NULL);
+                               end = onenand_get_block(&onenand_mtd,
+                                               end, NULL);
                                /* Don't include the end block */
-                               end--;
+                               if (end > 0)
+                                       end--;
                        }
 
                        if (!end || end < 0)
                                end = start;
-
-                       printf("Erase block from %lu to %lu\n", start, end);
-
-                       for (block = start; block <= end; block++) {
-                               instr.addr = block << onenand_chip.erase_shift;
-                               instr.len = 1 << onenand_chip.erase_shift;
-                               ret = onenand_erase(&onenand_mtd, &instr);
-                               if (ret) {
-                                       printf("erase failed %lu\n", block);
-                                       break;
-                               }
-                       }
-
-                       return 0;
+                       return do_erase(start, end);
                }
 
                if (strncmp(argv[1], "read", 4) == 0) {
@@ -134,15 +183,15 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, 
char *argv[])
                        ops.mode = MTD_OOB_PLACE;
 
 
-                       ofs = block << onenand_chip.erase_shift;
+                       ofs = onenand_get_addr(block);
                        if (page)
                                ofs += page << onenand_chip.page_shift;
 
                        if (!len) {
                                if (oob)
-                                       ops.ooblen = 64;
+                                       ops.ooblen = FLEXONENAND(this) ? 128 : 
64;
                                else
-                                       ops.len = 512;
+                                       ops.len = FLEXONENAND(this) ? 4096 : 
512;
                        }
 
                        if (oob) {
@@ -158,6 +207,39 @@ int do_onenand(cmd_tbl_t * cmdtp, int flag, int argc, char 
*argv[])
                        return 0;
                }
 
+               if (strncmp(argv[1], "setboundary", 11) == 0) {
+                       unsigned die = simple_strtoul(argv[2], NULL, 0);
+                       unsigned bdry = simple_strtoul(argv[3], NULL, 0);
+                       int lock = 0, old;
+
+                       if (!FLEXONENAND(this)) {
+                               printf("Flex-OneNAND not found.\n");
+                               return -1;
+                       }
+
+                       if (argc == 5 && strncmp(argv[4], "LOCK", 4) == 0)
+                               lock = 1;
+
+                       if (die >= this->dies) {
+                               printf("Invalid die index\n");
+                               return -1;
+                       }
+
+                       if (!(bdry % 2)) {
+                               printf("Attempt to set odd number of SLC 
blocks.\n");
+                               bdry += 1;
+                               printf("Setting boundary to %d\n", bdry);
+                       }
+
+                       old = this->boundary[die] + (die * this->density_mask);
+                       ret = flexonenand_set_boundary(mtd, die, bdry, lock);
+                       if (!ret) {
+                               int new = this->boundary[die] +
+                                               (die * this->density_mask);
+                               do_erase(min(old, new) + 1, max(old, new));
+                       }
+                       return 0;
+               }
                break;
        }
 
@@ -172,5 +254,7 @@ U_BOOT_CMD(
        "onenand write addr ofs len - write data at ofs with len from addr\n"
        "onenand erase saddr eaddr - erase block start addr to end addr\n"
        "onenand block[.oob] addr block [page] [len] - "
-               "read data with (block [, page]) to addr"
+               "read data with (block [, page]) to addr\n"
+       "onenand setboundary DIE BOUNDARY [LOCK] - "
+       "Change SLC boundary of Flex-OneNAND"
 );
diff --git a/common/env_onenand.c b/common/env_onenand.c
index 3c65b3e..b7d0bbf 100644
--- a/common/env_onenand.c
+++ b/common/env_onenand.c
@@ -58,11 +58,14 @@ uchar env_get_char_spec(int index)
 
 void env_relocate_spec(void)
 {
+       struct onenand_chip *this = &onenand_chip;
        unsigned long env_addr;
        int use_default = 0;
        size_t retlen;
 
        env_addr = CONFIG_ENV_ADDR;
+       if (FLEXONENAND(this))
+               env_addr <<= 1;
 
        /* Check OneNAND exist */
        if (onenand_mtd.writesize)
@@ -89,6 +92,7 @@ void env_relocate_spec(void)
 
 int saveenv(void)
 {
+       struct onenand_chip *this = &onenand_chip;
        unsigned long env_addr = CONFIG_ENV_ADDR;
        struct erase_info instr = {
                .callback       = NULL,
@@ -96,6 +100,12 @@ int saveenv(void)
        size_t retlen;
 
        instr.len = CONFIG_ENV_SIZE;
+       if (FLEXONENAND(this)) {
+               env_addr <<= 1;
+               instr.len = CONFIG_ENV_SIZE_FLEX;
+               instr.len <<= onenand_mtd.eraseregions[0].numblocks == 1 ?
+                               1 : 0;
+       }
        instr.addr = env_addr;
        if (onenand_erase(&onenand_mtd, &instr)) {
                printf("OneNAND: erase failed at 0x%08lx\n", env_addr);
diff --git a/drivers/mtd/onenand/onenand_base.c 
b/drivers/mtd/onenand/onenand_base.c
index 460e9c7..35d182e 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -45,6 +45,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 */
 };
 
 /**
@@ -83,9 +91,11 @@ static int onenand_block_address(int device, int block)
        if (device & ONENAND_DEVICE_IS_DDP) {
                /* Device Flash Core select, NAND Flash Block Address */
                int dfs = 0, density, mask;
+               int flex = device & DEVICE_IS_FLEXONENAND;
 
                density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-               mask = (1 << (density + 6));
+               density &= ONENAND_DEVICE_DENSITY_MASK;
+               mask = (1 << (density + (flex ? 4 : 6)));
 
                if (block & mask)
                        dfs = 1;
@@ -109,9 +119,11 @@ static int onenand_bufferram_address(int device, int block)
        if (device & ONENAND_DEVICE_IS_DDP) {
                /* Device BufferRAM Select */
                int dbs = 0, density, mask;
+               int flex = device & DEVICE_IS_FLEXONENAND;
 
                density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-               mask = (1 << (density + 6));
+               density &= ONENAND_DEVICE_DENSITY_MASK;
+               mask = (1 << (density + (flex ? 4 : 6)));
 
                if (block & mask)
                        dbs = 1;
@@ -169,6 +181,68 @@ static int onenand_buffer_address(int dataram1, int 
sectors, int count)
 }
 
 /**
+ * flexonenand_get_block- For given address return block number and if slc
+ * @param mtd          - MTD device structure
+ * @param addr         - Address for which block number is needed
+ * @return isblkslc    - Block is an SLC block or not
+ */
+static unsigned flexonenand_get_block(struct mtd_info *mtd, loff_t addr,
+                          unsigned *isblkslc)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned boundary, blk, die = 0;
+
+       if (unlikely(this->chipsize == 0))
+               /* We have been called by flexonenand_get_boundary.
+                * addr contains die index in this case.
+                */
+               return addr * this->density_mask;
+
+       if (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;
+
+       if (isblkslc)
+               *isblkslc = (blk <= boundary) ? 1 : 0;
+
+       blk += die ? this->density_mask : 0;
+       return blk;
+}
+
+inline unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr,
+                                       unsigned *isblkslc)
+{
+       struct onenand_chip *this = mtd->priv;
+
+       if (!FLEXONENAND(this))
+               return addr >> this->erase_shift;
+       return flexonenand_get_block(mtd, addr, isblkslc);
+}
+
+/**
+ * flexonenand_region - Get erase region of addr
+ * @param mtd          MTD device structure
+ * @param addr         address whose erase region needs to be identified
+ */
+inline int flexonenand_region(struct mtd_info *mtd, loff_t addr)
+{
+       int i;
+
+       for (i = 0; i < mtd->numeraseregions &&
+               addr >= mtd->eraseregions[i].offset; i++)
+               ;
+       i--;
+       return i;
+}
+
+/**
  * onenand_command - [DEFAULT] Send command to OneNAND device
  * @param mtd          MTD device structure
  * @param cmd          the command to be sent
@@ -182,10 +256,12 @@ 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;
+       unsigned slc = 0;
+
        /* Now we use page size operation */
-       int sectors = 4, count = 4;
+       int sectors = 0, count = 0;
 
        /* Address translation */
        switch (cmd) {
@@ -196,16 +272,19 @@ static int onenand_command(struct mtd_info *mtd, int cmd, 
loff_t addr,
                page = -1;
                break;
 
+       case FLEXONENAND_CMD_PI_ACCESS:
        case ONENAND_CMD_ERASE:
        case ONENAND_CMD_BUFFERRAM:
-               block = (int)(addr >> this->erase_shift);
+               block = onenand_get_block(mtd, addr, NULL);
                page = -1;
                break;
 
        default:
-               block = (int)(addr >> this->erase_shift);
+               block = onenand_get_block(mtd, addr, &slc);
                page = (int)(addr >> this->page_shift);
                page &= this->page_mask;
+               if (slc)
+                       page &= (this->page_mask >> 1);
                break;
        }
 
@@ -216,8 +295,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_MLC(this))
+                       ONENAND_SET_BUFFERRAM0(this);
+               else
+                       /* Switch to the next data buffer */
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
 
                return 0;
        }
@@ -227,6 +309,10 @@ static int onenand_command(struct mtd_info *mtd, int cmd, 
loff_t addr,
                value = onenand_block_address(this->device_id, block);
                this->write_word(value,
                                 this->base + ONENAND_REG_START_ADDRESS1);
+               /* Select DataRAM for DDP */
+               value = onenand_bufferram_address(this->device_id, block);
+               this->write_word(value,
+                       this->base + ONENAND_REG_START_ADDRESS2);
        }
 
        if (page != -1) {
@@ -235,8 +321,11 @@ static int onenand_command(struct mtd_info *mtd, int cmd, 
loff_t addr,
                switch (cmd) {
                case ONENAND_CMD_READ:
                case ONENAND_CMD_READOOB:
-                       dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
-                       readcmd = 1;
+                       if (ONENAND_IS_MLC(this))
+                               dataram = ONENAND_SET_BUFFERRAM0(this);
+                       else
+                               /* Switch to the next data buffer */
+                               dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
                        break;
 
                default:
@@ -253,14 +342,6 @@ static int onenand_command(struct mtd_info *mtd, int cmd, 
loff_t addr,
                value = onenand_buffer_address(dataram, sectors, count);
                this->write_word(value, this->base + ONENAND_REG_START_BUFFER);
 
-               if (readcmd) {
-                       /* Select DataRAM for DDP */
-                       value =
-                           onenand_bufferram_address(this->device_id, block);
-                       this->write_word(value,
-                                        this->base +
-                                        ONENAND_REG_START_ADDRESS2);
-               }
        }
 
        /* Interrupt clear */
@@ -272,6 +353,29 @@ static int onenand_command(struct mtd_info *mtd, int cmd, 
loff_t addr,
 }
 
 /**
+ * onenand_get_ecc - return ecc status
+ * @param mtd          MTD device structure
+ */
+static inline int onenand_get_ecc(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ecc[4];
+       int i, result = 0;
+
+       for (i = 0; i < 4; i++) {
+               ecc[i] = this->read_word(this->base + ((0xFF00 + i) << 1));
+               if (!FLEXONENAND(this))
+                       return ecc[i];
+               if (ecc[i] & FLEXONENAND_UNCORRECTABLE_ERROR) {
+                       result = ONENAND_ECC_2BIT_ALL;
+                       break;
+               }
+       }
+
+       return result;
+}
+
+/**
  * onenand_wait - [DEFAULT] wait until the command is done
  * @param mtd          MTD device structure
  * @param state                state to select the max. timeout value
@@ -295,6 +399,15 @@ 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) {
+               ecc = onenand_get_ecc(mtd);
+               if (ecc & ONENAND_ECC_2BIT_ALL) {
+                       MTDDEBUG(MTD_DEBUG_LEVEL0,
+                                 "onenand_wait: ECC error = 0x%04x\n", ecc);
+                       return -EBADMSG;
+               }
+       }
+
        if (ctrl & ONENAND_CTRL_ERROR) {
                MTDDEBUG (MTD_DEBUG_LEVEL0,
                          "onenand_wait: controller error = 0x%04x\n", ctrl);
@@ -307,15 +420,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;
 }
 
@@ -433,10 +537,13 @@ static int onenand_check_bufferram(struct mtd_info *mtd, 
loff_t addr)
        struct onenand_chip *this = mtd->priv;
        int block, page;
        int i;
+       unsigned slc = 0;
 
-       block = (int)(addr >> this->erase_shift);
+       block = onenand_get_block(mtd, addr, &slc);
        page = (int)(addr >> this->page_shift);
        page &= this->page_mask;
+       if (slc)
+               page &= (this->page_mask >> 1);
 
        i = ONENAND_CURRENT_BUFFERRAM(this);
 
@@ -462,10 +569,13 @@ static int onenand_update_bufferram(struct mtd_info *mtd, 
loff_t addr,
        struct onenand_chip *this = mtd->priv;
        int block, page;
        int i;
+       unsigned slc = 0;
 
-       block = (int)(addr >> this->erase_shift);
+       block = onenand_get_block(mtd, addr, &slc);
        page = (int)(addr >> this->page_shift);
        page &= this->page_mask;
+       if (FLEXONENAND(this) && slc)
+               page &= (this->page_mask >> 1);
 
        /* Invalidate BufferRAM */
        for (i = 0; i < MAX_BUFFERRAM; i++) {
@@ -573,6 +683,43 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, 
uint8_t *buf,
 }
 
 /**
+ * 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
+ *
+ * Issue recovery command when read fails on MLC area.
+ */
+static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned slc = 0;
+
+       /* Recovery is only for Flex-OneNAND */
+       if (!FLEXONENAND(this))
+               return status;
+
+       /* check if we failed due to uncorrectable error */
+       if (status != (-EBADMSG) && status != (ONENAND_BBT_READ_ECC_ERROR))
+               return status;
+
+       /* check if address lies in MLC region */
+       onenand_get_block(mtd, addr, &slc);
+       if (slc)
+               return status;
+
+       /* We are attempting to reread, so decrement stats.failed
+        * which was incremented by onenand_wait due to read failure
+        */
+       printk(KERN_DEBUG "Attempting to recover from uncorrectable read\n");
+       mtd->ecc_stats.failed--;
+
+       /* 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
  * @param from         offset to read from
@@ -616,12 +763,14 @@ 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) {
                if (!onenand_check_bufferram(mtd, from)) {
                        this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        ret = this->wait(mtd, FL_READING);
+                       ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret;
                        onenand_update_bufferram(mtd, from, !ret);
                        if (ret == -EBADMSG)
                                ret = 0;
@@ -636,7 +785,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_MLC(this) && read + thislen < len) {
                        this->command(mtd, ONENAND_CMD_READ, from, writesize);
                        /*
                         * Chip boundary handling in DDP
@@ -669,6 +818,15 @@ static int onenand_read_ops_nolock(struct mtd_info *mtd, 
loff_t from,
                        oobcolumn = 0;
                }
 
+               if (ONENAND_IS_MLC(this) && (read + thislen < len)) {
+                       this->command(mtd, ONENAND_CMD_READ, from, writesize);
+                       ret = this->wait(mtd, FL_READING);
+                       ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret;
+                       onenand_update_bufferram(mtd, from, !ret);
+                       if (ret == -EBADMSG)
+                               ret = 0;
+               }
+
                /* See if we are done */
                read += thislen;
                if (read == len)
@@ -676,16 +834,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_MLC(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_MLC(this)) {
+                       /* Now wait for load */
+                       ret = this->wait(mtd, FL_READING);
+                       onenand_update_bufferram(mtd, from, !ret);
+                       if (ret == -EBADMSG)
+                               ret = 0;
+               }
        }
 
        /*
@@ -722,7 +883,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, 
loff_t from,
        size_t len = ops->ooblen;
        mtd_oob_mode_t mode = ops->mode;
        u_char *buf = ops->oobbuf;
-       int ret = 0;
+       int ret = 0, readcmd;
 
        from += ops->ooboffs;
 
@@ -755,15 +916,18 @@ 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;
+
        while (read < len) {
                thislen = oobsize - column;
                thislen = min_t(int, thislen, len);
 
-               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);
+               ret = ret ? onenand_recover_lsb(mtd, from, ret) : ret;
                if (ret && ret != -EBADMSG) {
                        printk(KERN_ERR "onenand_read_oob_nolock: read failed = 
0x%x\n", ret);
                        break;
@@ -886,22 +1050,26 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int 
state)
        interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);
        ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);
 
-       /* Initial bad block case: 0x2400 or 0x0400 */
-       if (ctrl & ONENAND_CTRL_ERROR) {
-               printk(KERN_DEBUG "onenand_bbt_wait: controller error = 
0x%04x\n", ctrl);
-               return ONENAND_BBT_READ_ERROR;
-       }
-
        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_get_ecc(mtd);
+               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);
                return ONENAND_BBT_READ_FATAL_ERROR;
        }
 
+       /* Initial bad block case: 0x2400 or 0x0400 */
+       if (ctrl & ONENAND_CTRL_ERROR) {
+               printk(KERN_DEBUG "onenand_bbt_wait: controller error"
+                               " = 0x%04x\n", ctrl);
+               return ONENAND_BBT_READ_ERROR;
+       }
+
        return 0;
 }
 
@@ -918,7 +1086,7 @@ 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;
 
@@ -926,6 +1094,8 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n",
                (unsigned int) from, len);
 
+       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
        /* Initialize return value */
        ops->oobretlen = 0;
 
@@ -945,7 +1115,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                thislen = mtd->oobsize - column;
                thislen = min_t(int, thislen, len);
 
-               this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);
+               this->command(mtd, readcmd, from, mtd->oobsize);
 
                onenand_update_bufferram(mtd, from, 0);
 
@@ -987,9 +1157,11 @@ 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;
+
+       readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
 
-       this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+       this->command(mtd, readcmd, to, mtd->oobsize);
        onenand_update_bufferram(mtd, to, 0);
        status = this->wait(mtd, FL_READING);
        if (status)
@@ -1236,7 +1408,7 @@ 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;
@@ -1280,6 +1452,8 @@ 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;
+
        /* Loop until all data write */
        while (written < len) {
                int thislen = min_t(int, oobsize, len - written);
@@ -1295,7 +1469,14 @@ 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);
 
-               this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize);
+               if (ONENAND_IS_MLC(this)) {
+                       /* Set main area of DataRAM to 0xff*/
+                       memset(this->page_buf, 0xff, mtd->writesize);
+                       this->write_bufferram(mtd, 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)) {
@@ -1424,19 +1605,27 @@ int onenand_erase(struct mtd_info *mtd, struct 
erase_info *instr)
        unsigned int block_size;
        loff_t addr;
        int len;
-       int ret = 0;
+       int ret = 0, i = 0;
 
        MTDDEBUG (MTD_DEBUG_LEVEL3,
                 "onenand_erase: start = 0x%08x, len = %i\n",
                 (unsigned int)instr->addr, (unsigned int)ins tr->len);
 
-       block_size = (1 << this->erase_shift);
+       if (mtd->numeraseregions > 1) {
+               i = flexonenand_region(mtd, instr->addr);
+               block_size = mtd->eraseregions[i].erasesize;
+       } else
+               block_size = mtd->erasesize;
 
        /* 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;
+               /* We come here if boundary is even value. Just skip for now.*/
+               if (!FLEXONENAND(this) ||
+                       (instr->addr & ((block_size >> 1) - 1))) {
+                       MTDDEBUG(MTD_DEBUG_LEVEL0, "onenand_erase:"
+                                       " Unaligned address\n");
+                       return -EINVAL;
+               }
        }
 
        /* Length must align on block boundary */
@@ -1466,7 +1655,13 @@ int onenand_erase(struct mtd_info *mtd, struct 
erase_info *instr)
 
        while (len) {
 
-               /* TODO Check badblock */
+               /* Check if we have a bad block, we do not erase bad blocks */
+               if (onenand_block_isbad_nolock(mtd, addr, 0)) {
+                       printk(KERN_WARNING "onenand_erase: attempt to erase"
+                           "bad block at addr 0x%08x\n", (unsigned int) addr);
+                       instr->state = MTD_ERASE_FAILED;
+                       goto erase_exit;
+               }
 
                this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);
 
@@ -1481,7 +1676,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));
+                                       onenand_get_block(mtd, addr, NULL));
                        instr->state = MTD_ERASE_FAILED;
                        instr->fail_addr = addr;
                        goto erase_exit;
@@ -1489,6 +1684,12 @@ int onenand_erase(struct mtd_info *mtd, struct 
erase_info *instr)
 
                len -= block_size;
                addr += block_size;
+               if (mtd->numeraseregions > 1) {
+                       if ((i < (mtd->numeraseregions - 1)) &&
+                           (addr == mtd->eraseregions[i + 1].offset))
+                               i++;
+                       block_size = mtd->eraseregions[i].erasesize;
+               }
        }
 
        instr->state = MTD_ERASE_DONE;
@@ -1539,7 +1740,7 @@ int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
                return -EINVAL;
 
        onenand_get_device(mtd, FL_READING);
-       ret = onenand_block_isbad_nolock(mtd,ofs, 0);
+       ret = onenand_block_isbad_nolock(mtd, ofs, 0);
        onenand_release_device(mtd);
        return ret;
 }
@@ -1612,8 +1813,8 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, 
size_t len)
        struct onenand_chip *this = mtd->priv;
        int start, end, block, value, status;
 
-       start = ofs >> this->erase_shift;
-       end = len >> this->erase_shift;
+       start = onenand_get_block(mtd, ofs, NULL);
+       end = onenand_get_block(mtd, ofs + len - 1, NULL);
 
        /* Continuous lock scheme */
        if (this->options & ONENAND_CONT_LOCK) {
@@ -1621,7 +1822,7 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, 
size_t len)
                this->write_word(start,
                                 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
                /* Set end block address */
-               this->write_word(end - 1,
+               this->write_word(end,
                                 this->base + ONENAND_REG_END_BLOCK_ADDRESS);
                /* Write unlock command */
                this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
@@ -1643,7 +1844,17 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, 
size_t len)
        }
 
        /* Block lock scheme */
-       for (block = start; block < end; block++) {
+       for (block = start; block < end + 1; block++) {
+               /* Set block address */
+               value = onenand_block_address(this->device_id, block);
+               this->write_word(value,
+                       this->base + ONENAND_REG_START_ADDRESS1);
+
+               /* Select DataRAM for DDP */
+               value = onenand_bufferram_address(this->device_id, block);
+               this->write_word(value,
+                       this->base + ONENAND_REG_START_ADDRESS2);
+
                /* Set start block address */
                this->write_word(block,
                                 this->base + ONENAND_REG_START_BLOCK_ADDRESS);
@@ -1681,15 +1892,18 @@ int onenand_unlock(struct mtd_info *mtd, loff_t ofs, 
size_t len)
  */
 char * onenand_print_device_info(int device)
 {
-       int vcc, demuxed, ddp, density;
+       int vcc, demuxed, ddp, density, flexonenand;
        char *dev_info = malloc(80);
 
        vcc = device & ONENAND_DEVICE_VCC_MASK;
        demuxed = device & ONENAND_DEVICE_IS_DEMUX;
        ddp = device & ONENAND_DEVICE_IS_DDP;
        density = device >> ONENAND_DEVICE_DENSITY_SHIFT;
-       sprintf(dev_info, "%sOneNAND%s %dMB %sV 16-bit (0x%02x)",
-              demuxed ? "" : "Muxed ",
+       density &= ONENAND_DEVICE_DENSITY_MASK;
+       flexonenand = device & DEVICE_IS_FLEXONENAND;
+       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);
 
@@ -1725,6 +1939,174 @@ static int onenand_check_maf(int manuf)
 }
 
 /**
+* flexonenand_get_boundary - Reads the SLC boundary
+* @param onenand_info          onenand info structure
+**/
+static int flexonenand_get_boundary(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       unsigned die, bdry;
+       int ret, syscfg, unlocked;
+
+       /* 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, ONENAND_CMD_READ, die, 0);
+               ret = this->wait(mtd, FL_READING);
+
+               bdry = this->read_word(this->base + ONENAND_DATARAM);
+               unlocked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT;
+               unlocked = (unlocked == 0x3) ? 1 : 0;
+               this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
+
+               this->command(mtd, ONENAND_CMD_RESET, 0, 0);
+               ret = this->wait(mtd, FL_RESETING);
+
+               printk(KERN_INFO "Die %d boundary: %d%s\n", die,
+                       this->boundary[die],
+                       unlocked ? "(Unlocked)" : "(Locked)");
+       }
+
+       /* Enable ECC */
+       this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+       return 0;
+}
+
+/**
+ * get_flexonenand_size - Fill up fields in onenand_chip
+ *                          boundary[], diesize[], chipsize
+ * @param mtd             - MTD device structure
+ */
+static void get_flexonenand_size(struct mtd_info *mtd)
+{
+       struct onenand_chip *this = mtd->priv;
+       int die, ofs, i, eraseshift;
+       unsigned blksperdie = 1024;
+       unsigned maxbdry = blksperdie - 1;
+
+       eraseshift = this->erase_shift - 1;
+
+       this->chipsize = 0;
+       mtd->numeraseregions = this->dies << 1;
+
+       /* This fills up the device boundary */
+       flexonenand_get_boundary(mtd);
+       die = 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;
+       }
+
+       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%08x, erasesize: 0x%05x,"
+                       " numblocks: %04u]\n", mtd->eraseregions[i].offset,
+                       mtd->eraseregions[i].erasesize,
+                       mtd->eraseregions[i].numblocks);
+
+       eraseshift = this->erase_shift;
+       for (die = 0, mtd->size = 0; die < this->dies; die++) {
+               this->diesize[die] = (blksperdie << eraseshift);
+               this->diesize[die] -= (this->boundary[die] + 1)
+                                       << (eraseshift - 1);
+               mtd->size += this->diesize[die];
+       }
+       /* this->chipsize represents maximum possible chip size */
+       this->chipsize = (blksperdie << eraseshift) << (this->dies - 1);
+}
+
+/**
+* flexonenand_set_boundary - Writes the SLC boundary
+* @param onenand_info          onenand info structure
+**/
+int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die,
+                                       int boundary, int lock)
+{
+       struct onenand_chip *this = mtd->priv;
+       int ret, density, blksperdie;
+       unsigned addr;
+
+       density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+       density &= ONENAND_DEVICE_DENSITY_MASK;
+
+       blksperdie = ((16 << density) << 20) >> this->erase_shift;
+       blksperdie >>= (this->device_id & ONENAND_DEVICE_IS_DDP) ? 1 : 0;
+
+       addr = die ? this->diesize[0] : 0;
+
+       if (this->boundary[die] == boundary)
+               return -1;
+
+       printk(KERN_INFO "Changing boundary: %d%s\n", boundary, lock ?
+                       "(Locked)" : "(Unlocked)");
+       if (boundary >= blksperdie) {
+               printk(KERN_ERR "Invalid boundary value.\n");
+               return -1;
+       }
+
+       boundary &= FLEXONENAND_PI_MASK;
+       boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT);
+
+       this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, addr, 0);
+       this->wait(mtd, FL_SYNCING);
+
+       this->command(mtd, ONENAND_CMD_ERASE, addr, 0);
+       this->wait(mtd, FL_ERASING);
+
+       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(KERN_ERR "Failed PI write for Die %d\n", die);
+               goto out;
+       }
+
+       this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, addr, 0);
+       ret = this->wait(mtd, FL_WRITING);
+       if (ret)
+               printk(KERN_ERR "Failed PI update for Die %d\n", die);
+       else
+               printk(KERN_INFO "Done\n");
+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*/
+               get_flexonenand_size(mtd);
+       return ret;
+}
+
+/**
  * onenand_probe - [OneNAND Interface] Probe the OneNAND device
  * @param mtd          MTD device structure
  *
@@ -1758,31 +2140,48 @@ 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);
+       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;
 
-       /* FIXME : Current OneNAND MTD doesn't support Flex-OneNAND */
-       if (dev_id & (1 << 9)) {
-               printk("Not yet support Flex-OneNAND\n");
-               return -ENXIO;
-       }
-
        /* Flash device information */
        mtd->name = onenand_print_device_info(dev_id);
        this->device_id = dev_id;
 
        density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
-       this->chipsize = (16 << density) << 20;
+       density &= ONENAND_DEVICE_DENSITY_MASK;
+       if (FLEXONENAND(this)) {
+               this->dies = (dev_id & ONENAND_DEVICE_IS_DDP) ? 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;
+       }
+       this->chipsize = FLEXONENAND(this) ? 0 : (16 << density) << 20;
+       /* Set density mask. it is used for DDP */
+       if (dev_id & ONENAND_DEVICE_IS_DDP)
+               this->density_mask = (1 << (density + (FLEXONENAND(this) ?
+                                       4 : 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_MLC(this))
+               mtd->writesize <<= 1;
+
        mtd->oobsize = mtd->writesize >> 5;
        /* Pagers per block is always 64 in OneNAND */
        mtd->erasesize = mtd->writesize << 6;
+       /* Flex-OneNAND always has 128 pages per block */
+       mtd->erasesize <<= FLEXONENAND(this) ? 1 : 0;
 
        this->erase_shift = ffs(mtd->erasesize) - 1;
        this->page_shift = ffs(mtd->writesize) - 1;
@@ -1793,7 +2192,10 @@ static int onenand_probe(struct mtd_info *mtd)
 
        /* REVIST: Multichip handling */
 
-       mtd->size = this->chipsize;
+       if (FLEXONENAND(this))
+               get_flexonenand_size(mtd);
+       else
+               mtd->size = this->chipsize;
 
        /* Version ID */
        version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
@@ -1818,6 +2220,9 @@ static int onenand_probe(struct mtd_info *mtd)
        mtd->block_isbad = onenand_block_isbad;
        mtd->block_markbad = onenand_block_markbad;
 
+       if (FLEXONENAND(this))
+               this->options &= ~ONENAND_CONT_LOCK;
+
        return 0;
 }
 
diff --git a/drivers/mtd/onenand/onenand_bbt.c 
b/drivers/mtd/onenand/onenand_bbt.c
index f6092b9..e46fd29 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -66,6 +66,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
        struct bbm_info *bbm = this->bbm;
        int i, j, numblocks, len, scanlen;
        int startblock;
+       unsigned slc;
        loff_t from;
        size_t readlen, ooblen;
        struct mtd_oob_ops ops;
@@ -82,7 +83,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
        /* Note that numblocks is 2 * (real numblocks) here;
         * see i += 2 below as it makses shifting and masking less painful
         */
-       numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+       numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1);
        startblock = 0;
        from = 0;
 
@@ -115,7 +116,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
                        }
                }
                i += 2;
-               from += (1 << bbm->bbt_erase_shift);
+               if (FLEXONENAND(this)) {
+                       onenand_get_block(mtd, from, &slc);
+                       from += (1 << bbm->bbt_erase_shift) >> 1;
+                       if (!slc)
+                               from += (1 << bbm->bbt_erase_shift) >> 1;
+               } else
+                       from += (1 << bbm->bbt_erase_shift);
        }
 
        return 0;
@@ -152,7 +159,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t 
offs, int allowbbt)
        uint8_t res;
 
        /* Get block number * 2 */
-       block = (int)(offs >> (bbm->bbt_erase_shift - 1));
+       block = (int) (onenand_get_block(mtd, offs, NULL) << 1);
        res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 
        MTDDEBUG (MTD_DEBUG_LEVEL2,
@@ -191,7 +198,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct 
nand_bbt_descr *bd)
        struct bbm_info *bbm = this->bbm;
        int len, ret = 0;
 
-       len = mtd->size >> (this->erase_shift + 2);
+       len = this->chipsize >> (this->erase_shift + 2);
        /* Allocate memory (2bit per block) */
        bbm->bbt = malloc(len);
        if (!bbm->bbt) {
diff --git a/drivers/mtd/onenand/onenand_uboot.c 
b/drivers/mtd/onenand/onenand_uboot.c
index 08082f3..419db34 100644
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -31,6 +31,8 @@ void onenand_init(void)
 
        onenand_scan(&onenand_mtd, 1);
 
+       if (onenand_chip.device_id & DEVICE_IS_FLEXONENAND)
+               puts("Flex-");
        puts("OneNAND: ");
-       print_size(onenand_mtd.size, "\n");
+       print_size(onenand_chip.chipsize, "\n");
 }
diff --git a/include/configs/apollon.h b/include/configs/apollon.h
index d71ed44..bad86d1 100644
--- a/include/configs/apollon.h
+++ b/include/configs/apollon.h
@@ -73,6 +73,7 @@
  * Size of malloc() pool
  */
 #define        CONFIG_ENV_SIZE SZ_128K /* Total Size of Environment Sector */
+#define        CONFIG_ENV_SIZE_FLEX SZ_256K    /* Can change to 512K */
 #define        CONFIG_SYS_MALLOC_LEN   (CONFIG_ENV_SIZE + SZ_128K)
 #define        CONFIG_SYS_GBL_DATA_SIZE        128     /* bytes reserved for 
initial data */
 
@@ -227,5 +228,4 @@
 #define        CONFIG_SYS_ONENAND_BASE 0x00000000
 #define        CONFIG_ENV_IS_IN_ONENAND        1
 #define CONFIG_ENV_ADDR                0x00020000
-
 #endif /* __CONFIG_H */
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 420eb14..6d367ea 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -20,6 +20,7 @@
 #include <linux/mtd/compat.h>
 #include <linux/mtd/bbm.h>
 
+#define MAX_DIES               2
 #define MAX_BUFFERRAM          2
 #define MAX_ONENAND_PAGESIZE   (2048 + 64)
 
@@ -43,6 +44,9 @@ struct onenand_bufferram {
 /**
  * struct onenand_chip - OneNAND Private Flash Chip Data
  * @param base         [BOARDSPECIFIC] address to access OneNAND
+ * @dies:               [INTERN][FLEXONENAND] number of dies on chip
+ * @boundary:           [INTERN][FLEXONENAND] Boundary of the dies
+ * @diesize:            [INTERN][FLEXONENAND] Size of the dies
  * @param chipsize     [INTERN] the size of one chip for multichip arrays
  * @param device_id    [INTERN] device ID
  * @param verstion_id  [INTERN] version ID
@@ -67,8 +71,13 @@ struct onenand_bufferram {
  */
 struct onenand_chip {
        void __iomem *base;
+       unsigned int dies;
+       unsigned int boundary[MAX_DIES];
+       unsigned int diesize[MAX_DIES];
        unsigned int chipsize;
        unsigned int device_id;
+       unsigned int technology;
+       unsigned int density_mask;
        unsigned int options;
 
        unsigned int erase_shift;
@@ -116,6 +125,8 @@ struct onenand_chip {
 #define ONENAND_SET_BUFFERRAM0(this)           (this->bufferram_index = 0)
 #define ONENAND_SET_BUFFERRAM1(this)           (this->bufferram_index = 1)
 
+#define FLEXONENAND(this)      (this->device_id & DEVICE_IS_FLEXONENAND)
+#define ONENAND_IS_MLC(this)   (this->technology & ONENAND_TECHNOLOGY_IS_MLC)
 #define ONENAND_IS_DDP(this)                                           \
        (this->device_id & ONENAND_DEVICE_IS_DDP)
 
@@ -147,4 +158,9 @@ struct onenand_manufacturers {
 int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
                        struct mtd_oob_ops *ops);
 
+unsigned onenand_get_block(struct mtd_info *mtd, loff_t addr,
+                       unsigned *isblkslc);
+int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die,
+                               int bdry, int lock);
+int flexonenand_region(struct mtd_info *mtd, loff_t addr);
 #endif                         /* __LINUX_MTD_ONENAND_H */
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
index 6a8aa28..0108ad9 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -67,6 +67,10 @@
 /*
  * Device ID Register F001h (R)
  */
+#define DEVICE_IS_FLEXONENAND          (1 << 9)
+#define FLEXONENAND_PI_MASK            (0x3ff)
+#define FLEXONENAND_PI_UNLOCK_SHIFT    (14)
+#define ONENAND_DEVICE_DENSITY_MASK    (0xf)
 #define ONENAND_DEVICE_DENSITY_SHIFT   (4)
 #define ONENAND_DEVICE_IS_DDP          (1 << 3)
 #define ONENAND_DEVICE_IS_DEMUX                (1 << 2)
@@ -80,6 +84,11 @@
 #define ONENAND_VERSION_PROCESS_SHIFT  (8)
 
 /*
+ * Technology Register F006h (R)
+ */
+#define ONENAND_TECHNOLOGY_IS_MLC      (1 << 0)
+
+/*
  * Start Address 1 F100h (R/W)
  */
 #define ONENAND_DDP_SHIFT              (15)
@@ -89,7 +98,7 @@
 /*
  * Start Address 8 F107h (R/W)
  */
-#define ONENAND_FPA_MASK               (0x3f)
+#define ONENAND_FPA_MASK               (0x7f)
 #define ONENAND_FPA_SHIFT              (2)
 #define ONENAND_FSA_MASK               (0x03)
 
@@ -101,7 +110,7 @@
 #define ONENAND_BSA_BOOTRAM            (0 << 2)
 #define ONENAND_BSA_DATARAM0           (2 << 2)
 #define ONENAND_BSA_DATARAM1           (3 << 2)
-#define ONENAND_BSC_MASK               (0x03)
+#define ONENAND_BSC_MASK               (0x07)
 
 /*
  * Command Register F220h (R/W)
@@ -116,6 +125,10 @@
 #define ONENAND_CMD_ERASE              (0x94)
 #define ONENAND_CMD_RESET              (0xF0)
 #define ONENAND_CMD_READID             (0x90)
+#define FLEXONENAND_CMD_RESET          (0xF3)
+#define FLEXONENAND_CMD_PI_UPDATE      (0x05)
+#define FLEXONENAND_CMD_PI_ACCESS      (0x66)
+#define FLEXONENAND_CMD_RECOVER_LSB    (0x05)
 
 /* NOTE: Those are not *REAL* commands */
 #define ONENAND_CMD_BUFFERRAM          (0x1978)
@@ -179,5 +192,6 @@
 #define ONENAND_ECC_1BIT               (1 << 0)
 #define ONENAND_ECC_2BIT               (1 << 1)
 #define ONENAND_ECC_2BIT_ALL           (0xAAAA)
+#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010)
 
 #endif                         /* __ONENAND_REG_H */


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to