Patchwork state of support for "external ECC hardware"

login
register
mail settings
Submitter Angus CLARK
Date Nov. 14, 2012, 10:59 a.m.
Message ID <50A3797A.4040406@st.com>
Download mbox | patch
Permalink /patch/198872/
State New
Headers show

Comments

Angus CLARK - Nov. 14, 2012, 10:59 a.m.
Hi Chris,

Sorry to come to this thread late (I have been working on non-Flash related
projects recently!), but I have used several Micron "on-die" ECC parts so I
thought I would share my experience.   I will try to collate here my comments to
some of the issues raised in the thread.

On 11/08/2012 04:21 PM, Christopher Harvey wrote:
> We had BCH8 code running, but it wasn't enough. The main reason we
> switched away from host side ECC was because we were getting bitflips
> within the ECC codeword data itself.

I think this point has already been dealt with by others, but just to confirm,
the ECC algorithms handle bit-flips in the data or ECC code.  (In fact, as part
of my test procedures, I manually insert bit-errors in the data and ECC areas
and check for correct fixups.)

On 11/08/2012 04:37 PM, Gerlando Falauto wrote:
> And BTW, wouldn't you also need to explicitly disable on-die ECC in
> order to force that, anyway?

Yes, to insert bit-errors your driver needs to support raw read and write
operations.  You can then use a combination of nandwrite and nanddump to inject
1 or more bitflips, something like:

	1. Write data to Flash, generating ECC data in the process
		nandwrite page.bin /dev/mtd?
	2. Read data + OOB in raw mode
		nanddump --noecc --oob --length=2048 --file=page_ecc.bin /dev/mtd?
	3. Check data for no *real* bit flips

	4. Inject bit-flips to 'page_ecc.bin'

	5. Write corrupted data to a new page, in raw mode
		nandwrite --noecc --oob page_ecc_err.bin

	6. Read back, using ECC
		nanddump --length --file=page_ecc_fix.bin

	7. Check bit-flips have been corrected

[I have a standalone program that implements the same procedure, testing every
bit and multiple bits, although it is not really fit for public consumption I am
afraid.]
	
On 11/08/2012 05:02 PM, Christopher Harvey wrote:
> I was surprised too. I was seeing about 30 bitflips per 512MB. Running
> at about 1/3 of max bus speed. No error codes on write.

That is probably a bit higher than we have experienced, but not significantly so.

On 11/08/2012 05:02 PM, Christopher Harvey wrote:
> I don't know the details of BCH, but apparently not. I asked Micron if
> the OOB area was safer to write to, and they said no. Can somebody on
> this list confirm this?

The OOB area is the same as any other part of the page, in terms of reliability,
and therefore subject to the same ECC requirements.  One thing to look out for
with the Micron devices is that the on-die ECC is applied to some but not all of
the OOB area.  For the ECC-protected OOB, it is important that any data here is
written at the same time as the page data -- this has consequences when using
filesystems that store meta-data in the OOB (eg YAFFS2 and JFFS2 to some
extent).  At the time, there was no user-space tool, or IOCTL, that could write
Page+OOB in one go.  To support writing YAFFS2 images, they had to invent their
own IOCTL and a new tool!

On 11/12/2012 05:19 PM, Gerlando Falauto wrote:
> Would there be any reason *NOT* to use 4-bit ECC with parts which do not
> require it? Apart from performance, of course.

As long as your ECC potency matches or exceeds the reliability characteristics
of the NAND device, there should be no problem (except perhaps performance.)
Indeed, some have been known to use over-spec'ed ECC schemes in an attempt to
improve endurance and data retention -- the qualification reports from the
manufacturers tend to be a bit vague on how effective this strategy might be though.

