commit 305ef1ea077963ee5664214da6622a8cabe4e143
Author: Cliff Brake <cbrake@bec-systems.com>
Date:   Wed Jan 19 16:14:32 2011 -0500

    enable Micron ECC engine

diff --git a/arch/arm/cpu/arm_cortexa8/omap3/board.c b/arch/arm/cpu/arm_cortexa8/omap3/board.c
index 7b78fa4..846d2c4 100644
--- a/arch/arm/cpu/arm_cortexa8/omap3/board.c
+++ b/arch/arm/cpu/arm_cortexa8/omap3/board.c
@@ -319,9 +319,11 @@ static int do_switch_ecc(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 	if (argc != 2)
 		goto usage;
 	if (strncmp(argv[1], "hw", 2) == 0)
-		omap_nand_switch_ecc(1);
+		omap_nand_switch_ecc(NAND_ECC_HW);
 	else if (strncmp(argv[1], "sw", 2) == 0)
-		omap_nand_switch_ecc(0);
+		omap_nand_switch_ecc(NAND_ECC_SOFT);
+	else if (strncmp(argv[1], "micron", 2) == 0)
+		omap_nand_switch_ecc(NAND_ECC_MICRON);
 	else
 		goto usage;
 
@@ -335,7 +337,7 @@ usage:
 U_BOOT_CMD(
 	nandecc, 2, 1,	do_switch_ecc,
 	"switch OMAP3 NAND ECC calculation algorithm",
-	"[hw/sw] - Switch between NAND hardware (hw) or software (sw) ecc algorithm"
+	"[hw/sw/micron] - Switch between NAND hardware (hw), software (sw), or micron (internal nand) ecc algorithm"
 );
 
 #endif /* CONFIG_NAND_OMAP_GPMC */
diff --git a/arch/arm/include/asm/arch-omap3/sys_proto.h b/arch/arm/include/asm/arch-omap3/sys_proto.h
index 34bd515..1ba750b 100644
--- a/arch/arm/include/asm/arch-omap3/sys_proto.h
+++ b/arch/arm/include/asm/arch-omap3/sys_proto.h
@@ -21,6 +21,8 @@
 #ifndef _SYS_PROTO_H_
 #define _SYS_PROTO_H_
 
+#include <nand.h>
+
 typedef struct {
 	u32 mtype;
 	char *board_string;
@@ -62,7 +64,7 @@ void sr32(void *, u32, u32, u32);
 u32 wait_on_value(u32, u32, void *, u32);
 void sdelay(unsigned long);
 void make_cs1_contiguous(void);
-void omap_nand_switch_ecc(int);
+void omap_nand_switch_ecc(nand_ecc_modes_t ecc_mode);
 void power_init_r(void);
 void dieid_num_r(void);
 
diff --git a/board/levelstar/antero/antero.c b/board/levelstar/antero/antero.c
index 57e36e4..16a1be3 100644
--- a/board/levelstar/antero/antero.c
+++ b/board/levelstar/antero/antero.c
@@ -53,6 +53,7 @@ int board_init(void)
 	/* boot param addr */
 	gd->bd->bi_boot_params = (OMAP34XX_SDRC_CS0 + 0x100);
 
+
 	return 0;
 }
 
@@ -147,6 +148,8 @@ int misc_init_r(void)
 		printf("CODEC_MODE = 0x%x\n", val);
 	}
 
+	omap_nand_switch_ecc(NAND_ECC_MICRON);
+
 	return 0;
 }
 
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 7171bdd..35e15e5 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -120,6 +120,23 @@ static struct nand_ecclayout nand_oob_64 = {
 		 .length = 38}}
 };
 
