Merge "Add interleave support in android boot loader nand driver."
diff --git a/platform/msm_shared/include/nand.h b/platform/msm_shared/include/nand.h
index bf15f6a..1b6b2a6 100644
--- a/platform/msm_shared/include/nand.h
+++ b/platform/msm_shared/include/nand.h
@@ -34,7 +34,16 @@
 #else
 #define MSM_NAND_BASE 0xA0A00000
 #endif
-/* see 80-VA736-2 C pp 354-414 */
+
+#define MSM_NAND_NC01_BASE 	0xA0240000
+#define MSM_NAND_NC10_BASE 	0xA0280000
+#define MSM_NAND_NC11_BASE 	0xA02C0000
+#define EBI2_REG_BASE 		0xA0000000
+
+#define NC01(off) (MSM_NAND_NC01_BASE + (off))
+#define NC10(off) (MSM_NAND_NC10_BASE + (off))
+#define NC11(off) (MSM_NAND_NC11_BASE + (off))
+#define EBI2_REG(off) (EBI2_REG_BASE + (off))
 
 #define NAND_REG(off) (MSM_NAND_BASE + (off))
 
@@ -187,4 +196,8 @@
 #define FLASH_16BIT_NAND_DEVICE     0x02
 #define FLASH_ONENAND_DEVICE        0x03
 
+#define EBI2_CFG_REG		       	EBI2_REG(0x0004)
+#define EBI2_NAND_ADM_MUX       	EBI2_REG(0x005C)
+#define EBI2_CHIP_SELECT_CFG0		EBI2_REG(0x0000)
+
 #endif /* __PLATFORM_MSM_SHARED_NAND_H */
diff --git a/platform/msm_shared/nand.c b/platform/msm_shared/nand.c
index 40b080c..1a9bc28 100755
--- a/platform/msm_shared/nand.c
+++ b/platform/msm_shared/nand.c
@@ -310,6 +310,174 @@
 	return 0;
 }
 
