[U-Boot] [PATCH v2 1/4] Flex-OneNAND driver

Amul Kumar Saha amul.saha at samsung.com
Wed Mar 18 07:24:27 CET 2009


Hi Scott,

>Do you see this going into Linux soon?  Unless there's some particular
>obstacle to that (other than the code's readiness) I think I'd rather
>wait for it to be accepted there, with (hopefully) more eyes looking over
>it, and then bring the result into u-boot.

All the review comments are closed up to date;
it will hopefully get merged there.

>> + boundary = this->boundary[die];
>> + ofs += block << (this->erase_shift - 1);
>> + if (block > (boundary + 1))
>> + ofs += (block - boundary - 1) << (this->erase_shift - 1);
>> + return ofs;
>> +}
> 
> In the Linux patch here:
> http://groups.google.com/group/fa.linux.kernel/msg/8edda44803373f33?hl=en&&q=flex+onenand
> 
> you deal with 64-bit offsets, but that's missing above.
> 

Accepted and resolved.

>> +inline loff_t onenand_addr(struct onenand_chip *this, int block)
>> +{
>> + if (!FLEXONENAND(this))
>> + return block << this->erase_shift;
>> + return flexonenand_addr(this, block);
>> +}
> 
> Let the compiler figure out what is inline.  Can this function be static?
> 

Removed the inline. Converted to static.

>> + 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(addr & (block_size - 1))) {
>> + MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
>> + return -EINVAL;
>> + }
>>  }
> 
> It might be worthwhile to factor out the various if/else bits into some
> sort of "ops" struct, so that the differences in chip type are
> concentrated in one spot (and it's easier to add a third type if it comes
> along).
> 

This is a good idea; we will consider it in later versions.

>> + * 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, ofs, i, eraseshift, density;
>> + int blksperdie, maxbdry;
>> +
>> + density = onenand_get_density(this->device_id);
>> + blksperdie = ((16 << density) << 20) >> (this->erase_shift);
> 
> Can this overflow?  Might be better as:
> 
> blksperdie = 16 << (density + 20 - this->erase_shift);
> 

Accepted and resolved.

>> + 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;
>> + }
> 
> Can the above be simplified, or at least documented?  I'm having a hard
> time following it.
> 

Documented the above section in function header.


This patch adds support for MLC OneNAND and Flex-OneNAND devices.

Thank You,
Amul Kumar Saha

Signed-off-by: Rohit Hagargundgi <h.rohit at samsung.com>
Signed-off-by: Amul Kumar Saha <amul.saha at samsung.com>
---
 drivers/mtd/onenand/onenand_base.c  |  749 ++++++++++++++++++++++++++++++-----
 drivers/mtd/onenand/onenand_bbt.c   |   14 +-
 drivers/mtd/onenand/onenand_uboot.c |    4 +-
 include/linux/mtd/onenand.h         |   16 +-
 include/linux/mtd/onenand_regs.h    |   18 +-
 include/onenand_uboot.h             |    8 +
 6 files changed, 709 insertions(+), 100 deletions(-)

diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index d482437..8c621c7 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -37,6 +37,27 @@ static inline void *memcpy_16(void *dst, const void *src, unsigned int len)
 }
 
 /**
+ *  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
  */
 static struct nand_ecclayout onenand_oob_64 = {
@@ -74,6 +95,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 */
 };
 
 /**
@@ -180,6 +209,84 @@ static int onenand_buffer_address(int dataram1, int sectors, int count)
 }
 
 /**
+ * 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;
+}
+
+inline 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;
+}
+
+static 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 +312,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 +328,27 @@ 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 +360,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;
  }
@@ -252,7 +375,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 +385,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_MLC(this))
+    dataram = ONENAND_SET_BUFFERRAM0(this);
+   else
+    dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
+
    break;
 
   default:
@@ -292,6 +419,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr,
 }
 
 /**
+ * onenand_read_ecc - return ecc status
+ * @param this  onenand chip structure
+ */
+static inline 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
  * @param state  state to select the max. timeout value
@@ -305,7 +454,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 +464,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 +481,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;
 }
@@ -499,7 +648,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);
  }
@@ -632,6 +781,44 @@ 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
+ *
+ * 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 (status != -EBADMSG && 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
  * @param from  offset to read from
@@ -673,6 +860,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 +868,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 +884,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->main_buf = buf + thislen;
    this->command(mtd, ONENAND_CMD_READ, from, writesize);
    /*
@@ -728,6 +918,16 @@ 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);
+   if (unlikely(ret))
+    ret = onenand_recover_lsb(mtd, from, ret);
+   onenand_update_bufferram(mtd, from, !ret);
+   if (ret == -EBADMSG)
+    ret = 0;
+  }
+
   /* See if we are done */
   read += thislen;
   if (read == len)
@@ -735,16 +935,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;
+  }
  }
 
  /*
@@ -781,7 +984,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;
 
@@ -812,16 +1015,21 @@ 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->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;
@@ -945,9 +1153,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 +1187,14 @@ 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_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
+
  /* Initialize return value */
  ops->oobretlen = 0;
 