On 11/08/2012 11:02 AM, Gerlando Falauto wrote:
> As for hardware-based (or on-die) ECC support, one of the application
> notes from Micron (TN-29-56 Enabling On-Die ECC for OMAP3 on
> Linux/Android OS

The TN provides a good start, but neglects a few areas, including:
	* the default BBT pattern clashes with on-die ECC locations
	* it makes no attempt to support raw read/write operations
	* it does not handle the the REWRITE status flag

For what it's worth, I have attached the patch we added to support the Micron
on-die ECC devices -- based on a rather old 2.6.32 kernel I am afraid.  We have
since updated the probing code that detects on-die ECC capabilities, but it
might help if you are planning to do your own support.

Cheers,

Angus
From 31fb1df8757177e47e3368f6e735623748449ef4 Mon Sep 17 00:00:00 2001
From: Angus CLARK <angus.clark@st.com>
Date: Wed, 8 Jun 2011 17:31:24 +0100
Subject: [PATCH (sh-2.6.32.y)] mtd_nand: Support for Micron on-die 4-bit ECC
 SLC LP NAND devices

This patch adds support for the Micron on-die 4-bit ECC SLC LP family of
devices.  The main changes are:

    * Add support for the SET/GET FEATURES NAND CMD (required to enable/disable
      on-die ECC).

    * BBT signature moved so as not to clash with the on-die ECC layout

    * Check STATUS after READ operations for correctable/non-correctable ECC
      errors

    * Add a new ECC layout for on-die 4-bit ECC devices. Note, the use of on-die
      ECC brings a number of limitations to the way in which the OOB area can be
      used.  In particular, some bytes in OOB are ECC-protected, and some are
      not.  The ECC-protected bytes must be written at the same time as the page
      data.  This breaks a number of assumptions made by existing MTD clients.
      As a result, it is not possible to define a ECC layout that is compatible
      with both JFFS2 and YAFFS2.  Here we have chosen to support JFFS2, since
      it breaks fewer assumptions, and requires fewer changes to be made
      elsewhere.  (UBI/UBIFS is fully supported, since it does not use the OOB
      area.)

    * Disable on-die ECC for 'RAW' page read/write operations

    * Disable on-die ECC for all OOB read/write operations (provides greatest
      level of compatibility with existing MTD utilities).

    * Extend nand_get_flash_type() to correctly distinguish between "legacy" and
      4-bit on-die ECC Micron devices.

Signed-off-by: Angus Clark <angus.clark@st.com>
---
 drivers/mtd/nand/nand_base.c     |  277 +++++++++++++++++++++++++++++++++++---
 drivers/mtd/nand/nand_bbt.c      |   30 ++++-
 drivers/mtd/nand/stm_nand_flex.c |    7 +
 include/linux/mtd/nand.h         |    7 +
 4 files changed, 302 insertions(+), 19 deletions(-)

Patch

diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 98060be..45634cb 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -103,6 +103,40 @@  static struct nand_ecclayout nand_oob_128 = {
 		 .length = 78}}
 };
 
+/* Micron 4-bit on-die ECC layout
+ *
+ * The 64-byte OOB is divided into 4 identical records.  Each 16-byte record has
+ * the following layout:
+ *	0x00 - 0x01 : Reserved (for Bad Block Markers)
+ *	0x02 - 0x03 : User Metadata II (unprotected)
+ *	0x04 - 0x07 : User Metadata I  (protected)
+ *	0x08 - 0x0f : ECC for main + Metadata I regions
+ *
+ * The use of on-die ECC brings a number of limitations to the way in which the
+ * OOB area can be used.  In particular, some bytes in OOB are ECC-protected,
+ * and some are not.  The ECC-protected bytes must be written at the same time
+ * as the page data.  This breaks a number of assumptions made by existing MTD
+ * clients.  As a result, it is not possible to define a ECC layout that is
+ * compatible with both JFFS2 and YAFFS2.  Here we have chosen to support JFFS2,
+ * since it breaks fewer assumptions, and requires fewer changes to be made
+ * elsewhere.  (UBI/UBIFS is fully supported, since it does not use the OOB
+ * area.)
+ */
+static struct nand_ecclayout nand_oob_64_4bitondie = {
+	.eccbytes = 32,
+	.eccpos = {
+		8, 9, 10, 11, 12, 13, 14, 15,
+		24, 25, 26, 27, 28, 29, 30, 31,
+		40, 41, 42, 43, 44, 45, 46, 47,
+		56, 57, 58, 59, 60, 61, 62, 63 },
+	.oobfree = {
+		{.offset =  2, .length = 2},
+		{.offset = 18, .length = 2},
+		{.offset = 34, .length = 2},
+		{.offset = 50, .length = 2}
+	}
+};
+
 int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
 		    int new_state);
 
@@ -436,6 +470,104 @@  static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
 	return nand_isbad_bbt(mtd, ofs, allowbbt);
 }
 