+static int flash_nand_block_isbad_interleave(dmov_s *cmdlist, unsigned *ptrlist,
+								  unsigned page)
+{
+	dmov_s *cmd = cmdlist;
+	unsigned *ptr = ptrlist;
+	unsigned *data = ptrlist + 4;
+	char buf01[4];
+	char buf10[4];
+	unsigned cwperpage;
+
+	cwperpage = ((flash_pagesize >> 1)>> 9);
+
+	/* Check first page of this block */
+	if(page & 63)
+		page = page - (page & 63);
+
+	/* Check bad block marker */
+	data[0] = NAND_CMD_PAGE_READ;	/* command */
+
+	/* addr0 */
+	if (CFG1 & CFG1_WIDE_FLASH)
+		data[1] = (page << 16) | ((528*(cwperpage-1)) >> 1);
+	else
+		data[1] = (page << 16) | (528*(cwperpage-1));
+
+	data[2] = (page >> 16) & 0xff;				/* addr1	*/
+	data[3] = 0 | 4;					/* chipsel CS0	*/
+	data[4] = 0 | 5;					/* chipsel CS1	*/
+	data[5] = NAND_CFG0_RAW & ~(7U << 6);			/* cfg0		*/
+	data[6] = NAND_CFG1_RAW | (CFG1 & CFG1_WIDE_FLASH);	/* cfg1		*/
+	data[7] = 1;
+	data[8] = CLEAN_DATA_32;	/* NC01 flash status */
+	data[9] = CLEAN_DATA_32;	/* NC01 buf01 status   */
+	data[10] = CLEAN_DATA_32;	/* NC10 flash status */
+	data[11] = CLEAN_DATA_32;	/* NC10 buf10 status   */
+	data[12] = 0x00000A3C; 		/* adm_mux_data_ack_req_nc01 */
+	data[13] = 0x0000053C; 		/* adm_mux_cmd_ack_req_nc01  */
+	data[14] = 0x00000F28; 		/* adm_mux_data_ack_req_nc10 */
+	data[15] = 0x00000F14; 		/* adm_mux_cmd_ack_req_nc10  */
+	data[16] = 0x00000FC0; 		/* adm_default_mux */
+	data[17] = 0x00000805;		/* enable CS1 */
+	data[18] = 0x00000801;		/* disable CS1 */
+
+	/* enable CS1 */
+	cmd[0].cmd = 0;
+	cmd[0].src = paddr(data[17]);
+	cmd[0].dst = EBI2_CHIP_SELECT_CFG0;
+	cmd[0].len = 4;
+
+	/* Reading last code word from NC01 */
+	/* 0xF14 */
+	cmd[1].cmd = 0;
+	cmd[1].src = paddr(data[15]);
+	cmd[1].dst = EBI2_NAND_ADM_MUX;
+	cmd[1].len = 4;
+
+	cmd[2].cmd = DST_CRCI_NAND_CMD;
+	cmd[2].src = paddr(&data[0]);
+	cmd[2].dst = NC01(NAND_FLASH_CMD);
+	cmd[2].len = 16;
+
+	cmd[3].cmd = 0;
+	cmd[3].src = paddr(&data[5]);
+	cmd[3].dst = NC01(NAND_DEV0_CFG0);
+	cmd[3].len = 8;
+
+	cmd[4].cmd = 0;
+	cmd[4].src = paddr(&data[7]);
+	cmd[4].dst = NC01(NAND_EXEC_CMD);
+	cmd[4].len = 4;
+
+	/* 0xF28 */
+	cmd[5].cmd = 0;
+	cmd[5].src = paddr(data[14]);
+	cmd[5].dst = EBI2_NAND_ADM_MUX;
+	cmd[5].len = 4;
+
+	cmd[6].cmd = SRC_CRCI_NAND_DATA;
+	cmd[6].src = NC01(NAND_FLASH_STATUS);
+	cmd[6].dst = paddr(&data[8]);
+	cmd[6].len = 8;
+
+	cmd[7].cmd = 0;
+	cmd[7].src = NC01(NAND_FLASH_BUFFER) + (flash_pagesize - (528*(cwperpage-1)));
+	cmd[7].dst = paddr(&buf01);
+	cmd[7].len = 4;
+
+	/* Reading last code word from NC10 */
+	/* 0x53C */
+	cmd[8].cmd = 0;
+	cmd[8].src = paddr(data[13]);
+	cmd[8].dst = EBI2_NAND_ADM_MUX;
+	cmd[8].len = 4;
+
+	cmd[9].cmd = DST_CRCI_NAND_CMD;
+	cmd[9].src = paddr(&data[0]);
+	cmd[9].dst = NC10(NAND_FLASH_CMD);
+	cmd[9].len = 12;
+
+	cmd[10].cmd = 0;
+	cmd[10].src = paddr(&data[4]);
+	cmd[10].dst = NC10(NAND_FLASH_CHIP_SELECT);
+	cmd[10].len = 4;
+
+	cmd[11].cmd = 0;
+	cmd[11].src = paddr(&data[5]);
+	cmd[11].dst = NC10(NAND_DEV1_CFG0);
+	cmd[11].len = 8;
+
+	cmd[12].cmd = 0;
+	cmd[12].src = paddr(&data[7]);
+	cmd[12].dst = NC10(NAND_EXEC_CMD);
+	cmd[12].len = 4;
+
+	/* 0xA3C */
+	cmd[13].cmd = 0;
+	cmd[13].src = paddr(data[12]);
+	cmd[13].dst = EBI2_NAND_ADM_MUX;
+	cmd[13].len = 4;
+
+	cmd[14].cmd = SRC_CRCI_NAND_DATA;
+	cmd[14].src = NC10(NAND_FLASH_STATUS);
+	cmd[14].dst = paddr(&data[10]);
+	cmd[14].len = 8;
+
+	cmd[15].cmd = 0;
+	cmd[15].src = NC10(NAND_FLASH_BUFFER) + (flash_pagesize - (528*(cwperpage-1)));
+	cmd[15].dst = paddr(&buf10);
+	cmd[15].len = 4;
+
+	cmd[16].cmd = 0;
+	cmd[16].src = paddr(&data[16]);
+	cmd[16].dst = EBI2_NAND_ADM_MUX;
+	cmd[16].len = 4;
+
+	/* setting default value */
+	cmd[17].cmd = CMD_OCU | CMD_LC;
+	cmd[17].src = paddr(&data[18]);
+	cmd[17].dst = EBI2_CHIP_SELECT_CFG0;
+	cmd[17].len = 4;
+
+	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
+
+	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
+
+#if VERBOSE
+	dprintf(INFO, "NC01 status: %x\n", data[8]);
+	dprintf(INFO, "NC10 status: %x\n", data[10]);
+#endif
+
+	/* we fail if there was an operation error, a mpu error, or the
+	** erase success bit was not set.
+	*/
+	if((data[8] & 0x110) || (data[10] & 0x110)) return -1;
+
+	/* Check for bad block marker byte */
+	if (CFG1 & CFG1_WIDE_FLASH) {
+		if ((buf01[0] != 0xFF || buf01[1] != 0xFF) ||
+			(buf10[0] != 0xFF || buf10[1] != 0xFF))
+			return 1;
+		} else {
+			if (buf01[0] != 0xFF || buf10[0] != 0xFF)
+			return 1;
+		}
+
+	return 0;
+}
+
 static int flash_nand_erase_block(dmov_s *cmdlist, unsigned *ptrlist,
 								  unsigned page)
 {
@@ -388,6 +556,152 @@
 	return 0;
 }
 
