[ANNOUNCE] [PATCH] [MTD] Flex-OneNAND MTD Driver available.

Adrian Hunter ext-adrian.hunter at nokia.com
Fri Oct 24 10:26:47 EDT 2008


apgmoorthy wrote:
> On Monday, October 20, 2008 11:24 AM , Artem wrote:
> 
>>>   * onenand_get_density - [DEFAULT] Get OneNAND density
>>>   * @param dev_id    OneNAND device ID
>>>   *
>>> @@ -196,6 +280,7 @@ static int onenand_command(struct mtd_info *mtd, int
>>> cmd, loff_t addr, size_t le
>>>  {
>>>      struct onenand_chip *this = mtd->priv;
>>>      int value, block, page;
>>> +    unsigned slc = 0;
>>>  
>>>      /* Address translation */
>>>      switch (cmd) {
>>> @@ -207,15 +292,16 @@ static int onenand_command(struct mtd_info
>>> *mtd, int
>>> cmd, loff_t addr, size_t le
>>>          page = -1;
>>>          break;
>>>  
>>> +    case FLEXONENAND_CMD_PI_ACCESS:
>>>      case ONENAND_CMD_ERASE:
>>>      case ONENAND_CMD_BUFFERRAM:
>>>      case ONENAND_CMD_OTP_ACCESS:
>>> -        block = (int) (addr >> this->erase_shift);
>>> +        block = onenand_get_block(mtd, addr, NULL);
>> +/**
>>
>> Why do you make SLC slower by adding a function call it which it does
>> not need? Could you please make it in-line for SLC and a func for MLC.
>> IOW, I do not like that you make SLC degrade because of supporting MLC.
>>
>> E.g., you may do it like this:
>>
>> static inline onenand_get_block()
>> {
>>     if (SLC)
>>         return addr >> this->erase_shift;
>>     else
>>         return flexonenand_get_block()
>> }
>            - Agreed. Changed it to inline.
> 
>>>          page = -1;
>>>          break;
>>>  
>>>      default:
>>> -        block = (int) (addr >> this->erase_shift);
>>> +        block = onenand_get_block(mtd, addr, &slc);
>>>          page = (int) (addr >> this->page_shift);
>>>  
>>>          if (ONENAND_IS_2PLANE(this)) {
>>> @@ -227,6 +313,8 @@ static int onenand_command(struct mtd_info *mtd, int
>>> cmd, loff_t addr, size_t le
>>>              page >>= 1;
>>>          }
>>>          page &= this->page_mask;
>>> +        if (FLEXONENAND(this) && slc)
>>> +            page &= (this->page_mask >> 1);
>> This is bad. Please, be consistent and use _one_ way to get the address.
>> Either a function or the if statement. Do not introduce mess by mixing
>> these approaches.
> 
> Ok.  
> Actually, there are two macros : ONENAND_IS_MLC(this) and FLEXONENAND(this).
> 1) In OneNAND, page size is 2KB, pages_per_block is 64.
> 2) In MLC OneNAND, page size is 4KB, pages_per_block is 64.
> 3) In Flex-OneNAND, page size is 4KB, pages_per_block is 64 (SLC) or 128
> (MLC)
> 
> With 4KB page size,
>  - all dataram is used, so read while load is not there.
>  - read/write oob-only commands are not present.
> 
> With Flex-OneNAND, we have
>  - erase regions for handling SLC and MLC areas of device.
>  - LSB recovery for MLC area read failure
>  - 4 ecc registers
>  - different OTP lock position

So if FLEXONENAND(this) is true, does ONENAND_IS_MLC(this) also have to be true?

Did you consider making MLC and Flex-OneNAND support optional via a config option?

> 
> The patch adds support for both 2) and 3)
> 
>> I tried to review the rest of the patch. But quite frankly, it is so
>> difficult to do, because you injected little pieces of
>>
>> if (MLC) {
>>     do;
>>     various;
>>     stuff;
>> }
>>
>> all over the place, and made the driver very difficult to follow. Could
>> you please work some more on the driver and try to improve the
>> readability? E.g., by making some functions to have 2 separate variants
>> - one for SLC, one for MLC. If you do not want to duplicate some code -
>> this is what fuctions exist for.
> 
>           - Agreed. We now have read function for MLC OneNAND without read-while-load.
> 
> 
> Please find the patch , inherited with comments

There are a couple more comments below, but in general it seems like you could
still try to have fewer FLEXONENAND(this) and ONENAND_IS_MLC(this).

> 
> 
> Signed-off-by: Vishak G <vishak.g at samsung.com>
> Signed-off-by: Rohit Hagargundgi <h.rohit at samsung.com>
> ---
>  drivers/mtd/onenand/onenand_base.c |  690 +++++++++++++++++++++++++++++++++----
>  drivers/mtd/onenand/onenand_bbt.c  |   15
>  drivers/mtd/onenand/onenand_sim.c  |  105 +++++
>  include/linux/mtd/onenand.h        |   33 +
>  include/linux/mtd/onenand_regs.h   |   19 -
>  5 files changed, 788 insertions(+), 74 deletions(-)
> 
> diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
> index 90ed319..2175329 100644
> --- a/drivers/mtd/onenand/onenand_base.c
> +++ b/drivers/mtd/onenand/onenand_base.c
> @@ -9,6 +9,10 @@
>   *	auto-placement support, read-while load support, various fixes
>   *	Copyright (C) Nokia Corporation, 2007
>   *
> + *	Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
> + *	Flex-OneNAND support
> + *	Copyright (C) Samsung Electronics, 2008
> + *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License version 2 as
>   * published by the Free Software Foundation.
> @@ -27,6 +31,37 @@
>  
>  #include <asm/io.h>
>  
> +static const int boundary[] = {
> +	FLEXONENAND_DIE0_BOUNDARY,
> +	FLEXONENAND_DIE1_BOUNDARY,
> +};
> +
> +static const int lock[] = {
> +	FLEXONENAND_DIE0_ISLOCKED,
> +	FLEXONENAND_DIE1_ISLOCKED,
> +};
> +
> +/**
> + *  onenand_oob_128 - oob info for Flex-Onenand with 4KB page
> + *  For now, we expose only 64 out of 80 ecc bytes
> + */
> +static struct nand_ecclayout onenand_oob_128 = {
> +	.eccbytes	= 64,
> +	.eccpos		= {
> +		6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
> +		22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
> +		38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
> +		54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
> +		70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
> +		86, 87, 88, 89, 90, 91, 92, 93, 94, 95,
> +		102, 103, 104, 105
> +		},
> +	.oobfree	= {
> +		{2, 4}, {18, 4}, {34, 4}, {50, 4},
> +		{66, 4}, {82, 4}, {98, 4}, {114, 4}
> +	}
> +};
> +
>  /**
>   * onenand_oob_64 - oob info for large (2KB) page
>   */
> @@ -65,6 +100,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 */
>  };
>  
>  /**
> @@ -171,6 +214,52 @@ 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);
> +}
> +
> +/**
>   * onenand_get_density - [DEFAULT] Get OneNAND density
>   * @param dev_id	OneNAND device ID
>   *
> @@ -196,6 +285,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  {
>  	struct onenand_chip *this = mtd->priv;
>  	int value, block, page;
> +	unsigned slc = 0;
>  
>  	/* Address translation */
>  	switch (cmd) {
> @@ -207,15 +297,16 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  		page = -1;
>  		break;
>  
> +	case FLEXONENAND_CMD_PI_ACCESS:
>  	case ONENAND_CMD_ERASE:
>  	case ONENAND_CMD_BUFFERRAM:
>  	case ONENAND_CMD_OTP_ACCESS:
> -		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);
>  
>  		if (ONENAND_IS_2PLANE(this)) {
> @@ -227,6 +318,8 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  			page >>= 1;
>  		}
>  		page &= this->page_mask;
> +		if (slc)
> +			page &= (this->page_mask >> 1);
>  		break;
>  	}
>  
> @@ -236,7 +329,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  		value = onenand_bufferram_address(this, block);
>  		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
>  
> -		if (ONENAND_IS_2PLANE(this))
> +		if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this))
>  			/* It is always BufferRAM0 */
>  			ONENAND_SET_BUFFERRAM0(this);
>  		else
> @@ -258,13 +351,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  
>  	if (page != -1) {
>  		/* Now we use page size operation */
> -		int sectors = 4, count = 4;
> +		int sectors = 0, count = 0;
>  		int dataram;
>  
>  		switch (cmd) {
> +		case FLEXONENAND_CMD_RECOVER_LSB:
>  		case ONENAND_CMD_READ:
>  		case ONENAND_CMD_READOOB:
> -			dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
> +			if (ONENAND_IS_MLC(this))
> +				/* It is always BufferRAM0 */
> +				dataram = ONENAND_SET_BUFFERRAM0(this);
> +			else
> +				dataram = ONENAND_SET_NEXT_BUFFERRAM(this);
>  			break;
>  
>  		default:
> @@ -293,6 +391,31 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
>  }
>  
>  /**
> + * onenand_read_ecc - return ecc status
> + * @param mtd 		MTD device structure
> + */
> +static inline int onenand_read_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 +
> +					 (ONENAND_REG_ECC_STATUS + i));
> +		if (!FLEXONENAND(this))
> +			return ecc[i];
> +		if (ecc[i] & FLEXONENAND_UNCORRECTABLE_ERROR) {
> +			result = ONENAND_ECC_2BIT_ALL;
> +			break;
> +		} else if (ecc[i])
> +			result = ONENAND_ECC_1BIT_ALL;
> +	}
> +
> +	return result;
> +}

