[U-Boot] [PATCH] Flex-OneNAND driver

Rohit h.rohit at samsung.com
Fri Oct 24 15:50:12 CEST 2008


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 <h.rohit at samsung.com>
> 
> 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 <h.rohit at samsung.com>
---
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 */




More information about the U-Boot mailing list