+/* MICRON: modified OOB Layout */
+static struct nand_ecclayout nand_oob_64_micron = {
+	.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 = 4, .length = 4 },
+		{ .offset = 20, .length = 4 },
+		{ .offset = 36, .length = 4 },
+		{ .offset = 52, .length = 4 },
+	},
+};
+
 static struct nand_ecclayout nand_oob_128 = {
 	.eccbytes = 48,
 	.eccpos = {
@@ -660,12 +677,16 @@ static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
 
 		/* 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 (command == NAND_CMD_GET_FEATURES || command == NAND_CMD_SET_FEATURES) {
+				chip->cmd_ctrl(mtd, column, ctrl);
+			} else {
+				/* 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);
@@ -732,6 +753,24 @@ 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 (chip->ecc.mode == NAND_ECC_MICRON) {
+			udelay(chip->chip_delay);
+			
+			chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+			if(chip->read_byte(mtd) & 0x01){/* MICRON: check error bit */
+				printf("ERROR: ON-DIE ECC READ FAILURE\n");
+				// do something here to handle the failure.
+			}
+
+			/* MICRON: restore to data out */
+			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);
+
+			//return;   // (not sure if this is needed)
+		}
+
 		/* This applies to read commands */
 	default:
 		/*
@@ -2847,7 +2886,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_MICRON)
+				chip->ecc.layout = &nand_oob_64_micron;
+			else
+				chip->ecc.layout = &nand_oob_64;
 			break;
 		case 128:
 			chip->ecc.layout = &nand_oob_128;
@@ -2939,6 +2981,17 @@ int nand_scan_tail(struct mtd_info *mtd)
 		chip->ecc.bytes = 3;
 		break;
 
+	case NAND_ECC_MICRON:
+		chip->ecc.read_page = nand_read_page_raw;
+		chip->ecc.write_page = nand_write_page_raw;
+		chip->ecc.read_oob = nand_read_oob_std;
+		chip->ecc.read_page_raw = nand_read_page_raw;
+		chip->ecc.write_page_raw = nand_write_page_raw;
+		chip->ecc.write_oob = nand_write_oob_std;
+		chip->ecc.size = mtd->writesize;
+		chip->ecc.bytes = 0;
+		break;
+
 	case NAND_ECC_NONE:
 		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
 		       "This is not recommended !!\n");
diff --git a/drivers/mtd/nand/omap_gpmc.c b/drivers/mtd/nand/omap_gpmc.c
index 99b9cef..25a626d 100644
--- a/drivers/mtd/nand/omap_gpmc.c
+++ b/drivers/mtd/nand/omap_gpmc.c
@@ -224,14 +224,57 @@ static void omap_enable_hwecc(struct mtd_info *mtd, int32_t mode)
 	}
 }
 
+static int test_on_die_ecc(struct mtd_info * mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	int addr = 0x90;
+	uint8_t data;
+
+	chip->cmdfunc(mtd, NAND_CMD_GET_FEATURES, addr, -1);
+
+	ndelay(1000);
+
+	data = chip->read_byte(mtd);
+
+	return ((data & 0x08) ? 1 : 0);
+}
+
+static void omap_nand_enable_micron_ecc(struct mtd_info * mtd, int enable)
+{
+	struct nand_chip *chip = mtd->priv;
+	int i, addr = 0x90;
+
+	if (enable)
+		printf("Enabling Micron ECC\n");
+	else 
+		printf("Disabling Micron ECC\n");
+
+	chip->cmdfunc(mtd, NAND_CMD_SET_FEATURES, addr, -1);
+
+	u8 data_out[8];
+
+	if (enable) 
+		data_out[0] = 0x8;
+
+	chip->write_buf(mtd, data_out, sizeof(data_out));
+
+	udelay(1000);
+
+	int enabled = test_on_die_ecc(mtd);
+	if (enable != enabled) {
+		printf("Warning: Micron ECC change did not stick, expected %i, got %i\n",
+				enable, enabled);
+	}
+}
+
 /*
  * omap_nand_switch_ecc - switch the ECC operation b/w h/w ecc and s/w ecc.
  * The default is to come up on s/w ecc
  *
- * @hardware - 1 -switch to h/w ecc, 0 - s/w ecc
+ * @ecc_mode ECC mode to use (NAND_ECC_HW, NAND_ECC_SOFT, NAND_ECC_MICRON)
  *
  */
-void omap_nand_switch_ecc(int32_t hardware)
+void omap_nand_switch_ecc(nand_ecc_modes_t ecc_mode)
 {
 	struct nand_chip *nand;
 	struct mtd_info *mtd;
@@ -257,8 +300,8 @@ void omap_nand_switch_ecc(int32_t hardware)
 	nand->ecc.correct = NULL;
 	nand->ecc.calculate = NULL;
 
-	/* Setup the ecc configurations again */
-	if (hardware) {
+	switch (ecc_mode) {
+	case NAND_ECC_HW: {
 		nand->ecc.mode = NAND_ECC_HW;
 		nand->ecc.layout = &hw_nand_oob;
 		nand->ecc.size = 512;
@@ -267,12 +310,29 @@ void omap_nand_switch_ecc(int32_t hardware)
 		nand->ecc.correct = omap_correct_data;
 		nand->ecc.calculate = omap_calculate_ecc;
 		omap_hwecc_init(nand);
+		omap_nand_enable_micron_ecc(mtd, 0);
 		printf("HW ECC selected\n");
-	} else {
+		break;
+	}
+	case NAND_ECC_SOFT:{
 		nand->ecc.mode = NAND_ECC_SOFT;
 		/* Use mtd default settings */
 		nand->ecc.layout = NULL;
+		omap_nand_enable_micron_ecc(mtd, 0);
 		printf("SW ECC selected\n");
+		break;
+	}
+	case NAND_ECC_MICRON: {
+		nand->ecc.mode = NAND_ECC_MICRON;
+		/* Use mtd default settings */
+		nand->ecc.layout = NULL;
+		omap_nand_enable_micron_ecc(mtd, 1);
+		printf("Micron ECC selected\n");
+		break;
+	}
+	default:{
+		printf("Error, ECC mode %i not handled\n", ecc_mode);
+	}
 	}
 
 	/* Update NAND handling after ECC mode switch */
diff --git a/include/configs/omap3_antero.h b/include/configs/omap3_antero.h
index ec42bf1..4280b5c 100644
--- a/include/configs/omap3_antero.h
+++ b/include/configs/omap3_antero.h
@@ -128,6 +128,9 @@
 					"1920k(u-boot),128k(u-boot-env),"\
 					"4m(kernel),-(fs)"
 
+#define CONFIG_MTD_DEBUG
+#define CONFIG_MTD_DEBUG_VERBOSE 0
+
 #define CONFIG_CMD_I2C		/* I2C serial bus support	*/
 #define CONFIG_CMD_MMC		/* MMC support			*/
 #define CONFIG_CMD_NAND		/* NAND support			*/
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 94ad0c0..04a090b 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -85,6 +85,8 @@ extern void nand_wait_ready(struct mtd_info *mtd);
 #define NAND_CMD_RNDIN		0x85
 #define NAND_CMD_READID		0x90
 #define NAND_CMD_ERASE2		0xd0
+#define NAND_CMD_GET_FEATURES	0xee
+#define NAND_CMD_SET_FEATURES	0xef
 #define NAND_CMD_RESET		0xff
 
 /* Extended commands for large page devices */
@@ -129,6 +131,7 @@ typedef enum {
 	NAND_ECC_HW,
 	NAND_ECC_HW_SYNDROME,
 	NAND_ECC_HW_OOB_FIRST,
+	NAND_ECC_MICRON,
 } nand_ecc_modes_t;
 
 /*