+static int flash_nand_erase_block_interleave(dmov_s *cmdlist, unsigned *ptrlist,
+								  unsigned page)
+{
+	dmov_s *cmd = cmdlist;
+	unsigned *ptr = ptrlist;
+	unsigned *data = ptrlist + 4;
+	int isbad = 0;
+
+	/* only allow erasing on block boundaries */
+	if(page & 63) return -1;
+
+	/* Check for bad block and erase only if block is not marked bad */
+	isbad = flash_nand_block_isbad(cmdlist, ptrlist, page);
+
+	if (isbad) {
+		dprintf(INFO, "skipping @ %d (bad block)\n", page >> 6);
+		return -1;
+	}
+
+	/* Erase block */
+	data[0] = NAND_CMD_BLOCK_ERASE;
+	data[1] = page;
+	data[2] = 0;
+	data[3] = 0 | 4;	/* chipselect CS0 */
+	data[4] = 0 | 5;	/* chipselect CS1 */
+	data[5] = 1;
+	data[6] = 0xeeeeeeee;
+	data[7] = 0xeeeeeeee;
+	data[8] = CFG0 & (~(7 << 6));  /* CW_PER_PAGE = 0 */
+	data[9] = CFG1;
+	data[10] = 0x00000A3C; 	/* adm_mux_data_ack_req_nc01 */
+	data[11] = 0x0000053C; 	/* adm_mux_cmd_ack_req_nc01  */
+	data[12] = 0x00000F28; 	/* adm_mux_data_ack_req_nc10 */
+	data[13] = 0x00000F14; 	/* adm_mux_cmd_ack_req_nc10  */
+	data[14] = 0x00000FC0; 	/* adm_default_mux */
+	data[15] = 0x00000805;	/* enable CS1 */
+	data[16] = 0x00000801;	/* disable CS1 */
+
+	/* enable CS1 */
+	cmd[0].cmd = 0 | CMD_OCB;
+	cmd[0].src = paddr(data[15]);
+	cmd[0].dst = EBI2_CHIP_SELECT_CFG0;
+	cmd[0].len = 4;
+
+	/* Reading last code word from NC01 */
+	/* 0xF14 */
+	cmd[1].cmd = 0;
+	cmd[1].src = paddr(data[13]);
+	cmd[1].dst = EBI2_NAND_ADM_MUX;
+	cmd[1].len = 4;
+
+	cmd[2].cmd = DST_CRCI_NAND_CMD;
+	cmd[2].src = paddr(&data[0]);
+	cmd[2].dst = NC01(NAND_FLASH_CMD);
+	cmd[2].len = 16;
+
+	cmd[3].cmd = 0;
+	cmd[3].src = paddr(&data[8]);
+	cmd[3].dst = NC01(NAND_DEV0_CFG0);
+	cmd[3].len = 8;
+
+	cmd[4].cmd = 0;
+	cmd[4].src = paddr(&data[5]);
+	cmd[4].dst = NC01(NAND_EXEC_CMD);
+	cmd[4].len = 4;
+
+	/* 0xF28 */
+	cmd[5].cmd = 0;
+	cmd[5].src = paddr(data[12]);
+	cmd[5].dst = EBI2_NAND_ADM_MUX;
+	cmd[5].len = 4;
+
+	cmd[6].cmd = SRC_CRCI_NAND_DATA;
+	cmd[6].src = NC01(NAND_FLASH_STATUS);
+	cmd[6].dst = paddr(&data[6]);
+	cmd[6].len = 4;
+
+	/* Reading last code word from NC10 */
+	/* 0x53C */
+	cmd[7].cmd = 0;
+	cmd[7].src = paddr(data[11]);
+	cmd[7].dst = EBI2_NAND_ADM_MUX;
+	cmd[7].len = 4;
+
+	cmd[8].cmd = DST_CRCI_NAND_CMD;
+	cmd[8].src = paddr(&data[0]);
+	cmd[8].dst = NC10(NAND_FLASH_CMD);
+	cmd[8].len = 12;
+
+	cmd[9].cmd = 0;
+	cmd[9].src = paddr(&data[4]);
+	cmd[9].dst = NC10(NAND_FLASH_CHIP_SELECT);
+	cmd[9].len = 4;
+
+	cmd[10].cmd = 0;
+	cmd[10].src = paddr(&data[8]);
+	cmd[10].dst = NC10(NAND_DEV1_CFG0);
+	cmd[10].len = 8;
+
+	cmd[11].cmd = 0;
+	cmd[11].src = paddr(&data[5]);
+	cmd[11].dst = NC10(NAND_EXEC_CMD);
+	cmd[11].len = 4;
+
+	/* 0xA3C */
+	cmd[12].cmd = 0;
+	cmd[12].src = paddr(data[10]);
+	cmd[12].dst = EBI2_NAND_ADM_MUX;
+	cmd[12].len = 4;
+
+	cmd[13].cmd = SRC_CRCI_NAND_DATA;
+	cmd[13].src = NC10(NAND_FLASH_STATUS);
+	cmd[13].dst = paddr(&data[7]);
+	cmd[13].len = 4;
+
+	/* adm default mux state */
+	/* 0xFCO */
+	cmd[14].cmd = 0;
+	cmd[14].src = paddr(data[14]);
+	cmd[14].dst = EBI2_NAND_ADM_MUX;
+	cmd[14].len = 4;
+
+	/* disable CS1 */
+	cmd[15].cmd = CMD_OCU | CMD_LC;
+	cmd[15].src = paddr(data[16]);
+	cmd[15].dst = EBI2_CHIP_SELECT_CFG0;
+	cmd[15].len = 4;
+
+	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
+
+	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
+
+#if VERBOSE
+	dprintf(INFO, "NC01 status: %x\n", data[6]);
+	dprintf(INFO, "NC10 status: %x\n", data[7]);
+#endif
+
+	/* we fail if there was an operation error, a mpu error, or the
+	 ** erase success bit was not set.
+	 */
+	if(data[6] & 0x110 || data[7] & 0x110) return -1;
+	if(!(data[6] & 0x80) || !(data[7] & 0x80)) return -1;
+
+	return 0;
+}
+
 struct data_flash_io {
 	unsigned cmd;
 	unsigned addr0;
@@ -406,6 +720,29 @@
 	} result[8];
 };
 