This function could be written more clearly.  Why not pass 'chip'
instead of 'mtd' and drop the array e.g.

static inline int onenand_read_ecc(struct onenand_chip *this)
{
	int ecc, i, result = 0;

	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);
		if (!ecc)
			continue;
		if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR)
			return ONENAND_ECC_2BIT_ALL;
		else
			result = ONENAND_ECC_1BIT_ALL;
	}
	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
> @@ -331,14 +454,14 @@ static int onenand_wait(struct mtd_info *mtd, int state)
>  	 * power off recovery (POR) test, it should read ECC status first
>  	 */
>  	if (interrupt & ONENAND_INT_READ) {
> -		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
> +		int ecc = onenand_read_ecc(mtd);
>  		if (ecc) {
>  			if (ecc & ONENAND_ECC_2BIT_ALL) {
>  				printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc);
>  				mtd->ecc_stats.failed++;
>  				return -EBADMSG;
>  			} else if (ecc & ONENAND_ECC_1BIT_ALL) {
> -				printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
> +				printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc);
>  				mtd->ecc_stats.corrected++;
>  			}
>  		}
> @@ -656,7 +779,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_get_block(mtd, addr, NULL);
>  		int value = onenand_bufferram_address(this, block);
>  		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
>  	}
> @@ -816,6 +939,143 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col
>  }
>  
>  /**
> + * 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 / onenand_bbt_wait
> + *
> + * Issue recovery command when read fails on MLC area.

Please explain more here about what LSB recovery is.

> + */
> +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_INFO "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_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band
> + * @param mtd		MTD device structure
> + * @param from		offset to read from
> + * @param ops:		oob operation description structure
> + *
> + * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram.
> + * So, read-while-load is not present.
> + */
> +static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from,
> +				struct mtd_oob_ops *ops)
> +{
> +	struct onenand_chip *this = mtd->priv;
> +	struct mtd_ecc_stats stats;
> +	size_t len = ops->len;
> +	size_t ooblen = ops->ooblen;
> +	u_char *buf = ops->datbuf;
> +	u_char *oobbuf = ops->oobbuf;
> +	int read = 0, column, thislen;
> +	int oobread = 0, oobcolumn, thisooblen, oobsize;
> +	int ret = 0;
> +	int writesize = this->writesize;
> +
> +	DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
> +
> +	if (ops->mode == MTD_OOB_AUTO)
> +		oobsize = this->ecclayout->oobavail;
> +	else
> +		oobsize = mtd->oobsize;
> +
> +	oobcolumn = from & (mtd->oobsize - 1);
> +
> +	/* Do not allow reads past end of device */
> +	if ((from + len) > mtd->size) {
> +		printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n");
> +		ops->retlen = 0;
> +		ops->oobretlen = 0;
> +		return -EINVAL;
> +	}
> +
> +	stats = mtd->ecc_stats;
> +
> +	while (read < len) {
> +		cond_resched();
> +
> +		thislen = min_t(int, writesize, len - read);
> +
> +		column = from & (writesize - 1);
> +		if (column + thislen > writesize)
> +			thislen = writesize - column;
> +
> +		if (!onenand_check_bufferram(mtd, from)) {
> +			this->command(mtd, ONENAND_CMD_READ, from, writesize);
> +
> +			ret = this->wait(mtd, FL_READING);
> +			ret = unlikely(ret) ? onenand_recover_lsb(mtd, from, ret) : ret;
> +			onenand_update_bufferram(mtd, from, !ret);
> +			if (ret == -EBADMSG)
> +				ret = 0;
> +		}
> +
> +		this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen);
> +		if (oobbuf) {
> +			thisooblen = oobsize - oobcolumn;
> +			thisooblen = min_t(int, thisooblen, ooblen - oobread);
> +
> +			if (ops->mode == MTD_OOB_AUTO)
> +				onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen);
> +			else
> +				this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen);
> +			oobread += thisooblen;
> +			oobbuf += thisooblen;
> +			oobcolumn = 0;
> +		}
> +
> +		read += thislen;
> +		if (read == len)
> +			break;
> +
> +		from += thislen;
> +		buf += thislen;
> +	}
> +
> +	/*
> +	 * Return success, if no ECC failures, else -EBADMSG
> +	 * fs driver will take care of that, because
> +	 * retlen == desired len and result == -EBADMSG
> +	 */
> +	ops->retlen = read;
> +	ops->oobretlen = oobread;
> +
> +	if (ret)
> +		return ret;
> +
> +	if (mtd->ecc_stats.failed - stats.failed)
> +		return -EBADMSG;
> +
> +	return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
> +}
> +
> +/**
>   * 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
> @@ -962,7 +1222,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;
>  
> @@ -993,17 +1253,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) {
>  		cond_resched();
>  
>  		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 = unlikely(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;
> @@ -1053,6 +1317,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from,
>  static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	size_t *retlen, u_char *buf)
>  {
> +	struct onenand_chip *this = mtd->priv;
>  	struct mtd_oob_ops ops = {
>  		.len	= len,
>  		.ooblen	= 0,
> @@ -1062,7 +1327,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	int ret;
>  
>  	onenand_get_device(mtd, FL_READING);
> -	ret = onenand_read_ops_nolock(mtd, from, &ops);
> +	ret = ONENAND_IS_MLC(this) ?
> +		onenand_mlc_read_ops_nolock(mtd, from, &ops) :
> +		onenand_read_ops_nolock(mtd, from, &ops);
>  	onenand_release_device(mtd);
>  
>  	*retlen = ops.retlen;
> @@ -1080,6 +1347,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,
>  static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
>  			    struct mtd_oob_ops *ops)
>  {
> +	struct onenand_chip *this = mtd->priv;
>  	int ret;
>  
>  	switch (ops->mode) {
> @@ -1094,7 +1362,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from,
>  
>  	onenand_get_device(mtd, FL_READING);
>  	if (ops->datbuf)
> -		ret = onenand_read_ops_nolock(mtd, from, ops);
> +		ret = ONENAND_IS_MLC(this) ?
> +			onenand_mlc_read_ops_nolock(mtd, from, ops) :
> +			onenand_read_ops_nolock(mtd, from, ops);
>  	else
>  		ret = onenand_read_oob_nolock(mtd, from, ops);
>  	onenand_release_device(mtd);
> @@ -1128,11 +1398,11 @@ 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);
> +		int ecc = onenand_read_ecc(mtd);
>  		if (ecc & ONENAND_ECC_2BIT_ALL) {
>  			printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x"
>  				", controller error 0x%04x\n", ecc, ctrl);
> -			return ONENAND_BBT_READ_ERROR;
> +			return ONENAND_BBT_READ_ECC_ERROR;
>  		}
>  	} else {
>  		printk(KERN_ERR "onenand_bbt_wait: read timeout!"
> @@ -1163,7 +1433,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;
>  
> @@ -1183,17 +1453,21 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from,
>  
>  	column = from & (mtd->oobsize - 1);
>  
> +	readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB;
> +
>  	while (read < len) {
>  		cond_resched();
>  
>  		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);
>  
>  		ret = onenand_bbt_wait(mtd, FL_READING);
> +		ret = unlikely(ret) ? onenand_recover_lsb(mtd, from, ret) : ret;
> +
>  		if (ret)
>  			break;
>  
> @@ -1230,9 +1504,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)
> @@ -1586,7 +1862,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;
> @@ -1628,6 +1904,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);
> @@ -1645,7 +1923,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)) {
> @@ -1770,11 +2055,32 @@ static 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;
>  
>  	DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
>  
> -	block_size = (1 << this->erase_shift);
> +	/* Do not allow erase past end of device */
> +	if (unlikely((instr->len + instr->addr) > mtd->size)) {
> +		printk(KERN_ERR "onenand_erase: Erase past end of device\n");
> +		return -EINVAL;
> +	}
> +
> +	if (mtd->numeraseregions > 1) {
> +		/* Find the eraseregion of this address */
> +		for (; i < mtd->numeraseregions &&
> +			instr->addr >= mtd->eraseregions[i].offset; i++)
> +			;
> +		i--;
> +		block_size = mtd->eraseregions[i].erasesize;
> +
> +		/* Start address should be aligned on erase region boundary */
> +		if (unlikely((instr->addr - mtd->eraseregions[i].offset) &
> +							(block_size - 1))) {
> +			printk(KERN_ERR "onenand_erase: Unaligned address\n");
> +			return -EINVAL;
> +		}
> +	} else
> +		block_size = mtd->erasesize;
>  
>  	/* Start address must align on block boundary */
>  	if (unlikely(instr->addr & (block_size - 1))) {
> @@ -1788,12 +2094,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
>  		return -EINVAL;
>  	}
>  
> -	/* Do not allow erase past end of device */
> -	if (unlikely((instr->len + instr->addr) > mtd->size)) {
> -		printk(KERN_ERR "onenand_erase: Erase past end of device\n");
> -		return -EINVAL;
> -	}
> -
>  	instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
>  
>  	/* Grab the lock and see if the device is available */
> @@ -1822,7 +2122,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr)
>  		ret = this->wait(mtd, FL_ERASING);
>  		/* Check, if it is write protected */
>  		if (ret) {
> -			printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift));
> +			printk(KERN_ERR "onenand_erase: Failed erase, block %d\n",
> +			 (unsigned)onenand_get_block(mtd, addr, NULL));
>  			instr->state = MTD_ERASE_FAILED;
>  			instr->fail_addr = addr;
>  			goto erase_exit;
> @@ -1830,6 +2131,19 @@ static 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;
> +			if (len & (block_size - 1)) {
> +				/* This should be handled at MTD partitioning
> +				 * level.
> +				 */
> +				printk(KERN_ERR "onenand_erase: Unaligned address\n");
> +				goto erase_exit;
> +			}
> +		}
>  	}
>  
>  	instr->state = MTD_ERASE_DONE;
> @@ -1908,13 +2222,17 @@ 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_get_block(mtd, ofs, NULL);
>          if (bbm->bbt)
>                  bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
>  
>          /* We write two bytes, so we dont have to mess with 16 bit access */
>          ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
> -        return onenand_write_oob_nolock(mtd, ofs, &ops);
> +	/* FIXME : What to do when marking SLC block in partition
> +	 * 	   with MLC erasesize? For now, it is not advisable to
> +	 *	   create partitions containing both SLC and MLC regions.
> +	 */
> +	return onenand_write_oob_nolock(mtd, ofs, &ops);
>  }
>  
>  /**
> @@ -1958,8 +2276,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_get_block(mtd, ofs, NULL);
> +	end = onenand_get_block(mtd, ofs + len, NULL) - 1;
>  
>  	if (cmd == ONENAND_CMD_LOCK)
>  		wp_status_mask = ONENAND_WP_LS;
> @@ -1971,7 +2289,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int
>  		/* Set start block address */
>  		this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
>  		/* Set end block address */
> -		this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
> +		this->write_word(end, this->base +
> +						 ONENAND_REG_END_BLOCK_ADDRESS);
>  		/* Write lock command */
>  		this->command(mtd, cmd, 0, 0);
>  
> @@ -1992,7 +2311,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 + 1; block++) {
>  		/* Set block address */
>  		value = onenand_block_address(this, block);
>  		this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
> @@ -2086,7 +2405,6 @@ static int onenand_check_lock_status(struct onenand_chip *this)
>  			return 0;
>  		}
>  	}
> -
>  	return 1;
>  }
>  
> @@ -2100,7 +2418,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 */
> @@ -2122,9 +2440,14 @@ static void onenand_unlock_all(struct mtd_info *mtd)
>  
>  		/* Workaround for all block unlock in DDP */
>  		if (ONENAND_IS_DDP(this)) {
> -			/* All blocks on another chip */
> -			ofs = this->chipsize >> 1;
> -			len = this->chipsize >> 1;
> +			/* All blocks on another chip
> +			 * For Flex-OneNAND with both slc
> +			 * mlc regions, we use diesize
> +			 */
> +			ofs = FLEXONENAND(this) ? this->diesize[0] :
> +							this->chipsize >> 1;
> +			len = FLEXONENAND(this) ? this->diesize[1] :
> +							this->chipsize >> 1;
>  		}
>  	}
>  
> @@ -2163,7 +2486,9 @@ static int do_otp_read(struct mtd_info *mtd, loff_t from, size_t len,
>  	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
>  	this->wait(mtd, FL_OTPING);
>  
> -	ret = onenand_read_ops_nolock(mtd, from, &ops);
> +	ret = ONENAND_IS_MLC(this) ?
> +		onenand_mlc_read_ops_nolock(mtd, from, &ops) :
> +		onenand_read_ops_nolock(mtd, from, &ops);
>  
>  	/* Exit OTP access mode */
>  	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> @@ -2230,21 +2555,34 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len,
>  		size_t *retlen, u_char *buf)
>  {
>  	struct onenand_chip *this = mtd->priv;
> -	struct mtd_oob_ops ops = {
> -		.mode = MTD_OOB_PLACE,
> -		.ooblen = len,
> -		.oobbuf = buf,
> -		.ooboffs = 0,
> -	};
> +	struct mtd_oob_ops ops;
>  	int ret;
>  
> +	if (FLEXONENAND(this)) {
> +		ops.len = mtd->writesize;
> +		ops.ooblen = 0;
> +		ops.datbuf = buf;
> +		ops.oobbuf = NULL;
> +	} else {
> +		ops.mode = MTD_OOB_PLACE;
> +		ops.ooblen = len;
> +		ops.oobbuf = buf;
> +		ops.ooboffs = 0;
> +	}
> +
>  	/* Enter OTP access mode */
>  	this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0);
>  	this->wait(mtd, FL_OTPING);
>  
> -	ret = onenand_write_oob_nolock(mtd, from, &ops);
> +	/*
> +	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
> +	 * main area of page 49.
> +	 */
> +	ret = FLEXONENAND(this) ?
> +		onenand_write_ops_nolock(mtd, (mtd->writesize * 49), &ops)
> +		: onenand_write_oob_nolock(mtd, from, &ops);
>  
> -	*retlen = ops.oobretlen;
> +	*retlen = FLEXONENAND(this) ? ops.retlen : ops.oobretlen;