+/**
+ * nand_get_features - issue a "GET FEATURES" command
+ * @mtd:	MTD device structure
+ * @feature:	the feature address (FA) to be used
+ * @parameters:	returned parameters (P1,P2,P3,P4)
+ *
+ * Send an entire "SET FEATURES" command to NAND device. This includes
+ * the feature address (FA), and the set of 4 parameters to use (P1,P2,P3,P4).
+ */
+static int nand_get_features(struct mtd_info *mtd, int feature,
+			     uint8_t *parameters)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	/* issue the appropriate command + address */
+	chip->cmdfunc(mtd, NAND_CMD_GETFEATURES, feature, -1);
+
+	/* short delay */
+	ndelay(100);	/* tWB = 100ns */
+
+	/* wait until "GET FEATURES" command is processed */
+	if (!chip->dev_ready)
+		udelay(chip->chip_delay);
+	else
+		while (!chip->dev_ready(mtd))
+			;
+
+	/* read the 4 parameters */
+	chip->read_buf(mtd, parameters, 4);
+
+	DEBUG(MTD_DEBUG_LEVEL0,
+	      "%s: with FA=0x%02x, P1=0x%02x, P2=0x%02x, "
+	      "P3=0x%02x, P4=0x%02x\n",
+	      __func__, feature,
+	      parameters[0], parameters[1], parameters[2], parameters[3]);
+
+	return 0;
+}
+
+/**
+ * nand_set_features - issue a "SET FEATURES" command
+ * @mtd:	MTD device structure
+ * @feature:	the feature address (FA) to be used
+ * @parameters:	the set of 4 parameters to use (P1,P2,P3,P4)
+ *
+ * Send an entire "SET FEATURES" command to NAND device. This includes
+ * the feature address (FA), and the set of 4 parameters to use (P1,P2,P3,P4).
+ */
+static int nand_set_features(struct mtd_info *mtd, int feature,
+			     const uint8_t *parameters)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	DEBUG(MTD_DEBUG_LEVEL0,
+	      "%s: with FA=0x%02x, P1=0x%02x, P2=0x%02x, "
+	      "P3=0x%02x, P4=0x%02x\n",
+	      __func__, feature,
+	      parameters[0], parameters[1], parameters[2], parameters[3]);
+
+	/* issue the appropriate command + address */
+	chip->cmdfunc(mtd, NAND_CMD_SETFEATURES, feature, -1);
+
+	/* write the 4 parameters */
+	chip->write_buf(mtd, parameters, 4);
+
+	/* short delay */
+	ndelay(100);	/* tWB = 100ns */
+
+	/* wait until "SET FEATURES" command is processed */
+	if (!chip->dev_ready)
+		udelay(chip->chip_delay);
+	else
+		while (!chip->dev_ready(mtd))
+			;
+
+	return 0;
+}
+
+/*
+ * Micron 4-bit on-die ECC: enable/disable ECC Note, we use 'ecc.postpad' as a
+ * flag to indicate that on-die ECC is currently enabled; used by
+ * nand_command_lp() to check on-die ECC status after a read operation.
+ */
+static void nand_micron_4bit_ondie_ecc(struct mtd_info *mtd, int enable)
+{
+	struct nand_chip *chip = mtd->priv;
+	const uint8_t fp_ecc[2][4]  = {
+		{0x0, 0x0, 0x0, 0x0},
+		{0x8, 0x0, 0x0, 0x0}
+	};
+
+	BUG_ON(enable != 0 && enable != 1);
+
+	nand_set_features(mtd, NAND_FEATURE_MICRON_ARRAY_OP_MODE,
+			  fp_ecc[enable]);
+	chip->ecc.postpad = enable;
+}
+
 /*
  * Wait for the ready pin, after a command
  * The timeout is catched later.
@@ -587,23 +719,30 @@  static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 	if (column != -1 || page_addr != -1) {
 		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
 
-		/* Serially input address */
-		if (column != -1) {
-			/* Adjust columns for 16 bit buswidth */
-			if (chip->options & NAND_BUSWIDTH_16)
-				column >>= 1;
-			chip->cmd_ctrl(mtd, column, ctrl);
-			ctrl &= ~NAND_CTRL_CHANGE;
-			chip->cmd_ctrl(mtd, column >> 8, ctrl);
-		}
-		if (page_addr != -1) {
-			chip->cmd_ctrl(mtd, page_addr, ctrl);
-			chip->cmd_ctrl(mtd, page_addr >> 8,
-				       NAND_NCE | NAND_ALE);
-			/* One more address cycle for devices > 128MiB */
-			if (chip->chipsize > (128 << 20))
-				chip->cmd_ctrl(mtd, page_addr >> 16,
+		if (command == NAND_CMD_SETFEATURES ||
+		    command == NAND_CMD_GETFEATURES) {
+			/* Write Feature Address */
+			chip->cmd_ctrl(mtd, column & 0xff, ctrl);
+		} else {
+			/* Serially input address */
+			if (column != -1) {
+				/* Adjust columns for 16 bit buswidth */
+				if (chip->options & NAND_BUSWIDTH_16)
+					column >>= 1;
+				chip->cmd_ctrl(mtd, column, ctrl);
+				ctrl &= ~NAND_CTRL_CHANGE;
+				chip->cmd_ctrl(mtd, column >> 8, ctrl);
+			}
+			if (page_addr != -1) {
+				chip->cmd_ctrl(mtd, page_addr, ctrl);
+				chip->cmd_ctrl(mtd, page_addr >> 8,
 					       NAND_NCE | NAND_ALE);
+				/* One more address cycle for devices > 128MiB
+				 */
+				if (chip->chipsize > (128 << 20))
+					chip->cmd_ctrl(mtd, page_addr >> 16,
+						       NAND_NCE | NAND_ALE);
+			}
 		}
 	}
 	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
@@ -624,6 +763,13 @@  static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 	case NAND_CMD_DEPLETE1:
 		return;
 
+	case NAND_CMD_SETFEATURES:
+		ndelay(70);	/* tADL = 70ns */
+		return;
+
+	case NAND_CMD_GETFEATURES:
+		break;
+
 		/*
 		 * read error status commands require only a short delay
 		 */
@@ -668,6 +814,30 @@  static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
 			       NAND_NCE | NAND_CTRL_CHANGE);
 
