text: /*
   * Copyright (c) 2008, Google Inc.
   * All rights reserved.
   * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *  * Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *  * Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
   * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
   * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
   * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
   * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
   * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
   * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   */
  
  #include <debug.h>
  #include <reg.h>
  #include <stdlib.h>
  #include <string.h>
  #include <dev/flash.h>
  #include <lib/ptable.h>
  #include <nand.h>
  
  #include "dmov.h"
  
  
  #define QUECTEL_NAND_NM14FSK2LAXCL //Ramos.zhang 20160108
  
  #define VERBOSE 0
  #define VERIFY_WRITE 0
  
  static void *flash_spare;
  static void *flash_data;
  void platform_config_interleaved_mode_gpios(void);
  
  typedef struct dmov_ch dmov_ch;
  struct dmov_ch {
  	volatile unsigned cmd;
  	volatile unsigned result;
  	volatile unsigned status;
  	volatile unsigned config;
  };
  
  static void dmov_prep_ch(dmov_ch * ch, unsigned id)
  {
  	ch->cmd = DMOV_CMD_PTR(id);
  	ch->result = DMOV_RSLT(id);
  	ch->status = DMOV_STATUS(id);
  	ch->config = DMOV_CONFIG(id);
  }
  
  #define SRC_CRCI_NAND_CMD  CMD_SRC_CRCI(DMOV_NAND_CRCI_CMD)
  #define DST_CRCI_NAND_CMD  CMD_DST_CRCI(DMOV_NAND_CRCI_CMD)
  #define SRC_CRCI_NAND_DATA CMD_SRC_CRCI(DMOV_NAND_CRCI_DATA)
  #define DST_CRCI_NAND_DATA CMD_DST_CRCI(DMOV_NAND_CRCI_DATA)
  
  #define CFG0_RAW 0xA80420C0
  #define CFG1_RAW 0x5045D
  
  #define CFG0_RAW_BCHECC   0xA80428C0
  
  static unsigned CFG0, CFG1;
  static unsigned CFG0_M, CFG1_M;
  static unsigned CFG0_A, CFG1_A;
  static unsigned NAND_CFG0_RAW, NAND_CFG1_RAW;
  static unsigned ECC_BCH_CFG;
  
  static uint32_t enable_bch_ecc;
  static unsigned int *bbtbl;
  
  #define CFG1_WIDE_FLASH (1U << 1)
  
  #define paddr(n) ((unsigned) (n))
  
  static int dmov_exec_cmdptr(unsigned id, unsigned *ptr)
  {
  	dmov_ch ch;
  	unsigned n;
  
  	dmov_prep_ch(&ch, id);
  
  	/* Set IRQ_EN low, not using IRQ mode */
  	writel(DMOV_CONFIG_FOREC_FLUSH_RSLT | 0x0, ch.config);
  
  	writel(DMOV_CMD_PTR_LIST | DMOV_CMD_ADDR(paddr(ptr)), ch.cmd);
  
  	while (!(readl(ch.status) & DMOV_STATUS_RSLT_VALID)) ;
  
  	n = readl(ch.status);
  	while (DMOV_STATUS_RSLT_COUNT(n)) {
  		n = readl(ch.result);
  		if (n != 0x80000002) {
  			dprintf(CRITICAL, "ERROR: result: %x\n", n);
  			dprintf(CRITICAL, "ERROR:  flush: %x %x %x %x\n",
  				readl(DMOV_FLUSH0(DMOV_NAND_CHAN)),
  				readl(DMOV_FLUSH1(DMOV_NAND_CHAN)),
  				readl(DMOV_FLUSH2(DMOV_NAND_CHAN)),
  				readl(DMOV_FLUSH3(DMOV_NAND_CHAN)));
  		}
  		n = readl(ch.status);
  	}
  
  	return 0;
  }
  
  static struct flash_info flash_info;
  static unsigned flash_pagesize = 0;
  static int interleaved_mode = 0;
  static unsigned num_pages_per_blk = 0;
  static unsigned num_pages_per_blk_mask = 0;
  
  struct flash_identification {
  	unsigned flash_id;
  	unsigned mask;
  	unsigned density;
  	unsigned widebus;
  	unsigned pagesize;
  	unsigned blksize;
  	unsigned oobsize;
  	unsigned onenand;
  };
  
  static struct flash_identification supported_flash[] = {
  	/* Flash ID     ID Mask Density(MB)  Wid Pgsz   Blksz   oobsz onenand   Manuf */
  	{0x00000000, 0xFFFFFFFF, 0, 0, 0, 0, 0, 0}, /*ONFI*/ {0x1500aaec, 0xFF00FFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 0},	/*Sams */
  #ifdef QUECTEL_NAND_FM6BD4G2GXA //Ramos.zhang 20151204
  	{0x2690ac2c, 0xFFFFFFFF, (512<<20), 0, 4096, (4096<<6), 224, 0},
  #endif
  #ifdef QUECTEL_NAND_NM14FSK2LAXCL //Ramos.zhang 20160108
  	{0x2690ac98, 0xFFFFFFFF, (512<<20), 0, 4096, (4096<<6), 256, 0},
  #endif
  	{0x5500baec, 0xFF00FFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Sams */
  	{0x1500aa98, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 0},	/*Tosh */
  	{0x5500ba98, 0xFFFFFFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Tosh */
  	{0xd580b12c, 0xFFFFFFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Micr */
  	{0x5590bc2c, 0xFFFFFFFF, (512 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Micr */
  	{0x1580aa2c, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 0},	/*Micr */
  	{0x1590aa2c, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 0},	/*Micr */
  	{0x1590ac2c, 0xFFFFFFFF, (512 << 20), 0, 2048, (2048 << 6), 64, 0},	/*Micr */
  	{0x5580baad, 0xFFFFFFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Hynx */
  	{0x5510baad, 0xFFFFFFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Hynx */
  	{0x004000ec, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 1},	/*Sams */
  	{0x005c00ec, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 1},	/*Sams */
  	{0x005800ec, 0xFFFFFFFF, (256 << 20), 0, 2048, (2048 << 6), 64, 1},	/*Sams */
  	{0x6600bcec, 0xFF00FFFF, (512 << 20), 1, 4096, (4096 << 6), 128, 0},	/*Sams */
  	{0x5580ba2c, 0xFFFFFFFF, (256 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Hynx */
  	{0x6600b3ec, 0xFFFFFFFF, (1024 << 20), 1, 4096, (4096 << 6), 128, 0},	/*Sams */
  	{0x2600482c, 0xFF00FFFF, (2048 << 20), 0, 4096, (4096 << 7), 224, 0},	/*8bit bch ecc */
  	{0x55d1b32c, 0xFFFFFFFF, (1024 << 20), 1, 2048, (2048 << 6), 64, 0},	/*Micr */
  	/* Note: Width flag is 0 for 8 bit Flash and 1 for 16 bit flash   */
  	/* Note: Onenand flag is 0 for NAND Flash and 1 for OneNAND flash       */
  	/* Note: The First row will be filled at runtime during ONFI probe      */
  };
  
  static void set_nand_configuration(char type)
  {
  	if (type == TYPE_MODEM_PARTITION) {
  		CFG0 = CFG0_M;
  		CFG1 = CFG1_M;
  	} else {
  		CFG0 = CFG0_A;
  		CFG1 = CFG1_A;
  	}
  }
  
  static void flash_nand_read_id(dmov_s * cmdlist, unsigned *ptrlist)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	unsigned *data = ptrlist + 4;
  
  	data[0] = 0 | 4;
  	data[1] = NAND_CMD_FETCH_ID;
  	data[2] = 1;
  	data[3] = 0;
  	data[4] = 0;
  	data[5] = 0;
  	data[6] = 0;
  	data[7] = 0xAAD40000;	/* Default value for CFG0 for reading device id */
  
  	/* Read NAND device id */
  	cmd[0].cmd = 0 | CMD_OCB;
  	cmd[0].src = paddr(&data[7]);
  	cmd[0].dst = NAND_DEV0_CFG0;
  	cmd[0].len = 4;
  
  	cmd[1].cmd = 0;
  	cmd[1].src = NAND_SFLASHC_BURST_CFG;
  	cmd[1].dst = paddr(&data[5]);
  	cmd[1].len = 4;
  
  	cmd[2].cmd = 0;
  	cmd[2].src = paddr(&data[6]);
  	cmd[2].dst = NAND_SFLASHC_BURST_CFG;
  	cmd[2].len = 4;
  
  	cmd[3].cmd = 0;
  	cmd[3].src = paddr(&data[0]);
  	cmd[3].dst = NAND_FLASH_CHIP_SELECT;
  	cmd[3].len = 4;
  
  	cmd[4].cmd = DST_CRCI_NAND_CMD;
  	cmd[4].src = paddr(&data[1]);
  	cmd[4].dst = NAND_FLASH_CMD;
  	cmd[4].len = 4;
  
  	cmd[5].cmd = 0;
  	cmd[5].src = paddr(&data[2]);
  	cmd[5].dst = NAND_EXEC_CMD;
  	cmd[5].len = 4;
  
  	cmd[6].cmd = SRC_CRCI_NAND_DATA;
  	cmd[6].src = NAND_FLASH_STATUS;
  	cmd[6].dst = paddr(&data[3]);
  	cmd[6].len = 4;
  
  	cmd[7].cmd = 0;
  	cmd[7].src = NAND_READ_ID;
  	cmd[7].dst = paddr(&data[4]);
  	cmd[7].len = 4;
  
  	cmd[8].cmd = CMD_OCU | CMD_LC;
  	cmd[8].src = paddr(&data[5]);
  	cmd[8].dst = NAND_SFLASHC_BURST_CFG;
  	cmd[8].len = 4;
  
  	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  #if VERBOSE
  	dprintf(INFO, "status: %x\n", data[3]);
  #endif
  
  	flash_info.id = data[4];
  	flash_info.vendor = data[4] & 0xff;
  	flash_info.device = (data[4] >> 8) & 0xff;
  	return;
  }
  
  static int
  flash_nand_block_isbad(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	unsigned *data = ptrlist + 4;
  	char buf[4];
  	unsigned cwperpage;
  
  	cwperpage = (flash_pagesize >> 9);
  
  	/* Check first page of this block */
  	if (page & num_pages_per_blk_mask)
  		page = page - (page & num_pages_per_blk_mask);
  
  	/* Check bad block marker */
  	data[0] = NAND_CMD_PAGE_READ;	/* command */
  
  	/* addr0 */
  	if (CFG1 & CFG1_WIDE_FLASH)
  		data[1] = enable_bch_ecc ?
  		    ((page << 16) | ((532 * (cwperpage - 1)) >> 1)) :
  		    ((page << 16) | ((528 * (cwperpage - 1)) >> 1));
  
  	else
  		data[1] = enable_bch_ecc ?
  		    ((page << 16) | (532 * (cwperpage - 1))) :
  		    ((page << 16) | (528 * (cwperpage - 1)));
  
  	data[2] = (page >> 16) & 0xff;	/* addr1        */
  	data[3] = 0 | 4;	/* chipsel      */
  	data[4] = NAND_CFG0_RAW & ~(7U << 6);	/* cfg0         */
  	data[5] = NAND_CFG1_RAW | (CFG1 & CFG1_WIDE_FLASH);	/* cfg1         */
  	if (enable_bch_ecc) {
  		data[6] = ECC_BCH_CFG;	/* ECC CFG */
  	}
  	data[7] = 1;
  	data[8] = CLEAN_DATA_32;	/* flash status */
  	data[9] = CLEAN_DATA_32;	/* buf status   */
  
  	cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
  	cmd[0].src = paddr(&data[0]);
  	cmd[0].dst = NAND_FLASH_CMD;
  	cmd[0].len = 16;
  
  	cmd[1].cmd = 0;
  	cmd[1].src = paddr(&data[4]);
  	cmd[1].dst = NAND_DEV0_CFG0;
  	if (enable_bch_ecc) {
  		cmd[1].len = 12;
  	} else {
  		cmd[1].len = 8;
  	}
  
  	cmd[2].cmd = 0;
  	cmd[2].src = paddr(&data[7]);
  	cmd[2].dst = NAND_EXEC_CMD;
  	cmd[2].len = 4;
  
  	cmd[3].cmd = SRC_CRCI_NAND_DATA;
  	cmd[3].src = NAND_FLASH_STATUS;
  	cmd[3].dst = paddr(&data[8]);
  	cmd[3].len = 8;
  
  	cmd[4].cmd = CMD_OCU | CMD_LC;
  	cmd[4].src = NAND_FLASH_BUFFER + (flash_pagesize - (enable_bch_ecc ?
  							    (532 *
  							     (cwperpage -
  							      1)) : (528 *
  								     (cwperpage
  								      - 1))));
  	cmd[4].dst = paddr(&buf);
  	cmd[4].len = 4;
  
  	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  #if VERBOSE
  	dprintf(INFO, "status: %x\n", data[8]);
  #endif
  
  	/* we fail if there was an operation error, a mpu error, or the
  	 ** erase success bit was not set.
  	 */
  	if (data[8] & 0x110)
  		return -1;
  
  	/* Check for bad block marker byte */
  	if (CFG1 & CFG1_WIDE_FLASH) {
  		if (buf[0] != 0xFF || buf[1] != 0xFF)
  			return 1;
  	} else {
  		if (buf[0] != 0xFF)
  			return 1;
  	}
  
  	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)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	unsigned *data = ptrlist + 4;
  	int isbad = 0;
  
  	/* only allow erasing on block boundaries */
  	if (page & num_pages_per_blk_mask)
  		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 / num_pages_per_blk);
  		return -1;
  	}
  
  	/* Erase block */
  	data[0] = NAND_CMD_BLOCK_ERASE;
  	data[1] = page;
  	data[2] = 0;
  	data[3] = 0 | 4;
  	data[4] = 1;
  	data[5] = 0xeeeeeeee;
  	data[6] = CFG0 & (~(7 << 6));	/* CW_PER_PAGE = 0 */
  	data[7] = CFG1;
  	data[8] = ECC_BCH_CFG;
  	data[9] = 0x00000020;
  	data[10] = 0x000000C0;
  
  	cmd[0].cmd = DST_CRCI_NAND_CMD | CMD_OCB;
  	cmd[0].src = paddr(&data[0]);
  	cmd[0].dst = NAND_FLASH_CMD;
  	cmd[0].len = 16;
  
  	cmd[1].cmd = 0;
  	cmd[1].src = paddr(&data[6]);
  	cmd[1].dst = NAND_DEV0_CFG0;
  	if (enable_bch_ecc) {
  		cmd[1].len = 12;
  	} else {
  		cmd[1].len = 8;
  	}
  
  	cmd[2].cmd = 0;
  	cmd[2].src = paddr(&data[4]);
  	cmd[2].dst = NAND_EXEC_CMD;
  	cmd[2].len = 4;
  
  	cmd[3].cmd = SRC_CRCI_NAND_DATA;
  	cmd[3].src = NAND_FLASH_STATUS;
  	cmd[3].dst = paddr(&data[5]);
  	cmd[3].len = 4;
  
  	cmd[4].cmd = 0;
  	cmd[4].src = paddr(&data[9]);
  	cmd[4].dst = NAND_FLASH_STATUS;
  	cmd[4].len = 4;
  
  	cmd[5].cmd = CMD_OCU | CMD_LC;
  	cmd[5].src = paddr(&data[10]);
  	cmd[5].dst = NAND_READ_STATUS;
  	cmd[5].len = 4;
  
  	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  #if VERBOSE
  	dprintf(INFO, "status: %x\n", data[5]);
  #endif
  
  	/* we fail if there was an operation error, a mpu error, or the
  	 ** erase success bit was not set.
  	 */
  	if (data[5] & 0x110)
  		return -1;
  	if (!(data[5] & 0x80))
  		return -1;
  
  	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;
  	unsigned addr1;
  	unsigned chipsel;
  	unsigned cfg0;
  	unsigned cfg1;
  	unsigned ecc_bch_cfg;
  	unsigned exec;
  	unsigned ecc_cfg;
  	unsigned ecc_cfg_save;
  	unsigned clrfstatus;
  	unsigned clrrstatus;
  	struct {
  		unsigned flash_status;
  		unsigned buffer_status;
  	} 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)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	struct data_flash_io *data = (void *)(ptrlist + 4);
  	unsigned addr = (unsigned)_addr;
  	unsigned spareaddr = (unsigned)_spareaddr;
  	unsigned n;
  	int isbad = 0;
  	unsigned cwperpage;
  	unsigned block = 0;
  	cwperpage = (flash_pagesize >> 9);
  
  	/* Find the block no for the page */
  	block = page / num_pages_per_blk;
  
  	/* Check the bad block table for each block
  	 * -1: indicates the block needs to be checked if good or bad
  	 * 1 : The block is bad
  	 * 0 : The block is good
  	 */
  	if (bbtbl[block] == -1) {
  		isbad = flash_nand_block_isbad(cmdlist, ptrlist, page);
  		if (isbad) {
  			/* Found bad , set the bad table entry */
  			bbtbl[block] = 1;
  			return -2;
  		} else {
  			/* Found good block , set the table entry &
  			 *  continue reading the data
  			 */
  			bbtbl[block] = 0;
  		}
  	} else if (bbtbl[block] == 1) {
  		/* If the block is already identified as bad, return error */
  		return -2;
  	}
  
  	data->cmd = NAND_CMD_PAGE_READ_ECC;
  	data->addr0 = page << 16;
  	data->addr1 = (page >> 16) & 0xff;
  	data->chipsel = 0 | 4;	/* flash0 + undoc bit */
  
  	/* GO bit for the EXEC register */
  	data->exec = 1;
  
  	data->cfg0 = CFG0;
  	data->cfg1 = CFG1;
  
  	if (enable_bch_ecc) {
  		data->ecc_bch_cfg = ECC_BCH_CFG;
  	}
  	data->ecc_cfg = 0x203;
  
  	/* save existing ecc config */
  	cmd->cmd = CMD_OCB;
  	cmd->src = NAND_EBI2_ECC_BUF_CFG;
  	cmd->dst = paddr(&data->ecc_cfg_save);
  	cmd->len = 4;
  	cmd++;
  
  	for (n = 0; n < cwperpage; n++) {
  		/* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
  		cmd->cmd = DST_CRCI_NAND_CMD;
  		cmd->src = paddr(&data->cmd);
  		cmd->dst = NAND_FLASH_CMD;
  		cmd->len = ((n == 0) ? 16 : 4);
  		cmd++;
  
  		if (n == 0) {
  			/* block on cmd ready, set configuration */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->cfg0);
  			cmd->dst = NAND_DEV0_CFG0;
  			if (enable_bch_ecc) {
  				cmd->len = 12;
  			} else {
  				cmd->len = 8;
  			}
  			cmd++;
  
  			/* set our ecc config */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->ecc_cfg);
  			cmd->dst = NAND_EBI2_ECC_BUF_CFG;
  			cmd->len = 4;
  			cmd++;
  		}
  		/* kick the execute register */
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->exec);
  		cmd->dst = NAND_EXEC_CMD;
  		cmd->len = 4;
  		cmd++;
  
  		/* block on data ready, then read the status register */
  		cmd->cmd = SRC_CRCI_NAND_DATA;
  		cmd->src = NAND_FLASH_STATUS;
  		cmd->dst = paddr(&data->result[n]);
  		cmd->len = 8;
  		cmd++;
  
  		/* read data block */
  		cmd->cmd = 0;
  		cmd->src = NAND_FLASH_BUFFER;
  		cmd->dst = addr + n * 516;
  		cmd->len =
  		    ((n <
  		      (cwperpage - 1)) ? 516 : (512 - ((cwperpage - 1) << 2)));
  		cmd++;
  	}
  
  	/* read extra data */
  	cmd->cmd = 0;
  	cmd->src = 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;
  
  	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\n",
  		page, data[5], data[6], data[7], data[8]);
  	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_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)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	struct data_flash_io *data = (void *)(ptrlist + 4);
  	unsigned addr = (unsigned)_addr;
  	unsigned spareaddr = (unsigned)_spareaddr;
  	unsigned n;
  	unsigned cwperpage;
  	cwperpage = (flash_pagesize >> 9);
  	unsigned modem_partition = 0;
  	if (CFG0 == CFG0_M) {
  		modem_partition = 1;
  	}
  
  	data->cmd = NAND_CMD_PRG_PAGE;
  	data->addr0 = page << 16;
  	data->addr1 = (page >> 16) & 0xff;
  	data->chipsel = 0 | 4;	/* flash0 + undoc bit */
  	data->clrfstatus = 0x00000020;
  	data->clrrstatus = 0x000000C0;
  
  	if (!raw_mode) {
  		data->cfg0 = CFG0;
  		data->cfg1 = CFG1;
  		if (enable_bch_ecc) {
  			data->ecc_bch_cfg = ECC_BCH_CFG;
  		}
  	} else {
  		data->cfg0 =
  		    (NAND_CFG0_RAW & ~(7 << 6)) | ((cwperpage - 1) << 6);
  		data->cfg1 = NAND_CFG1_RAW | (CFG1 & CFG1_WIDE_FLASH);
  	}
  
  	/* GO bit for the EXEC register */
  	data->exec = 1;
  
  	if (modem_partition)
  		data->ecc_cfg = 0x1FF;
  	else
  		data->ecc_cfg = 0x203;
  
  	/* save existing ecc config */
  	cmd->cmd = CMD_OCB;
  	cmd->src = NAND_EBI2_ECC_BUF_CFG;
  	cmd->dst = paddr(&data->ecc_cfg_save);
  	cmd->len = 4;
  	cmd++;
  
  	for (n = 0; n < cwperpage; n++) {
  		/* write CMD / ADDR0 / ADDR1 / CHIPSEL regs in a burst */
  		cmd->cmd = DST_CRCI_NAND_CMD;
  		cmd->src = paddr(&data->cmd);
  		cmd->dst = NAND_FLASH_CMD;
  		cmd->len = ((n == 0) ? 16 : 4);
  		cmd++;
  
  		if (n == 0) {
  			/*  set configuration */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->cfg0);
  			cmd->dst = NAND_DEV0_CFG0;
  			if (enable_bch_ecc) {
  				cmd->len = 12;
  			} else {
  				cmd->len = 8;
  			}
  
  			cmd++;
  
  			/* set our ecc config */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->ecc_cfg);
  			cmd->dst = NAND_EBI2_ECC_BUF_CFG;
  			cmd->len = 4;
  			cmd++;
  		}
  
  		/* write data block */
  		cmd->cmd = 0;
  		cmd->dst = NAND_FLASH_BUFFER;
  		if (!raw_mode) {
  			if (modem_partition) {
  				cmd->src = addr + n * 512;
  				cmd->len = 512;
  			} else {
  				cmd->src = addr + n * 516;
  				cmd->len =
  				    ((n <
  				      (cwperpage - 1)) ? 516 : (512 -
  								((cwperpage -
  								  1) << 2)));
  			}
  		} else {
  			cmd->src = addr;
  			cmd->len = 528;
  		}
  		cmd++;
  
  		if ((n == (cwperpage - 1)) && (!raw_mode) && (!modem_partition)) {
  			/* write extra data */
  			cmd->cmd = 0;
  			cmd->src = spareaddr;
  			cmd->dst =
  			    NAND_FLASH_BUFFER + (512 - ((cwperpage - 1) << 2));
  			cmd->len = (cwperpage << 2);
  			cmd++;
  		}
  
  		/* kick the execute register */
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->exec);
  		cmd->dst = NAND_EXEC_CMD;
  		cmd->len = 4;
  		cmd++;
  
  		/* block on data ready, then read the status register */
  		cmd->cmd = SRC_CRCI_NAND_DATA;
  		cmd->src = NAND_FLASH_STATUS;
  		cmd->dst = paddr(&data->result[n]);
  		cmd->len = 8;
  		cmd++;
  
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->clrfstatus);
  		cmd->dst = NAND_FLASH_STATUS;
  		cmd->len = 4;
  		cmd++;
  
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->clrrstatus);
  		cmd->dst = NAND_READ_STATUS;
  		cmd->len = 4;
  		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;
  
  	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\n",
  		page, data[5], data[6], data[7], data[8]);
  #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;
  }
  
  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)
  {
  	memset(empty_buf, 0, 528);
  	/* Going to first page of the block */
  	if (page & num_pages_per_blk_mask)
  		page = page - (page & num_pages_per_blk_mask);
  	return _flash_nand_write_page(cmdlist, ptrlist, page, empty_buf, 0, 1);
  }
  
  unsigned nand_cfg0;
  unsigned nand_cfg1;
  
  static int flash_nand_read_config(dmov_s * cmdlist, unsigned *ptrlist)
  {
  	static unsigned CFG0_TMP, CFG1_TMP;
  	cmdlist[0].cmd = CMD_OCB;
  	cmdlist[0].src = NAND_DEV0_CFG0;
  	cmdlist[0].dst = paddr(&CFG0_TMP);
  	cmdlist[0].len = 4;
  
  	cmdlist[1].cmd = CMD_OCU | CMD_LC;
  	cmdlist[1].src = NAND_DEV0_CFG1;
  	cmdlist[1].dst = paddr(&CFG1_TMP);
  	cmdlist[1].len = 4;
  
  	*ptrlist = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptrlist);
  
  	if ((CFG0_TMP == 0) || (CFG1_TMP == 0)) {
  		return -1;
  	}
  
  	CFG0_A = CFG0_TMP;
  	CFG1_A = CFG1_TMP;
  	if (flash_info.type == FLASH_16BIT_NAND_DEVICE) {
  		nand_cfg1 |= CFG1_WIDE_FLASH;
  	}
  	dprintf(INFO, "nandcfg: %x %x (initial)\n", CFG0, CFG1);
  
  	CFG0_A = (((flash_pagesize >> 9) - 1) << 6)	/* 4/8 cw/pg for 2/4k */
  	    |(516 << 9)		/* 516 user data bytes */
  	    |(10 << 19)		/* 10 parity bytes */
  	    |(5 << 27)		/* 5 address cycles */
  	    |(0 << 30)		/* Do not read status before data */
  	    |(1 << 31)
  	    /* Send read cmd */
  	    /* 0 spare bytes for 16 bit nand or 1 spare bytes for 8 bit */
  	    |((nand_cfg1 & CFG1_WIDE_FLASH) ? (0 << 23) : (enable_bch_ecc ? (2 << 23) : (1 << 23)));	/* 2 spare bytes for 8 bit bch ecc */
  	CFG1_A = (0 << 0)	/* Enable ecc */
  	    |(7 << 2)		/* 8 recovery cycles */
  	    |(0 << 5)		/* Allow CS deassertion */
  	    |((flash_pagesize - ((enable_bch_ecc ? 532 : 528) * ((flash_pagesize >> 9) - 1)) + 1) << 6)	/* Bad block marker location */
  	    |(0 << 16)		/* Bad block in user data area */
  	    |(2 << 17)		/* 6 cycle tWB/tRB */
  	    |(nand_cfg1 & CFG1_WIDE_FLASH);	/* preserve wide flash flag */
  
  	NAND_CFG0_RAW = CFG0_RAW;
  	NAND_CFG1_RAW = CFG1_RAW;
  
  	if (enable_bch_ecc) {
  		CFG1_A |= (1 << 27);	/* Enable BCH engine */
  		ECC_BCH_CFG = (0 << 0)	/* Enable ECC */
  		    |(0 << 1)	/* Enable/Disable SW reset of ECC engine */
  		    |(1 << 4)	/* 8bit ecc */
  		    |((nand_cfg1 & CFG1_WIDE_FLASH) ? (14 << 8) : (13 << 8))	/*parity bytes */
  		    |(516 << 16)	/* 516 user data bytes */
  		    |(1 << 30);	/* Turn on ECC engine clocks always */
  		NAND_CFG0_RAW = CFG0_RAW_BCHECC;	/* CW size is increased to 532B */
  	}
  
  	dprintf(INFO, "nandcfg(Apps): %x %x (used)\n", CFG0_A, CFG1_A);
  
  	CFG0_M = CFG0_TMP;
  	CFG1_M = CFG1_TMP;
  	if (flash_info.type == FLASH_16BIT_NAND_DEVICE) {
  		nand_cfg1 |= CFG1_WIDE_FLASH;
  	}
  	CFG0_M = (((flash_pagesize >> 9) - 1) << 6)	/* 4/8 cw/pg for 2/4k */
  	    |(512 << 9)		/* 512 user data bytes */
  	    |(10 << 19)		/* 10 parity bytes */
  	    |(5 << 27)		/* 5 address cycles */
  	    |(0 << 30)		/* Do not read status before data */
  	    |(1 << 31)		/* Send read cmd */
  	    |((nand_cfg1 & CFG1_WIDE_FLASH) ? (4 << 23) : (5 << 23));
  	CFG1_M = (0 << 0)	/* Enable ecc */
  	    |(7 << 2)		/* 8 recovery cycles */
  	    |(0 << 5)		/* Allow CS deassertion */
  	    |((flash_pagesize - (528 * ((flash_pagesize >> 9) - 1)) + 1) << 6)	/* Bad block marker location */
  	    |(0 << 16)		/* Bad block in user data area */
  	    |(2 << 17)		/* 6 cycle tWB/tRB */
  	    |(nand_cfg1 & CFG1_WIDE_FLASH);	/* preserve wide flash flag */
  	dprintf(INFO, "nandcfg(Modem): %x %x (used)\n", CFG0_M, CFG1_M);
  	return 0;
  }
  
  /* OneNAND programming functions */
  
  static void flash_onenand_read_id(dmov_s * cmdlist, unsigned *ptrlist)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	unsigned *data = ptrlist + 4;
  
  	data[0] = SFLASH_BCFG;
  	data[1] =
  	    SFLASH_PREPCMD(8, 0, 0, NAND_SFCMD_DATXS, NAND_SFCMD_ASYNC,
  			   NAND_SFCMD_REGRD);
  	data[2] = (ONENAND_DEVICE_ID << 16) | (ONENAND_MANUFACTURER_ID);
  	data[3] = (ONENAND_DATA_BUFFER_SIZE << 16) | (ONENAND_VERSION_ID);
  	data[4] =
  	    (ONENAND_AMOUNT_OF_BUFFERS << 16) | (ONENAND_BOOT_BUFFER_SIZE);
  	data[5] = (CLEAN_DATA_16 << 16) | (ONENAND_TECHNOLOGY);
  	data[6] = CLEAN_DATA_32;	//status
  	data[7] = CLEAN_DATA_32;	//register read
  	data[8] = CLEAN_DATA_32;	//register read
  	data[9] = CLEAN_DATA_32;	//register read
  	data[10] = CLEAN_DATA_32;	//register read
  	data[11] = 1;
  	data[12] = 0 | 4;
  
  	/* Setup controller in SFLASH mode */
  	cmd[0].cmd = 0 | CMD_OCB;
  	cmd[0].src = paddr(&data[0]);
  	cmd[0].dst = NAND_SFLASHC_BURST_CFG;
  	cmd[0].len = 4;
  
  	/* Enable data mover for controller */
  	cmd[1].cmd = 0;
  	cmd[1].src = paddr(&data[12]);
  	cmd[1].dst = NAND_FLASH_CHIP_SELECT;
  	cmd[1].len = 4;
  
  	/* Setup SFLASHC_CMD with xfers in async mode */
  	cmd[2].cmd = DST_CRCI_NAND_CMD;
  	cmd[2].src = paddr(&data[1]);
  	cmd[2].dst = NAND_SFLASHC_CMD;
  	cmd[2].len = 4;
  
  	/* Setup to read device information */
  	cmd[3].cmd = 0;
  	cmd[3].src = paddr(&data[2]);
  	cmd[3].dst = NAND_ADDR0;
  	cmd[3].len = 8;
  
  	cmd[4].cmd = 0;
  	cmd[4].src = paddr(&data[4]);
  	cmd[4].dst = NAND_ADDR2;
  	cmd[4].len = 8;
  
  	/* Set execute bit */
  	cmd[5].cmd = 0;
  	cmd[5].src = paddr(&data[11]);
  	cmd[5].dst = NAND_SFLASHC_EXEC_CMD;
  	cmd[5].len = 4;
  
  	/* Check status */
  	cmd[6].cmd = SRC_CRCI_NAND_DATA;
  	cmd[6].src = NAND_SFLASHC_STATUS;
  	cmd[6].dst = paddr(&data[6]);
  	cmd[6].len = 4;
  
  	/* Read result device registers */
  	cmd[7].cmd = 0 | CMD_OCU | CMD_LC;
  	cmd[7].src = NAND_GENP_REG0;
  	cmd[7].dst = paddr(&data[7]);
  	cmd[7].len = 16;
  
  	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  #if VERBOSE
  	dprintf(INFO, "status: %x\n", data[6]);
  #endif
  
  	flash_info.id = data[7];
  	flash_info.vendor = data[7] & CLEAN_DATA_16;
  	flash_info.device = (data[7] >> 16) & CLEAN_DATA_16;
  	return;
  }
  
  struct data_onenand_erase {
  	unsigned sfbcfg;
  	unsigned sfcmd[4];
  	unsigned sfexec;
  	unsigned sfstat[4];
  	unsigned addr0;
  	unsigned addr1;
  	unsigned addr2;
  	unsigned addr3;
  	unsigned addr4;
  	unsigned addr5;
  	unsigned addr6;
  	unsigned data0;
  	unsigned data1;
  	unsigned data2;
  	unsigned data3;
  	unsigned data4;
  	unsigned data5;
  	unsigned data6;
  };
  
  static int _flash_onenand_read_page(dmov_s * cmdlist, unsigned *ptrlist,
  				    unsigned page, void *_addr,
  				    void *_spareaddr, unsigned raw_mode);
  
  static int
  flash_onenand_block_isbad(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	unsigned char page_data[2112];
  	unsigned char *oobptr = &(page_data[2048]);
  
  	/* Going to first page of the block */
  	if (page & num_pages_per_blk_mask)
  		page = page - (page & num_pages_per_blk_mask);
  
  	/* Reading page in raw mode */
  	if (_flash_onenand_read_page(cmdlist, ptrlist, page, page_data, 0, 1))
  		return 1;
  
  	/* Checking if block is bad */
  	if ((oobptr[0] != 0xFF) || (oobptr[1] != 0xFF) ||
  	    (oobptr[16] != 0xFF) || (oobptr[17] != 0xFF) ||
  	    (oobptr[32] != 0xFF) || (oobptr[33] != 0xFF) ||
  	    (oobptr[48] != 0xFF) || (oobptr[49] != 0xFF)) {
  		return 1;
  	}
  	return 0;
  }
  
  static int
  flash_onenand_erase_block(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	struct data_onenand_erase *data = (void *)ptrlist + 4;
  	int isbad = 0;
  	unsigned erasesize = (flash_pagesize * num_pages_per_blk);
  	unsigned onenand_startaddr1 =
  	    DEVICE_FLASHCORE_0 | (page * flash_pagesize) / erasesize;
  	unsigned onenand_startaddr8 = 0x0000;
  	unsigned onenand_startaddr2 = DEVICE_BUFFERRAM_0 << 15;
  	unsigned onenand_startbuffer = DATARAM0_0 << 8;
  
  	unsigned controller_status;
  	unsigned interrupt_status;
  	unsigned ecc_status;
  
  	if ((page * flash_pagesize) & (erasesize - 1))
  		return -1;
  
  	/* Check for bad block and erase only if block is not marked bad */
  	isbad = flash_onenand_block_isbad(cmdlist, ptrlist, page);
  	if (isbad) {
  		dprintf(INFO, "skipping @ %d (bad block)\n",
  			page / num_pages_per_blk);
  		return -1;
  	}
  
  	/*Erase block */
  	onenand_startaddr1 = DEVICE_FLASHCORE_0 |
  	    ((page * flash_pagesize) / (erasesize));
  	onenand_startaddr8 = 0x0000;
  	onenand_startaddr2 = DEVICE_BUFFERRAM_0 << 15;
  	onenand_startbuffer = DATARAM0_0 << 8;
  
  	data->sfbcfg = SFLASH_BCFG;
  	data->sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_INTHI);
  	data->sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGRD);
  	data->sfcmd[3] = SFLASH_PREPCMD(4, 10, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfexec = 1;
  	data->sfstat[0] = CLEAN_DATA_32;
  	data->sfstat[1] = CLEAN_DATA_32;
  	data->sfstat[2] = CLEAN_DATA_32;
  	data->sfstat[3] = CLEAN_DATA_32;
  	data->addr0 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr1 =
  	    (ONENAND_START_ADDRESS_8 << 16) | (ONENAND_START_ADDRESS_1);
  	data->addr2 = (ONENAND_START_BUFFER << 16) | (ONENAND_START_ADDRESS_2);
  	data->addr3 = (ONENAND_ECC_STATUS << 16) | (ONENAND_COMMAND);
  	data->addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
  	    (ONENAND_INTERRUPT_STATUS);
  	data->addr5 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr6 =
  	    (ONENAND_START_ADDRESS_3 << 16) | (ONENAND_START_ADDRESS_1);
  	data->data0 = (ONENAND_CLRINTR << 16) | (ONENAND_SYSCFG1_ECCENA);
  	data->data1 = (onenand_startaddr8 << 16) | (onenand_startaddr1);
  	data->data2 = (onenand_startbuffer << 16) | (onenand_startaddr2);
  	data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDERAS);
  	data->data4 = (CLEAN_DATA_16 << 16) | (CLEAN_DATA_16);
  	data->data5 = (ONENAND_CLRINTR << 16) | (ONENAND_SYSCFG1_ECCENA);
  	data->data6 = (ONENAND_STARTADDR3_RES << 16) | (ONENAND_STARTADDR1_RES);
  
  	/***************************************************************/
  	/* Write the necessary address registers in the onenand device */
  	/***************************************************************/
  
  	/* Enable and configure the SFlash controller */
  	cmd->cmd = 0 | CMD_OCB;
  	cmd->src = paddr(&data->sfbcfg);
  	cmd->dst = NAND_SFLASHC_BURST_CFG;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[0]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the ADDR0 and ADDR1 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr0);
  	cmd->dst = NAND_ADDR0;
  	cmd->len = 8;
  	cmd++;
  
  	/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr2);
  	cmd->dst = NAND_ADDR2;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the ADDR6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr6);
  	cmd->dst = NAND_ADDR6;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the GENP0, GENP1, GENP2, GENP3, GENP4 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data0);
  	cmd->dst = NAND_GENP_REG0;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the FLASH_DEV_CMD4,5,6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data4);
  	cmd->dst = NAND_DEV_CMD4;
  	cmd->len = 12;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[0]);
  	cmd->len = 4;
  	cmd++;
  
  	/***************************************************************/
  	/* Wait for the interrupt from the Onenand device controller    */
  	/***************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[1]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[1]);
  	cmd->len = 4;
  	cmd++;
  
  	/***************************************************************/
  	/* Read the necessary status registers from the onenand device */
  	/***************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[2]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[2]);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the GENP3 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_GENP_REG3;
  	cmd->dst = paddr(&data->data3);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the DEVCMD4 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_DEV_CMD4;
  	cmd->dst = paddr(&data->data4);
  	cmd->len = 4;
  	cmd++;
  
  	/***************************************************************/
  	/* Restore the necessary registers to proper values                     */
  	/***************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[3]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[3]);
  	cmd->len = 4;
  	cmd++;
  
  	ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  	ecc_status = (data->data3 >> 16) & 0x0000FFFF;
  	interrupt_status = (data->data4 >> 0) & 0x0000FFFF;
  	controller_status = (data->data4 >> 16) & 0x0000FFFF;
  
  #if VERBOSE
  	dprintf(INFO, "\n%s: sflash status %x %x %x %x\n", __func__,
  		data->sfstat[0],
  		data->sfstat[1], data->sfstat[2], data->sfstat[3]);
  
  	dprintf(INFO, "%s: controller_status = %x\n", __func__,
  		controller_status);
  	dprintf(INFO, "%s: interrupt_status = %x\n", __func__,
  		interrupt_status);
  	dprintf(INFO, "%s: ecc_status = %x\n", __func__, ecc_status);
  #endif
  	/* Check for errors, protection violations etc */
  	if ((controller_status != 0)
  	    || (data->sfstat[0] & 0x110)
  	    || (data->sfstat[1] & 0x110)
  	    || (data->sfstat[2] & 0x110) || (data->sfstat[3] & 0x110)) {
  		dprintf(CRITICAL, "%s: ECC/MPU/OP error\n", __func__);
  		return -1;
  	}
  
  #if VERBOSE
  	dprintf(INFO, "status: %x\n", data[5]);
  #endif
  
  	return 0;
  }
  
  struct data_onenand_read {
  	unsigned sfbcfg;
  	unsigned sfcmd[9];
  	unsigned sfexec;
  	unsigned sfstat[9];
  	unsigned addr0;
  	unsigned addr1;
  	unsigned addr2;
  	unsigned addr3;
  	unsigned addr4;
  	unsigned addr5;
  	unsigned addr6;
  	unsigned data0;
  	unsigned data1;
  	unsigned data2;
  	unsigned data3;
  	unsigned data4;
  	unsigned data5;
  	unsigned data6;
  	unsigned macro[5];
  };
  
  static int
  _flash_onenand_read_page(dmov_s * cmdlist, unsigned *ptrlist,
  			 unsigned page, void *_addr, void *_spareaddr,
  			 unsigned raw_mode)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	struct data_onenand_read *data = (void *)(ptrlist + 4);
  	unsigned addr = (unsigned)_addr;
  	unsigned curr_addr = (unsigned)_addr;
  #if VERBOSE
  	unsigned spareaddr = (unsigned)_spareaddr;
  #endif
  	unsigned i;
  	unsigned erasesize = (flash_pagesize * num_pages_per_blk);
  	unsigned writesize = flash_pagesize;
  
  	unsigned onenand_startaddr1 = DEVICE_FLASHCORE_0 |
  	    ((unsigned)(page * flash_pagesize) / erasesize);
  	unsigned onenand_startaddr8 = (((unsigned)(page * flash_pagesize) &
  					(erasesize - 1)) / writesize) << 2;
  	unsigned onenand_startaddr2 = DEVICE_BUFFERRAM_0 << 15;
  	unsigned onenand_startbuffer = DATARAM0_0 << 8;
  	unsigned onenand_sysconfig1 = (raw_mode == 1) ? ONENAND_SYSCFG1_ECCDIS :
  	    ONENAND_SYSCFG1_ECCENA;
  
  	unsigned controller_status;
  	unsigned interrupt_status;
  	unsigned ecc_status;
  	if (raw_mode != 1) {
  		int isbad = 0;
  		isbad = flash_onenand_block_isbad(cmdlist, ptrlist, page);
  		if (isbad)
  			return -2;
  	}
  	//static int oobfree_offset[8] = {2, 14, 18, 30, 34, 46, 50, 62};
  	//static int oobfree_length[8] = {3, 2, 3, 2, 3, 2, 3, 2};
  
  	data->sfbcfg = SFLASH_BCFG;
  	data->sfcmd[0] = SFLASH_PREPCMD(7, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfcmd[1] = SFLASH_PREPCMD(0, 0, 32,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_INTHI);
  	data->sfcmd[2] = SFLASH_PREPCMD(3, 7, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGRD);
  	data->sfcmd[3] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATRD);
  	data->sfcmd[4] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATRD);
  	data->sfcmd[5] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATRD);
  	data->sfcmd[6] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATRD);
  	data->sfcmd[7] = SFLASH_PREPCMD(32, 0, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATRD);
  	data->sfcmd[8] = SFLASH_PREPCMD(4, 10, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfexec = 1;
  	data->sfstat[0] = CLEAN_DATA_32;
  	data->sfstat[1] = CLEAN_DATA_32;
  	data->sfstat[2] = CLEAN_DATA_32;
  	data->sfstat[3] = CLEAN_DATA_32;
  	data->sfstat[4] = CLEAN_DATA_32;
  	data->sfstat[5] = CLEAN_DATA_32;
  	data->sfstat[6] = CLEAN_DATA_32;
  	data->sfstat[7] = CLEAN_DATA_32;
  	data->sfstat[8] = CLEAN_DATA_32;
  
  	data->addr0 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr1 =
  	    (ONENAND_START_ADDRESS_8 << 16) | (ONENAND_START_ADDRESS_1);
  	data->addr2 = (ONENAND_START_BUFFER << 16) | (ONENAND_START_ADDRESS_2);
  	data->addr3 = (ONENAND_ECC_STATUS << 16) | (ONENAND_COMMAND);
  	data->addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
  	    (ONENAND_INTERRUPT_STATUS);
  	data->addr5 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr6 =
  	    (ONENAND_START_ADDRESS_3 << 16) | (ONENAND_START_ADDRESS_1);
  	data->data0 = (ONENAND_CLRINTR << 16) | (onenand_sysconfig1);
  	data->data1 = (onenand_startaddr8 << 16) | (onenand_startaddr1);
  	data->data2 = (onenand_startbuffer << 16) | (onenand_startaddr2);
  	data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDLOADSPARE);
  	data->data4 = (CLEAN_DATA_16 << 16) | (CLEAN_DATA_16);
  	data->data5 = (ONENAND_CLRINTR << 16) | (ONENAND_SYSCFG1_ECCENA);
  	data->data6 = (ONENAND_STARTADDR3_RES << 16) | (ONENAND_STARTADDR1_RES);
  	data->macro[0] = 0x0200;
  	data->macro[1] = 0x0300;
  	data->macro[2] = 0x0400;
  	data->macro[3] = 0x0500;
  	data->macro[4] = 0x8010;
  
  	/*************************************************************/
  	/* Write necessary address registers in the onenand device      */
  	/*************************************************************/
  
  	/* Enable and configure the SFlash controller */
  	cmd->cmd = 0 | CMD_OCB;
  	cmd->src = paddr(&data->sfbcfg);
  	cmd->dst = NAND_SFLASHC_BURST_CFG;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[0]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the ADDR0 and ADDR1 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr0);
  	cmd->dst = NAND_ADDR0;
  	cmd->len = 8;
  	cmd++;
  
  	/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr2);
  	cmd->dst = NAND_ADDR2;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the ADDR6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr6);
  	cmd->dst = NAND_ADDR6;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the GENP0, GENP1, GENP2, GENP3 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data0);
  	cmd->dst = NAND_GENP_REG0;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the FLASH_DEV_CMD4,5,6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data4);
  	cmd->dst = NAND_DEV_CMD4;
  	cmd->len = 12;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[0]);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Wait for the interrupt from the Onenand device controller */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[1]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[1]);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Read necessary status registers from the onenand device      */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[2]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[2]);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the GENP3 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_GENP_REG3;
  	cmd->dst = paddr(&data->data3);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the DEVCMD4 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_DEV_CMD4;
  	cmd->dst = paddr(&data->data4);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Read the data ram area from the onenand buffer ram           */
  	/*************************************************************/
  
  	if (addr) {
  
  		data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDLOAD);
  
  		for (i = 0; i < 4; i++) {
  
  			/* Block on cmd ready and write CMD register */
  			cmd->cmd = DST_CRCI_NAND_CMD;
  			cmd->src = paddr(&data->sfcmd[3 + i]);
  			cmd->dst = NAND_SFLASHC_CMD;
  			cmd->len = 4;
  			cmd++;
  
  			/* Write the MACRO1 register */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->macro[i]);
  			cmd->dst = NAND_MACRO1_REG;
  			cmd->len = 4;
  			cmd++;
  
  			/* Kick the execute command */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->sfexec);
  			cmd->dst = NAND_SFLASHC_EXEC_CMD;
  			cmd->len = 4;
  			cmd++;
  
  			/* Block on data rdy, & read status register */
  			cmd->cmd = SRC_CRCI_NAND_DATA;
  			cmd->src = NAND_SFLASHC_STATUS;
  			cmd->dst = paddr(&data->sfstat[3 + i]);
  			cmd->len = 4;
  			cmd++;
  
  			/* Transfer nand ctlr buf contents to usr buf */
  			cmd->cmd = 0;
  			cmd->src = NAND_FLASH_BUFFER;
  			cmd->dst = curr_addr;
  			cmd->len = 512;
  			curr_addr += 512;
  			cmd++;
  		}
  	}
  
  	/* Read oob bytes in Raw Mode */
  	if (raw_mode == 1) {
  		/* Block on cmd ready and write CMD register */
  		cmd->cmd = DST_CRCI_NAND_CMD;
  		cmd->src = paddr(&data->sfcmd[7]);
  		cmd->dst = NAND_SFLASHC_CMD;
  		cmd->len = 4;
  		cmd++;
  
  		/* Write the MACRO1 register */
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->macro[4]);
  		cmd->dst = NAND_MACRO1_REG;
  		cmd->len = 4;
  		cmd++;
  
  		/* Kick the execute command */
  		cmd->cmd = 0;
  		cmd->src = paddr(&data->sfexec);
  		cmd->dst = NAND_SFLASHC_EXEC_CMD;
  		cmd->len = 4;
  		cmd++;
  
  		/* Block on data rdy, & read status register */
  		cmd->cmd = SRC_CRCI_NAND_DATA;
  		cmd->src = NAND_SFLASHC_STATUS;
  		cmd->dst = paddr(&data->sfstat[7]);
  		cmd->len = 4;
  		cmd++;
  
  		/* Transfer nand ctlr buf contents to usr buf */
  		cmd->cmd = 0;
  		cmd->src = NAND_FLASH_BUFFER;
  		cmd->dst = curr_addr;
  		cmd->len = 64;
  		curr_addr += 64;
  		cmd++;
  	}
  
  	/*************************************************************/
  	/* Restore the necessary registers to proper values               */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[8]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[8]);
  	cmd->len = 4;
  	cmd++;
  
  	ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  	ecc_status = (data->data3 >> 16) & 0x0000FFFF;
  	interrupt_status = (data->data4 >> 0) & 0x0000FFFF;
  	controller_status = (data->data4 >> 16) & 0x0000FFFF;
  
  #if VERBOSE
  	dprintf(INFO, "\n%s: sflash status %x %x %x %x %x %x %x"
  		"%x %x\n", __func__,
  		data->sfstat[0],
  		data->sfstat[1],
  		data->sfstat[2],
  		data->sfstat[3],
  		data->sfstat[4],
  		data->sfstat[5], data->sfstat[6], data->sfstat[7]);
  
  	dprintf(INFO, "%s: controller_status = %x\n", __func__,
  		controller_status);
  	dprintf(INFO, "%s: interrupt_status = %x\n", __func__,
  		interrupt_status);
  	dprintf(INFO, "%s: ecc_status = %x\n", __func__, ecc_status);
  #endif
  	/* Check for errors, protection violations etc */
  	if ((controller_status != 0)
  	    || (data->sfstat[0] & 0x110)
  	    || (data->sfstat[1] & 0x110)
  	    || (data->sfstat[2] & 0x110)
  	    || ((data->sfstat[3] & 0x110) && (addr))
  	    || ((data->sfstat[4] & 0x110) && (addr))
  	    || ((data->sfstat[5] & 0x110) &&
  		(addr)) || ((data->sfstat[6] & 0x110) && (addr))) {
  		dprintf(INFO, "%s: ECC/MPU/OP error\n", __func__);
  		return -1;
  	}
  #if VERBOSE
  	dprintf(INFO, "read page %d: status: %x %x %x %x\n",
  		page, data[5], data[6], data[7], data[8]);
  	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
  
  	return 0;
  }
  
  struct data_onenand_write {
  	unsigned sfbcfg;
  	unsigned sfcmd[9];
  	unsigned sfexec;
  	unsigned sfstat[9];
  	unsigned addr0;
  	unsigned addr1;
  	unsigned addr2;
  	unsigned addr3;
  	unsigned addr4;
  	unsigned addr5;
  	unsigned addr6;
  	unsigned data0;
  	unsigned data1;
  	unsigned data2;
  	unsigned data3;
  	unsigned data4;
  	unsigned data5;
  	unsigned data6;
  	unsigned macro[5];
  };
  
  static int
  _flash_onenand_write_page(dmov_s * cmdlist, unsigned *ptrlist,
  			  unsigned page, const void *_addr,
  			  const void *_spareaddr, unsigned raw_mode)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	struct data_onenand_write *data = (void *)(ptrlist + 4);
  	unsigned addr = (unsigned)_addr;
  	unsigned addr_curr = (unsigned)_addr;
  	char *spareaddr = (char *)_spareaddr;
  	unsigned i, j, k;
  
  	unsigned erasesize = (flash_pagesize * num_pages_per_blk);
  	unsigned writesize = flash_pagesize;
  
  	unsigned onenand_startaddr1 = (page * flash_pagesize) / erasesize;
  	unsigned onenand_startaddr8 = (((unsigned)(page * flash_pagesize) &
  					(erasesize - 1)) / writesize) << 2;
  	unsigned onenand_startaddr2 = DEVICE_BUFFERRAM_0 << 15;
  	unsigned onenand_startbuffer = DATARAM0_0 << 8;
  	unsigned onenand_sysconfig1 = (raw_mode == 1) ? ONENAND_SYSCFG1_ECCDIS :
  	    ONENAND_SYSCFG1_ECCENA;
  
  	unsigned controller_status;
  	unsigned interrupt_status;
  	unsigned ecc_status;
  
  	char flash_oob[64];
  
  	unsigned oobfree_offset[8] = { 2, 14, 18, 30, 34, 46, 50, 62 };
  	unsigned oobfree_length[8] = { 3, 2, 3, 2, 3, 2, 3, 2 };
  
  	for (i = 0; i < 64; i++)
  		flash_oob[i] = 0xFF;
  
  	data->sfbcfg = SFLASH_BCFG;
  	data->sfcmd[0] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATWR);
  	data->sfcmd[1] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATWR);
  	data->sfcmd[2] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATWR);
  	data->sfcmd[3] = SFLASH_PREPCMD(256, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATWR);
  	data->sfcmd[4] = SFLASH_PREPCMD(32, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_DATWR);
  	data->sfcmd[5] = SFLASH_PREPCMD(7, 0, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfcmd[6] = SFLASH_PREPCMD(0, 0, 32,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_INTHI);
  	data->sfcmd[7] = SFLASH_PREPCMD(3, 7, 0,
  					NAND_SFCMD_DATXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGRD);
  	data->sfcmd[8] = SFLASH_PREPCMD(4, 10, 0,
  					NAND_SFCMD_CMDXS,
  					NAND_SFCMD_ASYNC, NAND_SFCMD_REGWR);
  	data->sfexec = 1;
  
  	data->sfstat[0] = CLEAN_DATA_32;
  	data->sfstat[1] = CLEAN_DATA_32;
  	data->sfstat[2] = CLEAN_DATA_32;
  	data->sfstat[3] = CLEAN_DATA_32;
  	data->sfstat[4] = CLEAN_DATA_32;
  	data->sfstat[5] = CLEAN_DATA_32;
  	data->sfstat[6] = CLEAN_DATA_32;
  	data->sfstat[7] = CLEAN_DATA_32;
  	data->sfstat[8] = CLEAN_DATA_32;
  	data->addr0 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr1 =
  	    (ONENAND_START_ADDRESS_8 << 16) | (ONENAND_START_ADDRESS_1);
  	data->addr2 = (ONENAND_START_BUFFER << 16) | (ONENAND_START_ADDRESS_2);
  	data->addr3 = (ONENAND_ECC_STATUS << 16) | (ONENAND_COMMAND);
  	data->addr4 = (ONENAND_CONTROLLER_STATUS << 16) |
  	    (ONENAND_INTERRUPT_STATUS);
  	data->addr5 =
  	    (ONENAND_INTERRUPT_STATUS << 16) | (ONENAND_SYSTEM_CONFIG_1);
  	data->addr6 =
  	    (ONENAND_START_ADDRESS_3 << 16) | (ONENAND_START_ADDRESS_1);
  	data->data0 = (ONENAND_CLRINTR << 16) | (onenand_sysconfig1);
  	data->data1 = (onenand_startaddr8 << 16) | (onenand_startaddr1);
  	data->data2 = (onenand_startbuffer << 16) | (onenand_startaddr2);
  	data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDPROGSPARE);
  	data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDPROGSPARE);
  	data->data4 = (CLEAN_DATA_16 << 16) | (CLEAN_DATA_16);
  	data->data5 = (ONENAND_CLRINTR << 16) | (ONENAND_SYSCFG1_ECCENA);
  	data->data6 = (ONENAND_STARTADDR3_RES << 16) | (ONENAND_STARTADDR1_RES);
  	data->macro[0] = 0x0200;
  	data->macro[1] = 0x0300;
  	data->macro[2] = 0x0400;
  	data->macro[3] = 0x0500;
  	data->macro[4] = 0x8010;
  
  	/*************************************************************/
  	/* Write the data ram area in the onenand buffer ram             */
  	/*************************************************************/
  
  	/* Enable and configure the SFlash controller */
  	cmd->cmd = 0 | CMD_OCB;
  	cmd->src = paddr(&data->sfbcfg);
  	cmd->dst = NAND_SFLASHC_BURST_CFG;
  	cmd->len = 4;
  	cmd++;
  
  	if (addr) {
  		data->data3 = (CLEAN_DATA_16 << 16) | (ONENAND_CMDPROG);
  
  		for (i = 0; i < 4; i++) {
  
  			/* Block on cmd ready and write CMD register */
  			cmd->cmd = DST_CRCI_NAND_CMD;
  			cmd->src = paddr(&data->sfcmd[i]);
  			cmd->dst = NAND_SFLASHC_CMD;
  			cmd->len = 4;
  			cmd++;
  
  			/* Trnsfr usr buf contents to nand ctlr buf */
  			cmd->cmd = 0;
  			cmd->src = paddr(addr_curr);
  			cmd->dst = NAND_FLASH_BUFFER;
  			cmd->len = 512;
  			if (!raw_mode)
  				addr_curr += 512;
  			cmd++;
  
  			/* Write the MACRO1 register */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->macro[i]);
  			cmd->dst = NAND_MACRO1_REG;
  			cmd->len = 4;
  			cmd++;
  
  			/* Kick the execute command */
  			cmd->cmd = 0;
  			cmd->src = paddr(&data->sfexec);
  			cmd->dst = NAND_SFLASHC_EXEC_CMD;
  			cmd->len = 4;
  			cmd++;
  
  			/* Block on data rdy, & read status register */
  			cmd->cmd = SRC_CRCI_NAND_DATA;
  			cmd->src = NAND_SFLASHC_STATUS;
  			cmd->dst = paddr(&data->sfstat[i]);
  			cmd->len = 4;
  			cmd++;
  
  		}
  	}
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[4]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	if (spareaddr) {
  		// Auto mode
  		for (i = 0, k = 0; i < 8; i++) {
  			for (j = 0; j < oobfree_length[i]; j++) {
  				flash_oob[j + oobfree_offset[i]] = spareaddr[k];
  				k++;
  			}
  		}
  
  		cmd->cmd = 0;
  		cmd->src = paddr(&flash_oob);
  		cmd->dst = NAND_FLASH_BUFFER;
  		cmd->len = 64;
  		cmd++;
  	}
  
  	if (raw_mode) {
  		cmd->cmd = 0;
  		cmd->src = paddr(addr_curr);
  		cmd->dst = NAND_FLASH_BUFFER;
  		cmd->len = 64;
  		cmd++;
  	}
  
  	/* Write the MACRO1 register */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->macro[4]);
  	cmd->dst = NAND_MACRO1_REG;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[4]);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Write necessary address registers in the onenand device      */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[5]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the ADDR0 and ADDR1 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr0);
  	cmd->dst = NAND_ADDR0;
  	cmd->len = 8;
  	cmd++;
  
  	/* Write the ADDR2 ADDR3 ADDR4 ADDR5 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr2);
  	cmd->dst = NAND_ADDR2;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the ADDR6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->addr6);
  	cmd->dst = NAND_ADDR6;
  	cmd->len = 4;
  	cmd++;
  
  	/* Write the GENP0, GENP1, GENP2, GENP3 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data0);
  	cmd->dst = NAND_GENP_REG0;
  	cmd->len = 16;
  	cmd++;
  
  	/* Write the FLASH_DEV_CMD4,5,6 registers */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->data4);
  	cmd->dst = NAND_DEV_CMD4;
  	cmd->len = 12;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[5]);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Wait for the interrupt from the Onenand device controller */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[6]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[6]);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Read necessary status registers from the onenand device      */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[7]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[7]);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the GENP3 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_GENP_REG3;
  	cmd->dst = paddr(&data->data3);
  	cmd->len = 4;
  	cmd++;
  
  	/* Read the DEVCMD4 register */
  	cmd->cmd = 0;
  	cmd->src = NAND_DEV_CMD4;
  	cmd->dst = paddr(&data->data4);
  	cmd->len = 4;
  	cmd++;
  
  	/*************************************************************/
  	/* Restore the necessary registers to proper values               */
  	/*************************************************************/
  
  	/* Block on cmd ready and write CMD register */
  	cmd->cmd = DST_CRCI_NAND_CMD;
  	cmd->src = paddr(&data->sfcmd[8]);
  	cmd->dst = NAND_SFLASHC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Kick the execute command */
  	cmd->cmd = 0;
  	cmd->src = paddr(&data->sfexec);
  	cmd->dst = NAND_SFLASHC_EXEC_CMD;
  	cmd->len = 4;
  	cmd++;
  
  	/* Block on data ready, and read the status register */
  	cmd->cmd = SRC_CRCI_NAND_DATA | CMD_OCU | CMD_LC;
  	cmd->src = NAND_SFLASHC_STATUS;
  	cmd->dst = paddr(&data->sfstat[8]);
  	cmd->len = 4;
  	cmd++;
  
  	ptr[0] = (paddr(cmdlist) >> 3) | CMD_PTR_LP;
  
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  
  	ecc_status = (data->data3 >> 16) & 0x0000FFFF;
  	interrupt_status = (data->data4 >> 0) & 0x0000FFFF;
  	controller_status = (data->data4 >> 16) & 0x0000FFFF;
  
  #if VERBOSE
  	dprintf(INFO, "\n%s: sflash status %x %x %x %x %x %x %x %x %x\n",
  		__func__, data->sfstat[0], data->sfstat[1], data->sfstat[2],
  		data->sfstat[3], data->sfstat[4], data->sfstat[5],
  		data->sfstat[6], data->sfstat[7], data->sfstat[8]);
  
  	dprintf(INFO, "%s: controller_status = %x\n", __func__,
  		controller_status);
  	dprintf(INFO, "%s: interrupt_status = %x\n", __func__,
  		interrupt_status);
  	dprintf(INFO, "%s: ecc_status = %x\n", __func__, ecc_status);
  #endif
  	/* Check for errors, protection violations etc */
  	if ((controller_status != 0)
  	    || (data->sfstat[5] & 0x110)
  	    || (data->sfstat[6] & 0x110)
  	    || (data->sfstat[7] & 0x110)
  	    || (data->sfstat[8] & 0x110)
  	    || ((data->sfstat[0] & 0x110) && (addr))
  	    || ((data->sfstat[1] & 0x110) && (addr))
  	    || ((data->sfstat[2] & 0x110) &&
  		(addr)) || ((data->sfstat[3] & 0x110) && (addr))) {
  		dprintf(CRITICAL, "%s: ECC/MPU/OP error\n", __func__);
  		return -1;
  	}
  
  	return 0;
  }
  
  static int
  flash_onenand_mark_badblock(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	memset(empty_buf, 0, 528);
  	/* Going to first page of the block */
  	if (page & num_pages_per_blk_mask)
  		page = page - (page & num_pages_per_blk_mask);
  	return _flash_onenand_write_page(cmdlist, ptrlist, page, empty_buf, 0,
  					 1);
  }
  
  static int
  flash_mark_badblock(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	switch (flash_info.type) {
  	case FLASH_8BIT_NAND_DEVICE:
  	case FLASH_16BIT_NAND_DEVICE:
  		return flash_nand_mark_badblock(cmdlist, ptrlist, page);
  	case FLASH_ONENAND_DEVICE:
  		return flash_onenand_mark_badblock(cmdlist, ptrlist, page);
  	default:
  		return -1;
  	}
  }
  
  unsigned flash_ctrl_hwinfo(dmov_s * cmdlist, unsigned *ptrlist)
  {
  	dmov_s *cmd = cmdlist;
  	unsigned *ptr = ptrlist;
  	unsigned *data = ptrlist + 4;
  
  	unsigned rv;
  
  	data[0] = 0xeeeeeeee;
  
  	cmd[0].cmd = CMD_LC | CMD_OCB | CMD_OCU;
  	cmd[0].src = NAND_HW_INFO;
  	cmd[0].dst = paddr(&data[0]);
  	cmd[0].len = 4;
  
  	ptr[0] = (paddr(cmd) >> 3) | CMD_PTR_LP;
  	dmov_exec_cmdptr(DMOV_NAND_CHAN, ptr);
  	rv = data[0];
  
  	return rv;
  }
  
  /* Wrapper functions */
  static void flash_read_id(dmov_s * cmdlist, unsigned *ptrlist)
  {
  	int dev_found = 0;
  	unsigned index;
  	uint32_t hwinfo;
  
  	// Try to read id
  	flash_nand_read_id(cmdlist, ptrlist);
  	// Check if we support the device
  	for (index = 1;
  	     index <
  	     (sizeof(supported_flash) / sizeof(struct flash_identification));
  	     index++) {
  		if ((flash_info.id & supported_flash[index].mask) ==
  		    (supported_flash[index].
  		     flash_id & (supported_flash[index].mask))) {
  			dev_found = 1;
  			break;
  		}
  	}
  
  	if (!dev_found) {
  		flash_onenand_read_id(cmdlist, ptrlist);
  		for (index = 1;
  		     index <
  		     (sizeof(supported_flash) /
  		      sizeof(struct flash_identification)); index++) {
  			if ((flash_info.id & supported_flash[index].mask) ==
  			    (supported_flash[index].
  			     flash_id & (supported_flash[index].mask))) {
  				dev_found = 1;
  				break;
  			}
  		}
  	}
  
  	if (dev_found) {
  		if (supported_flash[index].widebus)
  			flash_info.type = FLASH_16BIT_NAND_DEVICE;
  		else
  			flash_info.type = FLASH_8BIT_NAND_DEVICE;
  		if (supported_flash[index].onenand)
  			flash_info.type = FLASH_ONENAND_DEVICE;
  		flash_info.page_size = supported_flash[index].pagesize;
  		flash_pagesize = flash_info.page_size;
  		flash_info.block_size = supported_flash[index].blksize;
  		flash_info.spare_size = supported_flash[index].oobsize;
  		if (flash_info.block_size && flash_info.page_size) {
  			flash_info.num_blocks = supported_flash[index].density;
  			flash_info.num_blocks /= (flash_info.block_size);
  		} else {
  			flash_info.num_blocks = 0;
  		}
  		ASSERT(flash_info.num_blocks);
  		// Use this for getting the next/current blocks
  		num_pages_per_blk = flash_info.block_size / flash_pagesize;
  		num_pages_per_blk_mask = num_pages_per_blk - 1;
  
  		hwinfo = flash_ctrl_hwinfo(cmdlist, ptrlist);
  
  		//Look for 8bit BCH ECC Nand, TODO: ECC Correctability >= 8
  		if (((hwinfo == 0x307) || (hwinfo == 0x4030))
  		    && flash_info.id == 0x2600482c) {
  			enable_bch_ecc = 1;
  		}
  		return;
  	}
  	// Flash device is not supported, print flash device info and halt
  	if (dev_found == 0) {
  		dprintf(CRITICAL, "NAND device is not supported: nandid: 0x%x \
  			maker=0x%02x device=0x%02x\n", flash_info.id,
  			flash_info.vendor, flash_info.device);
  		ASSERT(0);
  	}
  	dprintf(INFO, "nandid: 0x%x maker=0x%02x device=0x%02x page_size=%d\n",
  		flash_info.id, flash_info.vendor, flash_info.device,
  		flash_info.page_size);
  	dprintf(INFO, "		spare_size=%d block_size=%d num_blocks=%d\n",
  		flash_info.spare_size, flash_info.block_size,
  		flash_info.num_blocks);
  }
  
  static int flash_erase_block(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	switch (flash_info.type) {
  	case FLASH_8BIT_NAND_DEVICE:
  	case FLASH_16BIT_NAND_DEVICE:
  		return flash_nand_erase_block(cmdlist, ptrlist, page);
  	case FLASH_ONENAND_DEVICE:
  		return flash_onenand_erase_block(cmdlist, ptrlist, page);
  	default:
  		return -1;
  	}
  }
  
  static int
  _flash_read_page(dmov_s * cmdlist, unsigned *ptrlist,
  		 unsigned page, void *_addr, void *_spareaddr)
  {
  	switch (flash_info.type) {
  	case FLASH_8BIT_NAND_DEVICE:
  	case FLASH_16BIT_NAND_DEVICE:
  		if (interleaved_mode)
  			return flash_nand_read_page_interleave(cmdlist, ptrlist,
  							       page, _addr,
  							       _spareaddr);
  		else
  			return _flash_nand_read_page(cmdlist, ptrlist, page,
  						     _addr, _spareaddr);
  	case FLASH_ONENAND_DEVICE:
  		return _flash_onenand_read_page(cmdlist, ptrlist, page, _addr,
  						_spareaddr, 0);
  	default:
  		return -1;
  	}
  }
  
  static int
  _flash_block_isbad(dmov_s * cmdlist, unsigned *ptrlist, unsigned page)
  {
  	switch (flash_info.type) {
  	case FLASH_8BIT_NAND_DEVICE:
  	case FLASH_16BIT_NAND_DEVICE:
  		return flash_nand_block_isbad(cmdlist, ptrlist, page);
  	case FLASH_ONENAND_DEVICE:
  		return flash_onenand_block_isbad(cmdlist, ptrlist, page);
  	default:
  		return -1;
  	}
  }
  
  static int
  _flash_write_page(dmov_s * cmdlist, unsigned *ptrlist,
  		  unsigned page, const void *_addr, const void *_spareaddr)
  {
  	switch (flash_info.type) {
  	case FLASH_8BIT_NAND_DEVICE:
  	case FLASH_16BIT_NAND_DEVICE:
  		if (interleaved_mode)
  			return flash_nand_write_page_interleave(cmdlist,
  								ptrlist, page,
  								_addr,
  								_spareaddr, 0);
  		else
  			return _flash_nand_write_page(cmdlist, ptrlist, page,
  						      _addr, _spareaddr, 0);
  	case FLASH_ONENAND_DEVICE:
  		return _flash_onenand_write_page(cmdlist, ptrlist, page, _addr,
  						 _spareaddr, 0);
  	default:
  		return -1;
  	}
  }
  
  static unsigned *flash_ptrlist;
  static dmov_s *flash_cmdlist;
  
  static struct ptable *flash_ptable = NULL;
  
  void flash_init(void)
  {
  	int i = 0;
  	ASSERT(flash_ptable == NULL);
  
  	flash_ptrlist = memalign(32, 1024);
  	flash_cmdlist = memalign(32, 1024);
  	flash_data = memalign(32, 4096 + 128);
  	flash_spare = memalign(32, 128);
  
  	if (flash_ptrlist == NULL || flash_cmdlist == NULL
  		|| flash_data == NULL || flash_spare == NULL)
  		ASSERT(0);
  
  	flash_read_id(flash_cmdlist, flash_ptrlist);
  	if ((FLASH_8BIT_NAND_DEVICE == flash_info.type)
  	    || (FLASH_16BIT_NAND_DEVICE == flash_info.type)) {
  		if (flash_nand_read_config(flash_cmdlist, flash_ptrlist)) {
  			dprintf(CRITICAL,
  				"ERROR: could not read CFG0/CFG1 state\n");
  			ASSERT(0);
  		}
  	}
  	/* Create a bad block table */
  	bbtbl =
  	    (unsigned int *)malloc(sizeof(unsigned int) *
  				   flash_info.num_blocks);
  	for (i = 0; i < flash_info.num_blocks; i++)
  		bbtbl[i] = -1;
  }
  
  struct ptable *flash_get_ptable(void)
  {
  	return flash_ptable;
  }
  
  void flash_set_ptable(struct ptable *new_ptable)
  {
  	ASSERT(flash_ptable == NULL && new_ptable != NULL);
  	flash_ptable = new_ptable;
  }
  
  struct flash_info *flash_get_info(void)
  {
  	return &flash_info;
  }
  
  int flash_erase(struct ptentry *ptn)
  {
  	unsigned block = ptn->start;
  	unsigned count = ptn->length;
  
  	set_nand_configuration(ptn->type);
  	while (count-- > 0) {
  		if (flash_erase_block
  		    (flash_cmdlist, flash_ptrlist, block * num_pages_per_blk)) {
  			dprintf(INFO, "cannot erase @ %d (bad block?)\n",
  				block);
  		}
  		block++;
  	}
  	return 0;
  }
  
  int
  flash_read_ext(struct ptentry *ptn, unsigned extra_per_page,
  	       unsigned offset, void *data, unsigned bytes)
  {
  	unsigned page =
  	    (ptn->start * num_pages_per_blk) + (offset / flash_pagesize);
  	unsigned lastpage = (ptn->start + ptn->length) * num_pages_per_blk;
  	unsigned count =
  	    (bytes + flash_pagesize - 1 + extra_per_page) / (flash_pagesize +
  							     extra_per_page);
  	unsigned *spare = (unsigned *)flash_spare;
  	unsigned errors = 0;
  	unsigned char *image = data;
  	unsigned current_block =
  	    (page - (page & num_pages_per_blk_mask)) / num_pages_per_blk;
  	unsigned start_block = ptn->start;
  	int result = 0;
  	int isbad = 0;
  	int start_block_count = 0;
  
  	set_nand_configuration(TYPE_APPS_PARTITION);
  
  	if (offset & (flash_pagesize - 1))
  		return -1;
  
  // Adjust page offset based on number of bad blocks from start to current page
  	if (start_block < current_block) {
  		start_block_count = (current_block - start_block);
  		while (start_block_count
  		       && (start_block < (ptn->start + ptn->length))) {
  			isbad =
  			    _flash_block_isbad(flash_cmdlist, flash_ptrlist,
  					       start_block * num_pages_per_blk);
  			if (isbad)
  				page += num_pages_per_blk;
  			else
  				start_block_count--;
  			start_block++;
  		}
  	}
  
  	while ((page < lastpage) && !start_block_count) {
  		if (count == 0) {
  			dprintf(INFO, "flash_read_image: success (%d errors)\n",
  				errors);
  			return 0;
  		}
  
  		result =
  		    _flash_read_page(flash_cmdlist, flash_ptrlist, page, image,
  				     spare);
  
  		if (result == -1) {
  			// bad page, go to next page
  			page++;
  			errors++;
  			continue;
  		} else if (result == -2) {
  			// bad block, go to next block same offset
  			page += num_pages_per_blk;
  			errors++;
  			continue;
  		}
  
  		page++;
  		image += flash_pagesize;
  		memcpy(image, spare, extra_per_page);
  		image += extra_per_page;
  		count -= 1;
  	}
  
  	/* could not find enough valid pages before we hit the end */
  	dprintf(INFO, "flash_read_image: failed (%d errors)\n", errors);
  	return 0xffffffff;
  }
  
  int
  flash_write(struct ptentry *ptn, unsigned write_extra_bytes, const void *data,
  	    unsigned bytes)
  {
  	unsigned page = ptn->start * num_pages_per_blk;
  	unsigned lastpage = (ptn->start + ptn->length) * num_pages_per_blk;
  	unsigned *spare = (unsigned *)flash_spare;
  	const unsigned char *image = data;
  	unsigned wsize;
  	unsigned n;
  	int r;
  
  	if(write_extra_bytes)
  		wsize = flash_pagesize + flash_info.spare_size;
  	else
  		wsize = flash_pagesize;
  
  	if ((flash_info.type == FLASH_ONENAND_DEVICE)
  	    && (ptn->type == TYPE_MODEM_PARTITION)) {
  		dprintf(CRITICAL, "flash_write_image: feature not supported\n");
  		return -1;
  	}
  
  	set_nand_configuration(ptn->type);
  	for (n = 0; n < 16; n++)
  		spare[n] = 0xffffffff;
  
  	while (bytes > 0) {
  		if (bytes < wsize) {
  			dprintf(CRITICAL,
  				"flash_write_image: image undersized (%d < %d)\n",
  				bytes, wsize);
  			return -1;
  		}
  		if (page >= lastpage) {
  			dprintf(CRITICAL, "flash_write_image: out of space\n");
  			return -1;
  		}
  
  		if ((page & num_pages_per_blk_mask) == 0) {
  			if (flash_erase_block
  			    (flash_cmdlist, flash_ptrlist, page)) {
  				dprintf(INFO,
  					"flash_write_image: bad block @ %d\n",
  					page / num_pages_per_blk);
  				page += num_pages_per_blk;
  				continue;
  			}
  		}
  
  		if (write_extra_bytes) {
  			r = _flash_write_page(flash_cmdlist, flash_ptrlist,
  					      page, image,
  					      image + flash_pagesize);
  		} else {
  			r = _flash_write_page(flash_cmdlist, flash_ptrlist,
  					      page, image, spare);
  		}
  		if (r) {
  			dprintf(INFO,
  				"flash_write_image: write failure @ page %d (src %d)\n",
  				page, image - (const unsigned char *)data);
  			image -= (page & num_pages_per_blk_mask) * wsize;
  			bytes += (page & num_pages_per_blk_mask) * wsize;
  			page &= ~num_pages_per_blk_mask;
  			if (flash_erase_block
  			    (flash_cmdlist, flash_ptrlist, page)) {
  				dprintf(INFO,
  					"flash_write_image: erase failure @ page %d\n",
  					page);
  			}
  			if (ptn->type != TYPE_MODEM_PARTITION) {
  				flash_mark_badblock(flash_cmdlist,
  						    flash_ptrlist, page);
  			}
  			dprintf(INFO,
  				"flash_write_image: restart write @ page %d (src %d)\n",
  				page, image - (const unsigned char *)data);
  			page += num_pages_per_blk;
  			continue;
  		}
  		page++;
  		image += wsize;
  		bytes -= wsize;
  	}
  
  	/* erase any remaining pages in the partition */
  	page = (page + num_pages_per_blk_mask) & (~num_pages_per_blk_mask);
  	while (page < lastpage) {
  		if (flash_erase_block(flash_cmdlist, flash_ptrlist, page)) {
  			dprintf(INFO, "flash_write_image: bad block @ %d\n",
  				page / num_pages_per_blk);
  		}
  		page += num_pages_per_blk;
  	}
  
  	dprintf(INFO, "flash_write_image: success\n");
  	return 0;
  }
  
  #if 0
  static int flash_read_page(unsigned page, void *data, void *extra)
  {
  	return _flash_read_page(flash_cmdlist, flash_ptrlist, page, data,
  				extra);
  }
  #endif
  
  unsigned flash_page_size(void)
  {
  	return flash_pagesize;
  }
  
  void enable_interleave_mode(int status)
  {
  	interleaved_mode = status;
  	if (status) {
  		flash_pagesize *= 2;
  		platform_config_interleaved_mode_gpios();
  	}
  	return;
  }
  
  int flash_ecc_bch_enabled()
  {
  	return enable_bch_ecc;
  }
  