Why not just:

	if (FLEXONENAND(this)) {
		/*
		 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
		 * main area of page 49.
		 */
		ops.len = mtd->writesize;
		ops.ooblen = 0;
		ops.datbuf = buf;
		ops.oobbuf = NULL;
		ret = onenand_write_ops_nolock(mtd, (mtd->writesize * 49), &ops);
		*retlen = ops.retlen;
	} else {
		ops.mode = MTD_OOB_PLACE;
		ops.ooblen = len;
		ops.oobbuf = buf;
		ops.ooboffs = 0;
		ret = onenand_write_oob_nolock(mtd, from, &ops);
		*retlen = ops.oobretlen;
	}

>  
>  	/* Exit OTP access mode */
>  	this->command(mtd, ONENAND_CMD_RESET, 0, 0);
> @@ -2428,27 +2766,33 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
>  			size_t len)
>  {
>  	struct onenand_chip *this = mtd->priv;
> -	u_char *oob_buf = this->oob_buf;
> +	u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf;
>  	size_t retlen;
>  	int ret;
>  
> -	memset(oob_buf, 0xff, mtd->oobsize);
> +	memset(buf, 0xff, FLEXONENAND(this) ? this->writesize
> +						 : mtd->oobsize);
>  	/*
>  	 * Note: OTP lock operation
>  	 *       OTP block : 0xXXFC
>  	 *       1st block : 0xXXF3 (If chip support)
>  	 *       Both      : 0xXXF0 (If chip support)
>  	 */
> -	oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
> +	FLEXONENAND(this) ?
> +		buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC :
> +		buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC;
>  
>  	/*
>  	 * Write lock mark to 8th word of sector0 of page0 of the spare0.
>  	 * We write 16 bytes spare area instead of 2 bytes.
> +	 * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of
> +	 * main area of page 49.
>  	 */
> +
>  	from = 0;
> -	len = 16;
> +	len = FLEXONENAND(this) ? mtd->writesize : 16;
>  
> -	ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER);
> +	ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER);
>  
>  	return ret ? : retlen;
>  }
> @@ -2495,6 +2839,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)
> @@ -2512,14 +2864,16 @@ static void onenand_check_features(struct mtd_info *mtd)
>   */
>  static void onenand_print_device_info(int device, int version)
>  {
> -        int vcc, demuxed, ddp, density;
> +	int vcc, demuxed, ddp, density, flexonenand;
>  
>          vcc = device & ONENAND_DEVICE_VCC_MASK;
>          demuxed = device & ONENAND_DEVICE_IS_DEMUX;
>          ddp = device & ONENAND_DEVICE_IS_DDP;
>          density = onenand_get_density(device);
> -        printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
> -                demuxed ? "" : "Muxed ",
> +	flexonenand = device & DEVICE_IS_FLEXONENAND;
> +	printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n",
> +		demuxed ? "" : "Muxed ",
> +		flexonenand ? "Flex-" : "",
>                  ddp ? "(DDP)" : "",
>                  (16 << density),
>                  vcc ? "2.65/3.3" : "1.8",
> @@ -2558,6 +2912,181 @@ 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, 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, ONENAND_CMD_READ, die, 0);
> +		ret = this->wait(mtd, FL_READING);
> +
> +		bdry = this->read_word(this->base + ONENAND_DATARAM);
> +		locked = bdry >> FLEXONENAND_PI_UNLOCK_SHIFT;
> +		locked = (locked == 0x3) ? 0 : 1;
> +		this->boundary[die] = bdry & FLEXONENAND_PI_MASK;
> +		this->boundary_locked[die] = locked;
> +		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
> + * 			  boundary[], diesize[], chipsize,
> + *			  boundary_locked[]
> + * @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);
> +	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
> +	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);
> +
> +	for (die = 0, mtd->size = 0; die < this->dies; die++) {
> +		this->diesize[die] = (blksperdie << this->erase_shift);
> +		this->diesize[die] -= (this->boundary[die] + 1)
> +						 << (this->erase_shift - 1);
> +		mtd->size += this->diesize[die];
> +	}
> +
> +	/* this->chipsize represents maximum possible chip size */
> +	this->chipsize = (16 << density) << 20;
> +}
> +
> +/**
> + * flexonenand_set_boundary	- Writes the SLC boundary
> + * @param onenand_info		- onenand info structure
> + */
> +static int flexonenand_set_boundary(struct mtd_info *mtd, unsigned die,
> +						int boundary, int lock)
> +{
> +	struct onenand_chip *this = mtd->priv;
> +	int ret, density, blksperdie;
> +	loff_t addr;
> +
> +	density = onenand_get_density(this->device_id);
> +	blksperdie = ((16 << density) << 20) >> this->erase_shift;
> +	blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0;
> +
> +	printk(KERN_INFO "Changing die %d boundary: %d%s\n", die, boundary,
> +					 lock ? "(Locked)" : "(Unlocked)");
> +	if (boundary >= blksperdie) {
> +		printk(KERN_ERR "Invalid boundary value.\
> +						 Boundary not changed.\n");
> +		return -1;
> +	}
> +
> +	if (this->boundary_locked[die]) {
> +		printk(KERN_ERR "Die boundary is locked.\
> +						 Boundary not changed.\n");
> +		return -1;
> +	}
> +
> +	addr = die ? this->diesize[0] : 0;
> +
> +	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, die, 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*/
> +		flexonenand_get_size(mtd);
> +	return ret;
> +}
> +
> +/**
>   * onenand_probe - [OneNAND Interface] Probe the OneNAND device
>   * @param mtd		MTD device structure
>   *
> @@ -2599,6 +3128,7 @@ 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)
> @@ -2610,20 +3140,35 @@ static int onenand_probe(struct mtd_info *mtd)
>  	this->version_id = ver_id;
>  
>  	density = onenand_get_density(dev_id);
> -	this->chipsize = (16 << density) << 20;
> +	if (FLEXONENAND(this)) {
> +		this->dies = ONENAND_IS_DDP(this) ? 2 : 1;
> +		/* Maximum possible erase regions */
> +		mtd->numeraseregions = this->dies << 1;
> +		mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info)
> +					* (this->dies << 1), GFP_KERNEL);
> +		if (!mtd->eraseregions)
> +			return -ENOMEM;
> +	}
> +	this->chipsize = FLEXONENAND(this) ? 0 : (16 << density) << 20;
>  	/* Set density mask. it is used for DDP */
>  	if (ONENAND_IS_DDP(this))
> -		this->density_mask = (1 << (density + 6));
> +		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;
>  	/* Pages per a block are 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;
>  	this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1;
> @@ -2632,7 +3177,20 @@ static int onenand_probe(struct mtd_info *mtd)
>  
>  	/* REVIST: Multichip handling */
>  
> -	mtd->size = this->chipsize;
> +	if (FLEXONENAND(this)) {
> +		unsigned die;
> +
> +		flexonenand_get_size(mtd);
> +
> +		/* Change the device boundaries if required */
> +		for (die = 0; die < this->dies; die++)
> +			if ((!this->boundary_locked[die]) &&
> +			   (boundary[die] >= 0) &&
> +			   (boundary[die] != this->boundary[die]))
> +				flexonenand_set_boundary(mtd, die,
> +						 boundary[die], lock[die]);
> +	} else
> +		mtd->size = this->chipsize;
>  
>  	/* Check OneNAND features */
>  	onenand_check_features(mtd);
> @@ -2749,6 +3307,10 @@ 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;
> @@ -2768,6 +3330,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
>  		break;
>  	}
>  
> +	/* Don't allow the sub-page write in MLC */
> +	if (ONENAND_IS_MLC(this))
> +		mtd->subpage_sft = 0;
> +
>  	this->subpagesize = mtd->writesize >> mtd->subpage_sft;
>  
>  	/*
> @@ -2843,6 +3409,8 @@ void onenand_release(struct mtd_info *mtd)
>  		kfree(this->page_buf);
>  	if (this->options & ONENAND_OOBBUF_ALLOC)
>  		kfree(this->oob_buf);
> +	if (FLEXONENAND(this))
> +		kfree(mtd->eraseregions);
>  }
>  
>  EXPORT_SYMBOL_GPL(onenand_scan);
> diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
> index 2f53b51..88c63b7 100644
> --- a/drivers/mtd/onenand/onenand_bbt.c
> +++ b/drivers/mtd/onenand/onenand_bbt.c
> @@ -60,6 +60,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
>  	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;
> @@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
>  	/* 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;
>  
> @@ -106,7 +107,13 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
>  			}
>  		}
>  		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;
> @@ -143,7 +150,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;
>  
>  	DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
> @@ -178,7 +185,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) and clear the memory bad block table */
>  	bbm->bbt = kzalloc(len, GFP_KERNEL);
>  	if (!bbm->bbt) {
> diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c
> index d64200b..012d926 100644
> --- a/drivers/mtd/onenand/onenand_sim.c
> +++ b/drivers/mtd/onenand/onenand_sim.c
> @@ -6,6 +6,10 @@
>   *  Copyright © 2005-2007 Samsung Electronics
>   *  Kyungmin Park <kyungmin.park at samsung.com>
>   *
> + *  Vishak G <vishak.g at samsung.com>, Rohit Hagargundgi <h.rohit at samsung.com>
> + *  Flex-OneNAND simulator support
> + *  Copyright (C) Samsung Electronics, 2008
> + *
>   * This program is free software; you can redistribute it and/or modify
>   * it under the terms of the GNU General Public License version 2 as
>   * published by the Free Software Foundation.
> @@ -24,16 +28,38 @@
>  #ifndef CONFIG_ONENAND_SIM_MANUFACTURER
>  #define CONFIG_ONENAND_SIM_MANUFACTURER         0xec
>  #endif
> +
>  #ifndef CONFIG_ONENAND_SIM_DEVICE_ID
>  #define CONFIG_ONENAND_SIM_DEVICE_ID            0x04
>  #endif
> +
> +#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1)
> +
>  #ifndef CONFIG_ONENAND_SIM_VERSION_ID
>  #define CONFIG_ONENAND_SIM_VERSION_ID           0x1e
>  #endif
>  
> +#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID
> +#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND
> +#endif
> +
> +/* Initial boundary values for Flex-OneNAND Simulator */
> +#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY
> +#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY	0x01
> +#endif
> +
> +#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY
> +#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY	0x01
> +#endif
> +
>  static int manuf_id	= CONFIG_ONENAND_SIM_MANUFACTURER;
>  static int device_id	= CONFIG_ONENAND_SIM_DEVICE_ID;
>  static int version_id	= CONFIG_ONENAND_SIM_VERSION_ID;
> +static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID;
> +static int boundary[] = {
> +	CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY,
> +	CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY,
> +};
>  
>  struct onenand_flash {
>  	void __iomem *base;
> @@ -57,12 +83,18 @@ struct onenand_flash {
>  	(writew(v, this->base + ONENAND_REG_WP_STATUS))
>  
>  /* It has all 0xff chars */
> -#define MAX_ONENAND_PAGESIZE		(2048 + 64)
> +#define MAX_ONENAND_PAGESIZE		(4096 + 128)
>  static unsigned char *ffchars;
>  
> +#if CONFIG_FLEXONENAND
> +#define PARTITION_NAME "Flex-OneNAND simulator partition"
> +#else
> +#define PARTITION_NAME "OneNAND simulator partition"
> +#endif
> +
>  static struct mtd_partition os_partitions[] = {
>  	{
> -		.name		= "OneNAND simulator partition",
> +		.name		= PARTITION_NAME,
>  		.offset		= 0,
>  		.size		= MTDPART_SIZ_FULL,
>  	},
> @@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd)
>  
>  	switch (cmd) {
>  	case ONENAND_CMD_UNLOCK:
> +	case ONENAND_CMD_UNLOCK_ALL:
>  		if (block_lock_scheme)
>  			ONENAND_SET_WP_STATUS(ONENAND_WP_US, this);
>  		else
> @@ -228,10 +261,11 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
>  {
>  	struct mtd_info *mtd = &info->mtd;
>  	struct onenand_flash *flash = this->priv;
> -	int main_offset, spare_offset;
> +	int main_offset, spare_offset, die = 0;
>  	void __iomem *src;
>  	void __iomem *dest;
> -	unsigned int i;
> +	unsigned int i, slc = 0;
> +	static int pi_operation;
>  
>  	if (dataram) {
>  		main_offset = mtd->writesize;
> @@ -241,10 +275,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
>  		spare_offset = 0;
>  	}
>  
> +	if (pi_operation) {
> +		die = readw(this->base + ONENAND_REG_START_ADDRESS2);
> +		die >>= ONENAND_DDP_SHIFT;
> +	}
> +
>  	switch (cmd) {
> +	case FLEXONENAND_CMD_PI_ACCESS:
> +		pi_operation = 1;
> +		break;
> +
> +	case ONENAND_CMD_RESET:
> +		pi_operation = 0;
> +		break;
> +
>  	case ONENAND_CMD_READ:
>  		src = ONENAND_CORE(flash) + offset;
>  		dest = ONENAND_MAIN_AREA(this, main_offset);
> +		if (pi_operation) {
> +			writew(boundary[die], this->base + ONENAND_DATARAM);
> +			break;
> +		}
>  		memcpy(dest, src, mtd->writesize);
>  		/* Fall through */
>  
> @@ -257,6 +308,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
>  	case ONENAND_CMD_PROG:
>  		src = ONENAND_MAIN_AREA(this, main_offset);
>  		dest = ONENAND_CORE(flash) + offset;
> +		if (pi_operation) {
> +			boundary[die] = readw(this->base + ONENAND_DATARAM);
> +			break;
> +		}
>  		/* To handle partial write */
>  		for (i = 0; i < (1 << mtd->subpage_sft); i++) {
>  			int off = i * this->subpagesize;
> @@ -284,9 +339,16 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
>  		break;
>  
>  	case ONENAND_CMD_ERASE:
> +		if (pi_operation)
> +			break;
> +		onenand_get_block(mtd, offset, &slc);
> +		if (slc && (mtd->numeraseregions > 1))
> +			mtd->erasesize >>= 1;
>  		memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize);
>  		memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff,
>  		       (mtd->erasesize >> 5));
> +		if (slc && (mtd->numeraseregions > 1))
> +			mtd->erasesize <<= 1;
>  		break;
>  
>  	default:
> @@ -295,6 +357,29 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd,
>  }
>  
>  /**
> + * flexonenand_get_addr - Return address of the block
> + * @block:		Block number on Flex-OneNAND
> + *
> + */
> +loff_t flexonenand_get_addr(struct onenand_chip *this, int block)
> +{
> +	loff_t ofs;
> +	int die = 0, boundary;
> +
> +	ofs = 0;
> +	if (ONENAND_IS_DDP(this) && 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;
> +}
> +
> +/**
>   * onenand_command_handle - Handle command
>   * @this:		OneNAND device structure
>   * @cmd:		The command to be sent
> @@ -338,8 +423,12 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd)
>  		break;
>  	}
>  
> -	if (block != -1)
> -		offset += block << this->erase_shift;
> +	if (block != -1) {
> +		if (FLEXONENAND(this))
> +			offset = flexonenand_get_addr(this, block);
> +		else
> +			offset += block << this->erase_shift;
> +	}
>  
>  	if (page != -1)
>  		offset += page << this->page_shift;
> @@ -390,6 +479,7 @@ static int __init flash_init(struct onenand_flash *flash)
>  	}
>  
>  	density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
> +	density &= ONENAND_DEVICE_DENSITY_MASK;
>  	size = ((16 << 20) << density);
>  
>  	ONENAND_CORE(flash) = vmalloc(size + (size >> 5));
> @@ -405,8 +495,9 @@ static int __init flash_init(struct onenand_flash *flash)
>  	writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID);
>  	writew(device_id, flash->base + ONENAND_REG_DEVICE_ID);
>  	writew(version_id, flash->base + ONENAND_REG_VERSION_ID);
> +	writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY);
>  
> -	if (density < 2)
> +	if (density < 2 && (!CONFIG_FLEXONENAND))
>  		buffer_size = 0x0400;	/* 1KiB page */
>  	else
>  		buffer_size = 0x0800;	/* 2KiB page */
> diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
> index 9aa2a91..c3b9f9a 100644
> --- a/include/linux/mtd/onenand.h
> +++ b/include/linux/mtd/onenand.h
> @@ -17,8 +17,24 @@
>  #include <linux/mtd/onenand_regs.h>
>  #include <linux/mtd/bbm.h>
>  
> +#define MAX_DIES			2
>  #define MAX_BUFFERRAM		2
>  
> +/**
> + *  FlexOneNAND device boundary setting
> + *  Setting -1 will not change the boundary
> + */
> +#define FLEXONENAND_DIE0_BOUNDARY	-1
> +#define FLEXONENAND_DIE1_BOUNDARY	-1
> +
> +/**
> + *  Setting value 1 locks the boundary
> + *  WARNING : Once locked, the boundary cannot be changed.
> + *	      Use with care.
> + */
> +#define FLEXONENAND_DIE0_ISLOCKED	0
> +#define FLEXONENAND_DIE1_ISLOCKED	0
> +
>  /* Scan and identify a OneNAND device */
>  extern int onenand_scan(struct mtd_info *mtd, int max_chips);
>  /* Free resources held by the OneNAND device */
> @@ -51,6 +67,11 @@ struct onenand_bufferram {
>  /**
>   * struct onenand_chip - OneNAND Private Flash Chip Data
>   * @base:		[BOARDSPECIFIC] address to access OneNAND
> + * @dies:		[INTERN][FLEX-ONENAND] number of dies on chip
> + * @boundary:		[INTERN][FLEX-ONENAND] Boundary of the dies
> + * @boundary_locked:	[INTERN][FLEX-ONENAND] TRUE indicates die boundary
> + * 			is locked and cannot be changed
> + * @diesize:		[INTERN][FLEX-ONENAND] Size of the dies
>   * @chipsize:		[INTERN] the size of one chip for multichip arrays
>   * @device_id:		[INTERN] device ID
>   * @density_mask:	chip density, used for DDP devices
> @@ -92,9 +113,14 @@ struct onenand_bufferram {
>   */
>  struct onenand_chip {
>  	void __iomem		*base;
> +	unsigned		dies;
> +	unsigned		boundary[MAX_DIES];
> +	unsigned int 		boundary_locked[MAX_DIES];
> +	unsigned int		diesize[MAX_DIES];
>  	unsigned int		chipsize;
>  	unsigned int		device_id;
>  	unsigned int		version_id;
> +	unsigned int		technology;
>  	unsigned int		density_mask;
>  	unsigned int		options;
>  
> @@ -145,6 +171,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_GET_SYS_CFG1(this)					\
>  	(this->read_word(this->base + ONENAND_REG_SYS_CFG1))
>  #define ONENAND_SET_SYS_CFG1(v, this)					\
> @@ -153,6 +181,9 @@ struct onenand_chip {
>  #define ONENAND_IS_DDP(this)						\
>  	(this->device_id & ONENAND_DEVICE_IS_DDP)
>  
> +#define ONENAND_IS_MLC(this)						\
> +	(this->technology & ONENAND_TECHNOLOGY_IS_MLC)
> +
>  #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM
>  #define ONENAND_IS_2PLANE(this)						\
>  	(this->options & ONENAND_HAS_2PLANE)
> @@ -189,5 +220,7 @@ 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);
>  
>  #endif	/* __LINUX_MTD_ONENAND_H */
> diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
> index 0c6bbe2..da48c36 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) & Start Address 2 F101h (R/W)
>   */
>  #define ONENAND_DDP_SHIFT		(15)
> @@ -93,7 +101,8 @@
>  /*
>   * Start Address 8 F107h (R/W)
>   */
> -#define ONENAND_FPA_MASK		(0x3f)
> +/* Note: It's actually 0x3f in case of SLC */
> +#define ONENAND_FPA_MASK		(0x7f)
>  #define ONENAND_FPA_SHIFT		(2)
>  #define ONENAND_FSA_MASK		(0x03)
>  
> @@ -105,7 +114,8 @@
>  #define ONENAND_BSA_BOOTRAM		(0 << 2)
>  #define ONENAND_BSA_DATARAM0		(2 << 2)
>  #define ONENAND_BSA_DATARAM1		(3 << 2)
> -#define ONENAND_BSC_MASK		(0x03)
> +/* Note: It's actually 0x03 in case of SLC */
> +#define ONENAND_BSC_MASK		(0x07)
>  
>  /*
>   * Command Register F220h (R/W)
> @@ -124,6 +134,9 @@
>  #define ONENAND_CMD_RESET		(0xF0)
>  #define ONENAND_CMD_OTP_ACCESS		(0x65)
>  #define ONENAND_CMD_READID		(0x90)
> +#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)
> @@ -192,10 +205,12 @@
>  #define ONENAND_ECC_1BIT_ALL		(0x5555)
>  #define ONENAND_ECC_2BIT		(1 << 1)
>  #define ONENAND_ECC_2BIT_ALL		(0xAAAA)
> +#define FLEXONENAND_UNCORRECTABLE_ERROR	(0x1010)
>  
>  /*
>   * One-Time Programmable (OTP)
>   */
> +#define FLEXONENAND_OTP_LOCK_OFFSET		(2048)
>  #define ONENAND_OTP_LOCK_OFFSET		(14)
>  
>  #endif	/* __ONENAND_REG_H */
> 
> 
> ______________________________________________________
> Linux MTD discussion mailing list
> http://lists.infradead.org/mailman/listinfo/linux-mtd/




More information about the linux-mtd mailing list