@@ -1002,11 +1215,14 @@ 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;
 
@@ -1044,9 +1260,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;
 
- this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);
+ readcmd = ONENAND_IS_MLC(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)
@@ -1291,7 +1509,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;
@@ -1333,6 +1551,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);
@@ -1348,7 +1568,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,
    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_MLC(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)) {
@@ -1475,34 +1702,50 @@ 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;
+ loff_t addr = instr->addr;
+ loff_t len = (loff_t)(instr->len);
+ int ret = 0, i;
+ struct mtd_erase_region_info *region = NULL;
+ loff_t region_end = 0;
 
- MTDDEBUG (MTD_DEBUG_LEVEL3,
-   "onenand_erase: start = 0x%08x, len = %i\n",
-   (unsigned int)instr->addr, (unsigned int)instr->len);
+ MTDDEBUG (MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n",
+   (unsigned int) addr, len);
 
- 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");
+ /* Do not allow erase past end of device */
+ if (unlikely((len + addr) > mtd->size)) {
+  MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n");
   return -EINVAL;
  }
 
- /* Length must align on block boundary */
- if (unlikely(instr->len & (block_size - 1))) {
-  MTDDEBUG (MTD_DEBUG_LEVEL0,
-    "onenand_erase: Length not block aligned\n");
-  return -EINVAL;
+ 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(addr & (block_size - 1))) {
+   MTDDEBUG (MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n");
+   return -EINVAL;
+  }
  }
 
- /* Do not allow erase past end of device */
- if (unlikely((instr->len + instr->addr) > mtd->size)) {
+ /* Length must align on block boundary */
+ if (unlikely(len & (block_size - 1))) {
   MTDDEBUG (MTD_DEBUG_LEVEL0,
-    "onenand_erase: Erase past end of device\n");
+    "onenand_erase: Length not block aligned\n");
   return -EINVAL;
  }
 
@@ -1512,9 +1755,6 @@ int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
  onenand_get_device(mtd, FL_ERASING);
 
  /* Loop throught the pages */
- len = instr->len;
- addr = instr->addr;
-
  instr->state = MTD_ERASING;
 
  while (len) {
@@ -1541,14 +1781,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 +1790,21 @@ 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(KERN_ERR "onenand_erase: Unaligned address\n");
+    goto erase_exit;
+   }
+  }
  }
 
  instr->state = MTD_ERASE_DONE;
@@ -1634,7 +1882,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);
 
@@ -1682,8 +1930,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
  int start, end, block, value, status;
  int wp_status_mask;
 
- start = ofs >> this->erase_shift;
- end = len >> this->erase_shift;
+ start = onenand_block(this, ofs);
+ end = onenand_block(this, ofs + len);
 
  if (cmd == ONENAND_CMD_LOCK)
   wp_status_mask = ONENAND_WP_LS;
@@ -1718,7 +1966,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 +2079,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 +2095,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;
@@ -1906,6 +2152,14 @@ static void onenand_check_features(struct mtd_info *mtd)
   break;
  }
 
+ if (ONENAND_IS_MLC(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)
@@ -1922,16 +2176,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);
 
@@ -1957,7 +2213,7 @@ static int onenand_check_maf(int manuf)
  char *name;
  int i;
 
- for (i = 0; size; i++)
+ for (i = 0; i < size; i++)
   if (manuf == onenand_manuf_ids[i].id)
    break;
 
@@ -1974,6 +2230,289 @@ static int onenand_check_maf(int manuf)
 }
 
 /**
+* 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 ret, 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);
+  ret = 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);
+  ret = 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
+ *
+ * Possible configurable scenarios:-
+ *
+ * S: SLC
+ * M: MLC
+ * Case 1: eraseregion = 4 boundary[die=0]!=maxbdry boundary[die=1]!=maxbdry
+ * Case 2: eraseregion = 3 boundary[die=0]!=maxbdry boundary[die=1]==maxbdry
+ * Case 3: eraseregion = 2 boundary[die=0]==maxbdry boundary[die=1]!=maxbdry
+ * Case 4: eraseregion = 1 boundary[die=0]==maxbdry boundary[die=1]==maxbdry
+ *
+ *  1     2
+ * ___ __   ___ __
+ * |S| |S|   |S| |S|
+ * -- --   -- | |
+ * |M|  |M|   |M| | |
+ * -- --   -- --
+ *
+ * 3     4
+ * ___ __   ___ __
+ * |S| |S|   |S| |S|
+ * | | --   | | | |
+ * | |  |M|   | | | |
+ *  -- --   -- --
+ */
+static void flexonenand_get_size(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ int die, ofs, i, eraseshift, density;
+ int blksperdie, maxbdry;
+
+ density = onenand_get_density(this->device_id);
+ blksperdie = 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 = ofs = 0;
+ i = -1;
+
+ for (; die < this->dies; die++) {
+ /*
+  * Case 1 OR 2
+  */
+  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++;
+  }
+  /*
+   * Case 3 OR 4
+   */
+  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%08x, 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_OOB_PLACE,
+  .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(KERN_ERR "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(KERN_ERR "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(KERN_ERR "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_probe - [OneNAND Interface] Probe the OneNAND device
  * @param mtd  MTD device structure
  *
@@ -2016,48 +2555,69 @@ static int onenand_probe(struct mtd_info *mtd)
  maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
  dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
  ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
+ this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY);
 
  /* Check OneNAND device */
  if (maf_id != bram_maf_id || dev_id != bram_dev_id)
   return -ENXIO;
 
- /* 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, ver_id);
  this->device_id = dev_id;
  this->version_id = ver_id;
 
  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_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 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;
+ if (FLEXONENAND(this))
+  flexonenand_get_size(mtd);
+ else
+  mtd->size = this->chipsize;
 
  /* Check OneNAND features */
  onenand_check_features(mtd);
@@ -2151,6 +2711,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;
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
index d538f95..1354877 100644
--- a/drivers/mtd/onenand/onenand_bbt.c
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -69,6 +69,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
  loff_t from;
  size_t readlen, ooblen;
  struct mtd_oob_ops ops;
+ int rgn;
 
  printk(KERN_INFO "Scanning device for bad blocks\n");
 
@@ -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,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t * buf,
    }
   }
   i += 2;