+		/* If using 4-bit on-die ECC, check status for
+		 * correctable/uncorrectable ECC errors. (ecc.postpad is used as
+		 * a flag to indicate on-die ECC is currently enabled)
+		 */
+		if (chip->ecc.mode == NAND_ECC_4BITONDIE && chip->ecc.postpad) {
+			int status;
+
+			status = chip->waitfunc(mtd, chip);
+
+			if (status & NAND_STATUS_FAIL)
+				mtd->ecc_stats.failed++;
+			else if (status & NAND_STATUS_ECCREWRITE)
+				mtd->ecc_stats.corrected++;
+
+			/* Re-issue CMD0 after STATUS Check */
+			chip->cmd_ctrl(mtd, NAND_CMD_READ0,
+				       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+			chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+				       NAND_NCE | NAND_CTRL_CHANGE);
+
+			/* Device now ready for reading, return immediately */
+			return;
+		}
+
 		/* This applies to read commands */
 	default:
 		/*
@@ -1172,6 +1342,7 @@  int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	uint32_t readlen = ops->len;
 	uint32_t oobreadlen = ops->ooblen;
 	uint8_t *bufpoi, *oob, *buf;
+	int reenable_ondie_ecc = 0;
 
 	stats = mtd->ecc_stats;
 
@@ -1186,6 +1357,12 @@  int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	buf = ops->datbuf;
 	oob = ops->oobbuf;
 
+	/* For 'RAW' reads, disable on-die ECC if necessary */
+	if (ops->mode == MTD_OOB_RAW && chip->ecc.mode == NAND_ECC_4BITONDIE) {
+		nand_micron_4bit_ondie_ecc(mtd, 0);
+		reenable_ondie_ecc = 1;
+	}
+
 	while(1) {
 		bytes = min(mtd->writesize - col, readlen);
 		aligned = (bytes == mtd->writesize);
@@ -1282,6 +1459,10 @@  int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
 	if (oob)
 		ops->oobretlen = ops->ooblen - oobreadlen;
 
+	/* Re-enable on-die ECC if necessary */
+	if (reenable_ondie_ecc)
+		nand_micron_4bit_ondie_ecc(mtd, 1);
+
 	if (ret)
 		return ret;
 
@@ -1485,6 +1666,7 @@  int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 	int readlen = ops->ooblen;
 	int len;
 	uint8_t *buf = ops->oobbuf;
+	int reenable_ondie_ecc = 0;
 
 	DEBUG(MTD_DEBUG_LEVEL3, "%s: from = 0x%08Lx, len = %i\n",
 			__func__, (unsigned long long)from, readlen);
@@ -1516,6 +1698,12 @@  int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 	realpage = (int)(from >> chip->page_shift);
 	page = realpage & chip->pagemask;
 
+	/* Disable on-die ECC if necessary */
+	if (chip->ecc.mode == NAND_ECC_4BITONDIE) {
+		nand_micron_4bit_ondie_ecc(mtd, 0);
+		reenable_ondie_ecc = 1;
+	}
+
 	while(1) {
 		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
 
@@ -1557,6 +1745,10 @@  int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
 			sndcmd = 1;
 	}
 
+	/* Re-enable on-die ECC if necessary */
+	if (reenable_ondie_ecc)
+		nand_micron_4bit_ondie_ecc(mtd, 1);
+
 	ops->oobretlen = ops->ooblen;
 	return 0;
 }