+struct interleave_data_flash_io {
+	uint32_t cmd;
+	uint32_t addr0;
+	uint32_t addr1;
+	uint32_t chipsel_cs0;
+	uint32_t chipsel_cs1;
+	uint32_t cfg0;
+	uint32_t cfg1;
+	uint32_t exec;
+	uint32_t ecc_cfg;
+	uint32_t ecc_cfg_save;
+	uint32_t ebi2_chip_select_cfg0;
+	uint32_t adm_mux_data_ack_req_nc01;
+	uint32_t adm_mux_cmd_ack_req_nc01;
+	uint32_t adm_mux_data_ack_req_nc10;
+	uint32_t adm_mux_cmd_ack_req_nc10;
+	uint32_t adm_default_mux;
+	uint32_t default_ebi2_chip_select_cfg0;
+	struct {
+		uint32_t flash_status;
+	} result[16];
+};
+
 static int _flash_nand_read_page(dmov_s *cmdlist, unsigned *ptrlist,
 								 unsigned page, void *_addr, void *_spareaddr)
 {
@@ -529,6 +866,372 @@
 	return 0;
 }
 
+static int flash_nand_read_page_interleave(dmov_s *cmdlist, unsigned *ptrlist,
+								 unsigned page, void *_addr, void *_spareaddr)
+{
+	dmov_s *cmd = cmdlist;
+	unsigned *ptr = ptrlist;
+	struct interleave_data_flash_io *data = (void*) (ptrlist + 4);
+	unsigned addr = (unsigned) _addr;
+	unsigned spareaddr = (unsigned) _spareaddr;
+	unsigned n;
+	int isbad = 0;
+	unsigned cwperpage;
+	cwperpage = (flash_pagesize >> 9);
+
+	/* Check for bad block and read only from a good block */
+	isbad = flash_nand_block_isbad(cmdlist, ptrlist, page);
+	if (isbad)
+		return -2;
+
+	data->cmd = NAND_CMD_PAGE_READ_ECC;
+	data->addr0 = page << 16;
+	data->addr1 = (page >> 16) & 0xff;
+	data->chipsel_cs0 = 0 | 4; /* flash0 + undoc bit */
+	data->chipsel_cs1 = 0 | 5; /* flash0 + undoc bit */
+	data->ebi2_chip_select_cfg0 = 0x00000805;
+	data->adm_mux_data_ack_req_nc01 = 0x00000A3C;
+	data->adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+	data->adm_mux_data_ack_req_nc10 = 0x00000F28;
+	data->adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+	data->adm_default_mux = 0x00000FC0;
+	data->default_ebi2_chip_select_cfg0 = 0x00000801;
+
+	/* GO bit for the EXEC register */
+	data->exec = 1;
+
+	data->cfg0 = CFG0;
+	data->cfg1 = CFG1;
+
+	data->ecc_cfg = 0x203;
+
+	for (n = 0; n < cwperpage; n++) {
+		/* flash + buffer status return words */
+		data->result[n].flash_status = 0xeeeeeeee;
+
+		if (n == 0) {
+			/* enable CS1 */
+			cmd->cmd = CMD_OCB;
+			cmd->src = paddr(&data->ebi2_chip_select_cfg0);
+			cmd->dst = EBI2_CHIP_SELECT_CFG0;
+			cmd->len = 4;
+			cmd++;
+
+			/* save existing ecc config */
+			cmd->cmd = 0;
+			cmd->src = NAND_EBI2_ECC_BUF_CFG;
+			cmd->dst = paddr(&data->ecc_cfg_save);
+			cmd->len = 4;
+			cmd++;
+
+			/* NC01, NC10 --> ADDR0/ADDR1 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->addr0);
+			cmd->dst = NC11(NAND_ADDR0);
+			cmd->len = 8;
+			cmd++;
+
+			/* Select the CS0,
+			 * for NC01!
+			 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->chipsel_cs0);
+			cmd->dst = NC01(NAND_FLASH_CHIP_SELECT);
+			cmd->len = 4;
+			cmd++;
+
+			/* Select the CS1,
+			 * for NC10!
+			 */
+			cmd->cmd = 0;
+			cmd->src =  paddr(&data->chipsel_cs1);
+			cmd->dst = NC10(NAND_FLASH_CHIP_SELECT);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->cfg0);
+			cmd->dst = NC01(NAND_DEV0_CFG0);
+			cmd->len = 8;
+			cmd++;
+
+			/* config DEV1 for CS1 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->cfg0);
+			cmd->dst = NC10(NAND_DEV1_CFG0);
+			cmd->len = 8;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->ecc_cfg);
+			cmd->dst = NC11(NAND_EBI2_ECC_BUF_CFG);
+			cmd->len = 4;
+			cmd++;
+
+			/* if 'only' the last code word */
+			if (n == cwperpage - 1) {
+					/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* CMD */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = paddr(&data->cmd);
+				cmd->dst = NC10(NAND_FLASH_CMD);
+				cmd->len = 4;
+				cmd++;
+
+				/* kick the execute register for NC10 */
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->exec);
+				cmd->dst = NC10(NAND_EXEC_CMD);
+				cmd->len = 4;
+				cmd++;
+
+				/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_data_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* block on data ready from NC10, then
+				 * read the status register
+				 */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = NC10(NAND_FLASH_STATUS);
+				cmd->dst = paddr(&data->result[n]);
+				/* NAND_FLASH_STATUS +
+				 * NAND_BUFFER_STATUS
+				 */
+				cmd->len = 4;
+				cmd++;
+			} else {
+				/* MASK CMD ACK/REQ --> NC10 (0xF14)*/
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc10);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* CMD */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = paddr(&data->cmd);
+				cmd->dst = NC01(NAND_FLASH_CMD);
+				cmd->len = 4;
+				cmd++;
+
+				/* kick the execute register for NC01*/
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->exec);
+				cmd->dst = NC01(NAND_EXEC_CMD);
+				cmd->len = 4;
+				cmd++;
+			}
+		}
+
+
+		if (n % 2 == 0) {
+			/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc01);
+			cmd->dst = EBI2_NAND_ADM_MUX;
+			cmd->len = 4;
+			cmd++;
+
+			/* CMD */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src = paddr(&data->cmd);
+			cmd->dst = NC10(NAND_FLASH_CMD);
+			cmd->len = 4;
+			cmd++;
+
+			/* kick the execute register for NC10 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->exec);
+			cmd->dst = NC10(NAND_EXEC_CMD);
+			cmd->len = 4;
+			cmd++;
+
+			/* MASK DATA ACK/REQ --> NC10 (0xF28)*/
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->adm_mux_data_ack_req_nc10);
+			cmd->dst = EBI2_NAND_ADM_MUX;
+			cmd->len = 4;
+			cmd++;
+
+			/* block on data ready from NC01, then
+			 * read the status register
+			 */
+			cmd->cmd = SRC_CRCI_NAND_DATA;
+			cmd->src = NC01(NAND_FLASH_STATUS);
+			cmd->dst = paddr(&data->result[n]);
+			/* NAND_FLASH_STATUS +
+			 * NAND_BUFFER_STATUS
+			 */
+			cmd->len = 4;
+			cmd++;
+
+			/* read data block */
+			cmd->cmd = 0;
+			cmd->src = NC01(NAND_FLASH_BUFFER);
+			cmd->dst = addr + n * 516;
+			cmd->len = ((n < (cwperpage -1 )) ? 516 : (512 - ((cwperpage - 1) << 2)));
+			cmd++;
+		} else {
+			if (n != cwperpage - 1) {
+				/* MASK CMD ACK/REQ -->
+				 * NC10 (0xF14)
+				 */
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc10);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* CMD */
+				cmd->cmd = DST_CRCI_NAND_CMD;
+				cmd->src = paddr(&data->cmd);
+				cmd->dst = NC01(NAND_FLASH_CMD);
+				cmd->len = 4;
+				cmd++;
+
+				/* EXEC */
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->exec);
+				cmd->dst = NC01(NAND_EXEC_CMD);
+				cmd->len = 4;
+				cmd++;
+
+				/* MASK DATA ACK/REQ -->
+				 * NC01 (0xA3C)
+				 */
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_data_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* block on data ready from NC10
+				 * then read the status register
+				 */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = NC10(NAND_FLASH_STATUS);
+				cmd->dst = paddr(&data->result[n]);
+				/* NAND_FLASH_STATUS +
+				 * NAND_BUFFER_STATUS
+				 */
+				cmd->len = 4;
+				cmd++;
+			} else {
+				/* MASK DATA ACK/REQ ->
+				 * NC01 (0xA3C)
+				 */
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_data_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* block on data ready from NC10
+				 * then read the status register
+				 */
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = NC10(NAND_FLASH_STATUS);
+				cmd->dst = paddr(&data->result[n]);
+				/* NAND_FLASH_STATUS +
+				 * NAND_BUFFER_STATUS
+				 */
+				cmd->len = 4;
+				cmd++;
+			}
+			/* read data block */
+			cmd->cmd = 0;
+			cmd->src = NC10(NAND_FLASH_BUFFER);
+			cmd->dst = addr + n * 516;
+			cmd->len = ((n < (cwperpage -1 )) ? 516 : (512 - ((cwperpage - 1) << 2)));
+			cmd++;
+
+			if (n == (cwperpage - 1)) {
+				/* Use NC10 for reading the
+				 * last codeword!!!
+				 */
+				cmd->cmd = 0;
+				cmd->src = NC10(NAND_FLASH_BUFFER) +
+				(512 - ((cwperpage -1) << 2));
+				cmd->dst = spareaddr;
+				cmd->len = 16;
+				cmd++;
+			}
+		}
+	}
+	/* restore saved ecc config */
+	cmd->cmd = CMD_OCU | CMD_LC;
+	cmd->src = paddr(&data->ecc_cfg_save);
+	cmd->dst = NAND_EBI2_ECC_BUF_CFG;
+	cmd->len = 4;
+
+	/* ADM --> Default mux state (0xFC0) */
+	cmd->cmd = 0;
+	cmd->src = paddr(&data->adm_default_mux);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	/* disable CS1 */
+	cmd->cmd = 0;
+	cmd->src =  paddr(&data->default_ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
+
+	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
+
+#if VERBOSE
+	dprintf(INFO, "read page %d: status: %x %x %x %x %x %x %x %x \
+	%x %x %x %x %x %x %x %x \n", page,
+	data->result[0].flash_status[0],
+	data->result[1].flash_status[1],
+	data->result[2].flash_status[2],
+	data->result[3].flash_status[3],
+	data->result[4].flash_status[4],
+	data->result[5].flash_status[5],
+	data->result[6].flash_status[6],
+	data->result[7].flash_status[7],
+	data->result[8].flash_status[8],
+	data->result[9].flash_status[9],
+	data->result[10].flash_status[10],
+	data->result[11].flash_status[11],
+	data->result[12].flash_status[12],
+	data->result[13].flash_status[13],
+	data->result[14].flash_status[14],
+	data->result[15].flash_status[15]);
+
+	for(n = 0; n < 4; n++) {
+		ptr = (unsigned*)(addr + 512 * n);
+		dprintf(INFO, "data%d:	%x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
+		ptr = (unsigned*)(spareaddr + 16 * n);
+		dprintf(INFO, "spare data%d	%x %x %x %x\n", n, ptr[0], ptr[1], ptr[2], ptr[3]);
+	}
+#endif
+
+	/* if any of the writes failed (0x10), or there was a
+	 ** protection violation (0x100), we lose
+	 */
+	for(n = 0; n < cwperpage; n++) {
+		if (data->result[n].flash_status & 0x110) {
+			return -1;
+		}
+	}
+
+	return 0;
+}
+
 static int _flash_nand_write_page(dmov_s *cmdlist, unsigned *ptrlist, unsigned page,
 								  const void *_addr, const void *_spareaddr, unsigned raw_mode)
 {
@@ -690,6 +1393,294 @@
 #endif
 	return 0;
 }
+
+static int flash_nand_write_page_interleave(dmov_s *cmdlist, unsigned *ptrlist, unsigned page,
+								  const void *_addr, const void *_spareaddr, unsigned raw_mode)
+{
+	dmov_s *cmd = cmdlist;
+	unsigned *ptr = ptrlist;
+	struct interleave_data_flash_io *data = (void*) (ptrlist + 4);
+	unsigned addr = (unsigned) _addr;
+	unsigned spareaddr = (unsigned) _spareaddr;
+	unsigned n;
+	unsigned cwperpage, cwcount;
+
+	cwperpage = (flash_pagesize >> 9) * 2;  /* double for interleave mode */
+	cwcount = (cwperpage << 1);
+
+	data->cmd = NAND_CMD_PRG_PAGE;
+	data->addr0 = page << 16;
+	data->addr1 = (page >> 16) & 0xff;
+	data->chipsel_cs0 = 0 | 4; /* flash0 + undoc bit */
+	data->chipsel_cs1 = 0 | 5; /* flash0 + undoc bit */
+	data->ebi2_chip_select_cfg0 = 0x00000805;
+	data->adm_mux_data_ack_req_nc01 = 0x00000A3C;
+	data->adm_mux_cmd_ack_req_nc01  = 0x0000053C;
+	data->adm_mux_data_ack_req_nc10 = 0x00000F28;
+	data->adm_mux_cmd_ack_req_nc10  = 0x00000F14;
+	data->adm_default_mux = 0x00000FC0;
+	data->default_ebi2_chip_select_cfg0 = 0x00000801;
+
+	if (!raw_mode){
+		data->cfg0 = CFG0;
+		data->cfg1 = CFG1;
+	}else{
+		data->cfg0 = (NAND_CFG0_RAW & ~(7 << 6)) |((cwcount-1) << 6);
+		data->cfg1 = NAND_CFG1_RAW | (CFG1 & CFG1_WIDE_FLASH);
+	}
+
+	/* GO bit for the EXEC register */
+	data->exec = 1;
+	data->ecc_cfg = 0x203;
+
+	for (n = 0; n < cwperpage; n++) {
+		/* status return words */
+		data->result[n].flash_status = 0xeeeeeeee;
+
+		if (n == 0) {
+			/* enable CS1 */
+			cmd->cmd = CMD_OCB;
+			cmd->src = paddr(&data->ebi2_chip_select_cfg0);
+			cmd->dst = EBI2_CHIP_SELECT_CFG0;
+			cmd->len = 4;
+			cmd++;
+
+			/* save existing ecc config */
+			cmd->cmd = 0;
+			cmd->src = NC11(NAND_EBI2_ECC_BUF_CFG);
+			cmd->dst = paddr(&data->ecc_cfg_save);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->ecc_cfg);
+			cmd->dst = NC11(NAND_EBI2_ECC_BUF_CFG);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->addr0);
+			cmd->dst = NC11(NAND_ADDR0);
+			cmd->len = 8;
+			cmd++;
+
+			/* enable CS0 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->chipsel_cs0);
+			cmd->dst = NC01(NAND_FLASH_CHIP_SELECT);
+			cmd->len = 4;
+			cmd++;
+
+			/* enable CS1 */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->chipsel_cs1);
+			cmd->dst = NC10(NAND_FLASH_CHIP_SELECT);
+			cmd->len = 4;
+			cmd++;
+
+			cmd->cmd = 0;
+			cmd->src =paddr(&data->cfg0);
+			cmd->dst = NC01(NAND_DEV0_CFG0);
+			cmd->len = 8;
+			cmd++;
+
+			/* config CFG1 for CS1 */
+			cmd->cmd = 0;
+			cmd->src =paddr(&data->cfg0);
+			cmd->dst = NC10(NAND_DEV1_CFG0);
+			cmd->len = 8;
+			cmd++;
+		}
+
+		if (n % 2 == 0) {
+			/* MASK CMD ACK/REQ --> NC10 (0xF14)*/
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc10);
+			cmd->dst = EBI2_NAND_ADM_MUX;
+			cmd->len = 4;
+			cmd++;
+
+			/* CMD */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src = paddr(&data->cmd);
+			cmd->dst = NC01(NAND_FLASH_CMD);
+			cmd->len = 4;
+			cmd++;
+		} else {
+			/* MASK CMD ACK/REQ --> NC01 (0x53C)*/
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->adm_mux_cmd_ack_req_nc01);
+			cmd->dst = EBI2_NAND_ADM_MUX;
+			cmd->len = 4;
+			cmd++;
+
+			/* CMD */
+			cmd->cmd = DST_CRCI_NAND_CMD;
+			cmd->src = paddr(&data->cmd);
+			cmd->dst = NC10(NAND_FLASH_CMD);
+			cmd->len = 4;
+			cmd++;
+		}
+
+		cmd->cmd = 0;
+		if (!raw_mode){
+			cmd->src = addr + n * 516;
+			cmd->len = ((n < (cwperpage - 1)) ? 516 : (512 - ((cwperpage - 1) << 2)));
+		}else{
+			cmd->src = addr;
+			cmd->len =  528;
+		}
+
+		if (n % 2 == 0)
+			cmd->dst = NC01(NAND_FLASH_BUFFER);
+		else
+			cmd->dst = NC10(NAND_FLASH_BUFFER);
+		cmd++;
+
+		if ((n == (cwperpage - 1)) && (!raw_mode)) {
+			/* write extra data */
+			cmd->cmd = 0;
+			cmd->src = spareaddr;
+			cmd->dst = NC10(NAND_FLASH_BUFFER) + (512 - ((cwperpage - 1) << 2));
+			cmd->len = (cwperpage << 2);
+			cmd++;
+		}
+
+		if (n % 2 == 0) {
+			/* kick the NC01 execute register */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->exec);
+			cmd->dst = NC01(NAND_EXEC_CMD);
+			cmd->len = 4;
+			cmd++;
+			if (n != 0) {
+				/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+				cmd->cmd = 0;
+				cmd->src = paddr(&data->adm_mux_data_ack_req_nc01);
+				cmd->dst = EBI2_NAND_ADM_MUX;
+				cmd->len = 4;
+				cmd++;
+
+				/* block on data ready from NC10, then
+				* read the status register
+				*/
+				cmd->cmd = SRC_CRCI_NAND_DATA;
+				cmd->src = NC10(NAND_FLASH_STATUS);
+				cmd->dst = paddr(&data->result[n-1]);
+				cmd->len = 4;
+				cmd++;
+			}
+		} else {
+			/* kick the execute register */
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->exec);
+			cmd->dst = NC10(NAND_EXEC_CMD);
+			cmd->len = 4;
+			cmd++;
+
+			/* MASK DATA ACK/REQ --> NC10 (0xF28)*/
+			cmd->cmd = 0;
+			cmd->src = paddr(&data->adm_mux_data_ack_req_nc10);
+			cmd->dst = EBI2_NAND_ADM_MUX;
+			cmd->len = 4;
+			cmd++;
+
+			/* block on data ready from NC01, then
+			 * read the status register
+			 */
+			cmd->cmd = SRC_CRCI_NAND_DATA;
+			cmd->src = NC01(NAND_FLASH_STATUS);
+			cmd->dst = paddr(&data->result[n-1]);
+			cmd->len = 4;
+			cmd++;
+		}
+	}
+
+	/* MASK DATA ACK/REQ --> NC01 (0xA3C)*/
+	cmd->cmd = 0;
+	cmd->src = paddr(&data->adm_mux_data_ack_req_nc01);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	/* we should process outstanding request */
+	/* block on data ready, then
+	 * read the status register
+	 */
+	cmd->cmd = SRC_CRCI_NAND_DATA;
+	cmd->src = NC10(NAND_FLASH_STATUS);
+	cmd->dst = paddr(&data->result[n-1]);
+	cmd->len = 4;
+	cmd++;
+
+	/* restore saved ecc config */
+	cmd->cmd = 0;
+	cmd->src = paddr(&data->ecc_cfg_save);
+	cmd->dst = NAND_EBI2_ECC_BUF_CFG;
+	cmd->len = 4;
+
+	/* MASK DATA ACK/REQ --> NC01 (0xFC0)*/
+	cmd->cmd = 0;
+	cmd->src = paddr(&data->adm_default_mux);
+	cmd->dst = EBI2_NAND_ADM_MUX;
+	cmd->len = 4;
+	cmd++;
+
+	/* disable CS1 */
+	cmd->cmd = CMD_OCU | CMD_LC;
+	cmd->src = paddr(&data->default_ebi2_chip_select_cfg0);
+	cmd->dst = EBI2_CHIP_SELECT_CFG0;
+	cmd->len = 4;
+	cmd++;
+
+	ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
+
+	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
+
+#if VERBOSE
+dprintf(INFO, "write page %d: status: %x %x %x %x %x %x %x %x \
+	%x %x %x %x %x %x %x %x \n", page,
+	data->result[0].flash_status[0],
+	data->result[1].flash_status[1],
+	data->result[2].flash_status[2],
+	data->result[3].flash_status[3],
+	data->result[4].flash_status[4],
+	data->result[5].flash_status[5],
+	data->result[6].flash_status[6],
+	data->result[7].flash_status[7],
+	data->result[8].flash_status[8],
+	data->result[9].flash_status[9],
+	data->result[10].flash_status[10],
+	data->result[11].flash_status[11],
+	data->result[12].flash_status[12],
+	data->result[13].flash_status[13],
+	data->result[14].flash_status[14],
+	data->result[15].flash_status[15]);
+#endif
+
+	/* if any of the writes failed (0x10), or there was a
+	 ** protection violation (0x100), or the program success
+	 ** bit (0x80) is unset, we lose
+	 */
+	for(n = 0; n < cwperpage; n++) {
+		if(data->result[n].flash_status & 0x110) return -1;
+		if(!(data->result[n].flash_status & 0x80)) return -1;
+	}
+
+#if VERIFY_WRITE
+	n = _flash_read_page(cmdlist, ptrlist, page, flash_data,
+				 flash_data + 2048);
+	if (n != 0)
+		return -1;
+	if (memcmp(flash_data, _addr, 2048) ||
+		memcmp(flash_data + 2048, _spareaddr, 16)) {
+		dprintf(CRITICAL, "verify error @ page %d\n", page);
+		return -1;
+	}
+#endif
+	return 0;
+}
+
 char empty_buf[528];
 static int flash_nand_mark_badblock(dmov_s *cmdlist, unsigned *ptrlist, unsigned page)
 {