-  from += (1 << bbm->bbt_erase_shift);
+
+  if (FLEXONENAND(this)) {
+   rgn = flexonenand_region(mtd, from);
+   from += mtd->eraseregions[rgn].erasesize;
+  } else
+   from += (1 << bbm->bbt_erase_shift);
  }
 
  return 0;
@@ -152,7 +158,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_block(this, offs) << 1);
  res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
 
  MTDDEBUG (MTD_DEBUG_LEVEL2,
@@ -191,7 +197,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 4541b22..90dad46 100644
--- a/drivers/mtd/onenand/onenand_uboot.c
+++ b/drivers/mtd/onenand/onenand_uboot.c
@@ -39,6 +39,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/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
index 2597e34..6c2f718 100644
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -20,8 +20,9 @@
 #include <linux/mtd/compat.h>
 #include <linux/mtd/bbm.h>
 
+#define MAX_DIES  2
 #define MAX_BUFFERRAM  2
-#define MAX_ONENAND_PAGESIZE (2048 + 64)
+#define MAX_ONENAND_PAGESIZE (4096 + 128)
 
 /* Scan and identify a OneNAND device */
 extern int onenand_scan (struct mtd_info *mtd, int max_chips);
@@ -39,9 +40,14 @@ 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
+ * @technology  [INTERN] describes the internal NAND array technology such as SLC or MLC.
+ * @density_mask: [INTERN] chip density, used for DDP devices
  * @param options [BOARDSPECIFIC] various chip options. They can partly be set to inform onenand_scan about
  * @param erase_shift [INTERN] number of address bits in a block
  * @param page_shift [INTERN] number of address bits in a page
@@ -64,9 +70,13 @@ struct onenand_bufferram {
  */
 struct onenand_chip {
  void __iomem *base;
+ unsigned int dies;
+ unsigned int boundary[MAX_DIES];
+ loff_t diesize[MAX_DIES];
  unsigned int chipsize;
  unsigned int device_id;
  unsigned int version_id;
+ unsigned int technology;
  unsigned int density_mask;
  unsigned int options;
 
@@ -125,6 +135,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)
 
@@ -157,4 +169,6 @@ struct onenand_manufacturers {
 int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
    struct mtd_oob_ops *ops);
 
+unsigned int onenand_block(struct onenand_chip *this, loff_t addr);
+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 fc63380..bc2bb4a 100644
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -67,6 +67,9 @@
 /*
  * 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)
@@ -84,6 +87,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)
@@ -93,7 +101,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)
 
@@ -105,7 +113,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)
@@ -123,9 +131,14 @@
 #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)
+#define FLEXONENAND_CMD_READ_PI  (0x1985)
 
 /*
  * System Configuration 1 Register F221h (R, R/W)
@@ -186,5 +199,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 */
diff --git a/include/onenand_uboot.h b/include/onenand_uboot.h
index 49da9d0..cfa46f8 100644
--- a/include/onenand_uboot.h
+++ b/include/onenand_uboot.h
@@ -23,6 +23,7 @@ struct erase_info;
 struct onenand_chip;
 
 extern struct mtd_info onenand_mtd;
+extern struct onenand_chip onenand_chip;
 
 /* board */
 extern void onenand_board_init(struct mtd_info *);
@@ -38,6 +39,13 @@ extern int onenand_erase(struct mtd_info *mtd, struct erase_info *instr);
 
 extern char *onenand_print_device_info(int device, int version);
 
+extern unsigned onenand_block(struct onenand_chip *this, loff_t addr);
+
+extern int flexonenand_region(struct mtd_info *mtd, loff_t addr);
+
+extern int flexonenand_set_boundary(struct mtd_info *mtd, int die,
+     int boundary, int lock);
+
 /* S3C64xx */
 extern void s3c64xx_onenand_init(struct mtd_info *);
 extern void s3c64xx_set_width_regs(struct onenand_chip *);


More information about the U-Boot mailing list