@@ -1883,6 +2075,7 @@  int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 	uint8_t *oob = ops->oobbuf;
 	uint8_t *buf = ops->datbuf;
 	int ret, subpage;
+	int reenable_ondie_ecc = 0;
 
 	ops->retlen = 0;
 	if (!writelen)
@@ -1921,6 +2114,12 @@  int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 	if (likely(!oob))
 		memset(chip->oob_poi, 0xff, mtd->oobsize);
 
+	/* For 'RAW' writes, disable on-die ECC if necessary */
+	if (ops->mode == MTD_OOB_RAW && chip->ecc.mode == NAND_ECC_4BITONDIE) {
+		nand_micron_4bit_ondie_ecc(mtd, 0);
+		reenable_ondie_ecc = 1;
+	}
+
 	while(1) {
 		int bytes = mtd->writesize;
 		int cached = writelen > bytes && page != blockmask;
@@ -1961,6 +2160,10 @@  int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
 		}
 	}
 
+	/* Re-enable on-die ECC if necessary */
+	if (reenable_ondie_ecc)
+		nand_micron_4bit_ondie_ecc(mtd, 1);
+
 	ops->retlen = ops->len - writelen;
 	if (unlikely(oob))
 		ops->oobretlen = ops->ooblen;
@@ -2018,6 +2221,7 @@  int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 {
 	int chipnr, page, status, len;
 	struct nand_chip *chip = mtd->priv;
+	int reenable_ondie_ecc = 0;
 
 	DEBUG(MTD_DEBUG_LEVEL3, "%s: to = 0x%08x, len = %i\n",
 			 __func__, (unsigned int)to, (int)ops->ooblen);
@@ -2072,11 +2276,21 @@  int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
 	if (page == chip->pagebuf)
 		chip->pagebuf = -1;
 
+	/* Disable on-die ECC */
+	if (chip->ecc.mode == NAND_ECC_4BITONDIE) {
+		nand_micron_4bit_ondie_ecc(mtd, 0);
+		reenable_ondie_ecc = 1;
+	}
+
 	memset(chip->oob_poi, 0xff, mtd->oobsize);
 	nand_fill_oob(chip, ops->oobbuf, ops);
 	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
 	memset(chip->oob_poi, 0xff, mtd->oobsize);
 
+	/* Re-enable on-die ECC if necessary */
+	if (reenable_ondie_ecc)
+		nand_micron_4bit_ondie_ecc(mtd, 1);
+
 	if (status)
 		return status;
 
@@ -2566,6 +2780,18 @@  static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
 		/* Get buswidth information */
 		busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
 
+		/* Micron device: check for 4-bit on-die ECC */
+		if (*maf_id == NAND_MFR_MICRON) {
+			u8 id4, id5;
+			id4 = chip->read_byte(mtd);
+			id5 = chip->read_byte(mtd);
+
+			/* Do we have a 5-byte ID ? */
+			if (!(id4 == *maf_id && id5 == dev_id))
+				/* ECC level in id4[1:0] */
+				if ((id4 & 0x3) == 0x2)
+					chip->ecc.mode = NAND_ECC_4BITONDIE;
+		}
 	} else {
 		/*
 		 * Old devices have chip data hardcoded in the device id table
@@ -2730,7 +2956,10 @@  int nand_scan_tail(struct mtd_info *mtd)
 			chip->ecc.layout = &nand_oob_16;
 			break;
 		case 64:
-			chip->ecc.layout = &nand_oob_64;
+			if (chip->ecc.mode == NAND_ECC_4BITONDIE)
+				chip->ecc.layout = &nand_oob_64_4bitondie;
+			else
+				chip->ecc.layout = &nand_oob_64;
 			break;
 		case 128:
 			chip->ecc.layout = &nand_oob_128;
@@ -2837,6 +3066,20 @@  int nand_scan_tail(struct mtd_info *mtd)
 		chip->ecc.bytes = 0;
 		break;
 
+	case NAND_ECC_4BITONDIE:
+		chip->ecc.read_page = nand_read_page_raw;
+		chip->ecc.write_page = nand_write_page_raw;
+		chip->ecc.read_page_raw = nand_read_page_raw;
+		chip->ecc.write_page_raw = nand_write_page_raw;
+		chip->ecc.read_oob = nand_read_oob_std;
+		chip->ecc.write_oob = nand_write_oob_std;
+		chip->ecc.size = 512;
+		chip->ecc.bytes = 8;
+
+		/* Turn on on-die ECC */
+		nand_micron_4bit_ondie_ecc(mtd, 1);
+		break;
+
 	default:
 		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
 		       chip->ecc.mode);
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index 6b1942b..fee510a 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -1165,6 +1165,27 @@  static struct nand_bbt_descr bbt_mirror_descr = {
 	.pattern = mirror_pattern
 };
 
+/* BBT descriptors for (Micron) 4-bit on-die ECC */
+static struct nand_bbt_descr bbt_main_descr_ode = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+	| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8 + 8,		/* need to shift by 8 due to on-die ECC */
+	.len = 4,
+	.veroffs = 12 + 8,	/* need to shift by 8 due to on-die ECC */
+	.maxblocks = 4,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr_ode = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8 + 8,		/* need to shift by 8 due to on-die ECC */
+	.len = 4,
+	.veroffs = 12 + 8,	/* need to shift by 8 due to on-die ECC */
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
 /**
  * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
  * @mtd:	MTD device structure
@@ -1198,8 +1219,13 @@  int nand_default_bbt(struct mtd_info *mtd)
 	if (this->options & NAND_USE_FLASH_BBT) {
 		/* Use the default pattern descriptors */
 		if (!this->bbt_td) {
-			this->bbt_td = &bbt_main_descr;
-			this->bbt_md = &bbt_mirror_descr;
+			if (this->ecc.mode == NAND_ECC_4BITONDIE) {
+				this->bbt_td = &bbt_main_descr_ode;
+				this->bbt_md = &bbt_mirror_descr_ode;
+			} else {
+				this->bbt_td = &bbt_main_descr;
+				this->bbt_md = &bbt_mirror_descr;
+			}
 		}
 		if (!this->badblock_pattern) {
 			this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 0471f01..67144b7 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -80,6 +80,8 @@  extern void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_READID		0x90
 #define NAND_CMD_ERASE2		0xd0
 #define NAND_CMD_RESET		0xff
+#define NAND_CMD_SETFEATURES    0xef
+#define NAND_CMD_GETFEATURES    0xee
 
 /* Extended commands for large page devices */
 #define NAND_CMD_READSTART	0x30
@@ -107,12 +109,16 @@  extern void nand_wait_ready(struct mtd_info *mtd);
 
 #define NAND_CMD_NONE		-1
 
+/* Feature Addresses (for the "SET/GET FEATURES" commands) */
+#define NAND_FEATURE_MICRON_ARRAY_OP_MODE	0x90
+
 /* Status bits */
 #define NAND_STATUS_FAIL	0x01
 #define NAND_STATUS_FAIL_N1	0x02
 #define NAND_STATUS_TRUE_READY	0x20
 #define NAND_STATUS_READY	0x40
 #define NAND_STATUS_WP		0x80
+#define NAND_STATUS_ECCREWRITE	0x08
 
 /*
  * Constants for ECC_MODES
@@ -123,6 +129,7 @@  typedef enum {
 	NAND_ECC_HW,
 	NAND_ECC_HW_SYNDROME,
 	NAND_ECC_HW_OOB_FIRST,
+	NAND_ECC_4BITONDIE,
 } nand_ecc_modes_t;
 
 /*