zte's code,first commit

Change-Id: I9a04da59e459a9bc0d67f101f700d9d7dc8d681b
diff --git a/boot/common/src/uboot/drivers/mtd/nand/Makefile b/boot/common/src/uboot/drivers/mtd/nand/Makefile
new file mode 100644
index 0000000..3a5107a
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/Makefile
@@ -0,0 +1,58 @@
+#
+# (C) Copyright 2006
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	:= $(obj)libnand.o
+
+ifdef CONFIG_CMD_NAND
+COBJS-y += nand.o
+COBJS-y += nand_base.o
+COBJS-y += nand_bbt.o
+COBJS-y += nand_ecc.o
+COBJS-y += nand_util.o
+COBJS-y += denali.o
+COBJS-y += spi_nand.o
+COBJS-y += zxic_spifc.o
+COBJS-y += spi_nand_devices.o
+COBJS-y += spi_nand_debug.o
+COBJS-y += nand_ids.o
+endif
+
+COBJS	:= $(COBJS-y)
+SRCS	:= $(COBJS:.o=.c)
+OBJS	:= $(addprefix $(obj),$(COBJS))
+
+all:	$(LIB)
+
+$(LIB):	$(obj).depend $(OBJS)
+	$(call cmd_link_o_target, $(OBJS))
+
+#########################################################################
+
+# defines $(obj).depend target
+include $(SRCTREE)/rules.mk
+
+sinclude $(obj).depend
+
+#########################################################################
diff --git a/boot/common/src/uboot/drivers/mtd/nand/denali.c b/boot/common/src/uboot/drivers/mtd/nand/denali.c
new file mode 100644
index 0000000..c3b851b
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/denali.c
@@ -0,0 +1,1600 @@
+/*
+ * NAND Flash Controller Device Driver
+ * Copyright © 2009-2010, Intel Corporation and its suppliers.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+#include <malloc.h> 
+#include <asm/io.h> 
+#include <linux/mtd/mtd.h>
+#include <asm-generic/ioctl.h>
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/nand.h> 
+#include <linux/mtd/nand.h>
+#include "denali.h"
+#include <asm/arch/lsp_crpm.h>
+#include <boot_mode.h>
+
+ 
+/* DEBUG */
+#if	DENALI_DEBUG
+#define denali_debug(fmt,args...)	printf (fmt ,##args)
+#define denali_debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args);
+#else
+#define denali_debug(fmt,args...)
+#define denali_debugX(level,fmt,args...)
+#endif	/* DEBUG */
+
+
+
+/* We define a module parameter that allows the user to override
+ * the hardware and decide what timing mode should be used.
+ */
+#define NAND_DEFAULT_TIMINGS	-1
+
+//static int onfi_timing_mode = NAND_DEFAULT_TIMINGS;
+
+/* We define a macro here that combines all interrupts this driver uses into
+ * a single constant value, for convenience. */
+#define DENALI_IRQ_ALL	(INTR_STATUS__DMA_CMD_COMP | \
+			INTR_STATUS__ECC_ERR | \
+			INTR_STATUS__PROGRAM_FAIL | \
+			INTR_STATUS__LOAD_COMP | \
+			INTR_STATUS__PROGRAM_COMP | \
+			INTR_STATUS__TIME_OUT | \
+			INTR_STATUS__ERASE_FAIL | \
+			INTR_STATUS__RST_COMP | \
+			INTR_STATUS__ERASE_COMP)
+
+/* indicates whether or not the internal value for the flash bank is
+ * valid or not */
+#define CHIP_SELECT_INVALID	-1
+
+#define SUPPORT_8BITECC		1
+
+/* This macro divides two integers and rounds fractional values up
+ * to the nearest integer value. */
+#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y)))
+
+/* this macro allows us to convert from an MTD structure to our own
+ * device context (denali) structure.
+ */
+//#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd)
+
+/* These constants are defined by the driver to enable common driver
+ * configuration options. */
+#define SPARE_ACCESS		0x41
+#define MAIN_ACCESS		0x42
+#define MAIN_SPARE_ACCESS	0x43
+
+#define DENALI_READ	0
+#define DENALI_WRITE	0x100
+
+/* types of device accesses. We can issue commands and get status */
+#define COMMAND_CYCLE	0
+#define ADDR_CYCLE	1
+#define STATUS_CYCLE	2
+
+/* this is a helper macro that allows us to
+ * format the bank into the proper bits for the controller */
+#define BANK(x) ((x) << 24)
+
+#define true		    1
+#define false		    0
+
+extern struct nand_flash_device_para nand_flash_para[];
+extern struct mtd_info nand_info[];
+extern struct nand_chip nand_chip[];
+extern int flash_dmabuf_disable_flag; 
+
+
+struct denali_nand_info denali_info = {0};
+struct denali_nand_info *g_denali = &denali_info;
+struct nand_flash_device_para *g_nand_dev_info = NULL;
+
+/* forward declarations */
+//static void clear_interrupts(struct denali_nand_info *denali);
+//static void denali_irq_enable(struct denali_nand_info *denali,
+//							uint32_t int_mask);
+//static uint32_t read_interrupt_status(struct denali_nand_info *denali);
+
+/* Certain operations for the denali NAND controller use
+ * an indexed mode to read/write data. The operation is
+ * performed by writing the address value of the command
+ * to the device memory followed by the data. This function
+ * abstracts this common operation.
+*/
+static void index_addr(struct denali_nand_info *denali,
+				uint32_t address, uint32_t data)
+{
+	writel(address, denali->flash_mem);
+	writel(data, denali->flash_mem + 0x10);
+}
+
+/* Perform an indexed read of the device */
+static void index_addr_read_data(struct denali_nand_info *denali,
+				 uint32_t address, uint32_t *pdata)
+{
+	writel(address, denali->flash_mem);
+	*pdata = readl(denali->flash_mem + 0x10);
+}
+
+/* We need to buffer some data for some of the NAND core routines.
+ * The operations manage buffering that data. */
+static void reset_buf(struct denali_nand_info *denali)
+{
+	denali->buf.head = denali->buf.tail = 0;
+}
+
+static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte)
+{
+	BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf));
+	denali->buf.buf[denali->buf.tail++] = byte;
+}
+
+/* reads the status of the device */
+static void read_status(struct denali_nand_info *denali)
+{
+	uint32_t status, addr;     
+    addr = (uint32_t)MODE_11 | BANK(denali->flash_bank);
+	index_addr(denali, (uint32_t)addr | 0, 0x70);
+	index_addr_read_data(denali,(uint32_t)addr | 2,	&status);
+	write_byte_to_buf(denali, status);
+}
+
+/* resets a specific device connected to the core */
+static void reset_bank(struct denali_nand_info *denali)
+{
+//	uint32_t status_type = 0;
+//	uint32_t status_mask = INTR_STATUS__RST_COMP |
+//			    INTR_STATUS__TIME_OUT;
+
+//	clear_interrupts(denali);
+
+	writel(1 << denali->flash_bank, denali->flash_reg + DEVICE_RESET);
+
+    while (!(readl(denali->flash_reg +
+				INTR_STATUS(denali->flash_bank)) &
+			(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT)))
+    {
+
+		if (readl(denali->flash_reg + INTR_STATUS(denali->flash_bank)) &
+			INTR_STATUS__TIME_OUT)
+		{
+			debug("NAND Reset operation timed out on bank %d\n", denali->flash_bank);
+		}
+    }
+}
+
+/* Reset the flash controller */
+static uint16_t denali_nand_reset(struct denali_nand_info *denali)
+{
+	uint32_t i;
+
+	for (i = 0 ; i < denali->max_banks; i++)
+		writel(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
+		denali->flash_reg + INTR_STATUS(i));
+
+	for (i = 0 ; i < denali->max_banks; i++) 
+    {
+		writel(1 << i, denali->flash_reg + DEVICE_RESET);
+		while (!(readl(denali->flash_reg +
+				INTR_STATUS(i)) &
+			(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT))); //zhouqi fpga 4.27
+
+		if (readl(denali->flash_reg + INTR_STATUS(i)) &
+			INTR_STATUS__TIME_OUT)
+			debug("NAND Reset operation timed out on bank %d\n", i);
+	}
+
+	for (i = 0; i < denali->max_banks; i++)
+		writel(INTR_STATUS__RST_COMP | INTR_STATUS__TIME_OUT,
+			denali->flash_reg + INTR_STATUS(i));
+
+	return PASS;
+}
+
+
+/* determines how many NAND chips are connected to the controller. Note for
+ * Intel CE4100 devices we don't support more than one device.
+ */
+ #if 0 
+static void find_valid_banks(struct denali_nand_info *denali)
+{
+	uint32_t id[denali->max_banks];
+	int i;
+
+	denali->total_used_banks = 1;
+	for (i = 0; i < denali->max_banks; i++) 
+    {
+		index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90);
+		index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0);
+		index_addr_read_data(denali,
+				(uint32_t)(MODE_11 | (i << 24) | 2), &id[i]);
+
+		debug("Return 1st ID for bank[%d]: %x\n", i, id[i]);
+
+		if (i == 0) 
+        {
+			if (!(id[i] & 0x0ff))
+				break; /* WTF? */
+		} 
+        else 
+        {
+			if ((id[i] & 0x0ff) == (id[0] & 0x0ff))
+				denali->total_used_banks++;
+			else
+				break;
+		}
+	}
+	debug(	"denali->total_used_banks: %d\n", denali->total_used_banks);
+}
+#endif
+/*
+ * Use the configuration feature register to determine the maximum number of
+ * banks that the hardware supports.
+ */
+static void detect_max_banks(struct denali_nand_info *denali)
+{
+	//uint32_t features = readl(denali->flash_reg + FEATURES);
+
+	//denali->max_banks = 2 << (features & FEATURES__N_BANKS);
+	denali->max_banks = 1; //zhouqi for fpge 4.27 and for evb 7.19
+}
+
+
+static uint32_t detect_nand_bus_freq(void)
+{
+    uint32_t clk_reg = 0;
+
+	clk_reg = readl(0x01306050);
+	clk_reg &= 0xffffcfff;        /*MOD_CLK_SEL[13:12]=00,7520v2 NAND 104MHz*/
+    writel(clk_reg, 0x01306050);  
+    if((((readl(0x01306050))>>12) & 0x3) == 0)        
+        return 104;
+    else
+        return 26;      
+}
+
+#if 0
+static void detect_partition_feature(struct denali_nand_info *denali)
+{
+	/* For MRST platform, denali->fwblks represent the
+	 * number of blocks firmware is taken,
+	 * FW is in protect partition and MTD driver has no
+	 * permission to access it. So let driver know how many
+	 * blocks it can't touch.
+	 * */
+	if (readl(denali->flash_reg + FEATURES) & FEATURES__PARTITION) {
+		if ((readl(denali->flash_reg + PERM_SRC_ID(1)) &
+			PERM_SRC_ID__SRCID) == SPECTRA_PARTITION_ID) {
+			denali->fwblks =
+			    ((readl(denali->flash_reg + MIN_MAX_BANK(1)) &
+			      MIN_MAX_BANK__MIN_VALUE) *
+			     denali->blksperchip)
+			    +
+			    (readl(denali->flash_reg + MIN_BLK_ADDR(1)) &
+			    MIN_BLK_ADDR__VALUE);
+		} else
+			denali->fwblks = SPECTRA_START_BLOCK;
+	} else
+		denali->fwblks = SPECTRA_START_BLOCK;
+}
+#endif
+
+static void denali_nand_register_set(struct denali_nand_info *denali,
+                                                struct nand_flash_device_para * table)
+{
+	writel(table->pages_per_block, denali->flash_reg + PAGES_PER_BLOCK);
+    writel(table->bus_num, denali->flash_reg + DEVICE_WIDTH);
+    writel(table->page_size, denali->flash_reg + DEVICE_MAIN_AREA_SIZE);
+    writel(table->oob_size, denali->flash_reg + DEVICE_SPARE_AREA_SIZE);
+    
+    if(table->row_addr_num == 2)       
+        writel(1, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
+    else
+        writel(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES);
+
+    writel(table->page_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE);
+    writel(table->oob_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE);
+    writel(1, denali->flash_reg + ECC_ENABLE);
+
+    
+}
+
+
+static void denali_nand_timing_set(struct denali_nand_info *denali,
+                                                struct nand_flash_device_para * table)
+{
+	uint32_t bus_freq;
+    struct nand_flash_timing * timing = NULL;
+
+    timing = &(table->nand_timeing);
+    bus_freq = detect_nand_bus_freq();
+	writel(((timing->Twhr * bus_freq)/1000+2) | (((timing->Trr1 * bus_freq)/1000+2)<<8), 
+                denali->flash_reg + WE_2_RE);
+    writel(((timing->Tadl * bus_freq)/1000+2) | (((timing->Trr2 * bus_freq)/1000+2)<<8), 
+                denali->flash_reg + ADDR_2_DATA);
+    writel((timing->Trhw * bus_freq)/1000+2, denali->flash_reg + RE_2_WE);
+    writel((timing->Trp * bus_freq)/1000+2, denali->flash_reg + RDWR_EN_LO_CNT);
+    writel((timing->Treh * bus_freq)/1000+2, denali->flash_reg + RDWR_EN_HI_CNT);
+    writel((timing->Tcs * bus_freq)/1000+2, denali->flash_reg + CS_SETUP_CNT);
+    writel((timing->Trhz * bus_freq)/1000+2, denali->flash_reg + RE_2_RE);   
+}
+
+static void denali_set_intr_modes(struct denali_nand_info *denali,
+					uint16_t INT_ENABLE)
+{
+	if (INT_ENABLE)
+		writel(1, denali->flash_reg + GLOBAL_INT_ENABLE);
+	else
+		writel(0, denali->flash_reg + GLOBAL_INT_ENABLE);
+}
+
+/* validation function to verify that the controlling software is making
+ * a valid request
+ */
+static inline uint32_t is_flash_bank_valid(int flash_bank)
+{
+	return (flash_bank >= 0 && flash_bank < 4);
+}
+
+static void denali_irq_init(struct denali_nand_info *denali)
+{
+	uint32_t int_mask = 0;
+	int i;
+
+	/* Disable global interrupts */
+	denali_set_intr_modes(denali, false);
+
+	int_mask = DENALI_IRQ_ALL;
+
+	/* Clear all status bits */
+	for (i = 0; i < denali->max_banks; ++i)
+		writel(0xFFFF, denali->flash_reg + INTR_STATUS(i));
+}
+
+#define BANK(x) ((x) << 24)
+
+static uint32_t wait_for_ready(struct denali_nand_info *denali, uint32_t status_type)
+{
+    uint32_t status = 0;
+
+    while (!(readl(denali->flash_reg +
+				INTR_STATUS(denali->flash_bank)) & status_type));
+    status = readl(denali->flash_reg + INTR_STATUS(denali->flash_bank));
+
+    #if 0
+    while (!(readl(0x1207410) & status_type));
+    status = readl(0x1207410);
+    #endif
+    
+    if (status & INTR_STATUS__ECC_ERR )
+    {
+        printf ("    Deanli Nand Failed: ECC Error\n");
+    }
+    if (status & INTR_STATUS__TIME_OUT)
+    {
+        printf ("    Deanli Nand Failed: Time out\n");
+    }
+    if (status & INTR_STATUS__PROGRAM_FAIL)
+    {
+        printf ("    Deanli Nand Failed: Program Fail\n");
+    } 
+    
+    writew(0xffff, denali->flash_reg + INTR_STATUS(denali->flash_bank));
+	return status;
+}
+
+/* This helper function setups the registers for ECC and whether or not
+ * the spare area will be transferred. */
+static void setup_ecc_for_xfer(struct denali_nand_info *denali, uint32_t ecc_en,uint32_t transfer_spare)
+{
+	int ecc_en_flag = 0, transfer_spare_flag = 0;
+   #if ECC_TEST_VER
+   ecc_en_flag = 0;
+   #else
+	/* set ECC, transfer spare bits if needed */
+	ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0;
+   #endif
+	transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0;
+
+	/* Enable spare area/ECC per user's request. */
+	writel(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
+	writel(transfer_spare_flag,
+			denali->flash_reg + TRANSFER_SPARE_REG);
+}
+
+
+static void setup_ecc_the_end(struct denali_nand_info *denali, uint32_t ecc_en)
+{
+	int ecc_en_flag = 0;
+
+	/* set ECC, transfer spare bits if needed */
+	ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0;
+
+	/* Enable spare area/ECC per user's request. */
+	writel(ecc_en_flag, denali->flash_reg + ECC_ENABLE);
+}
+
+
+/* sends a pipeline command operation to the controller. See the Denali NAND
+ * controller's user guide for more information (section 4.2.3.6).
+ */
+static int denali_send_pipeline_cmd(struct denali_nand_info *denali,
+                            uint32_t ecc_en,
+							uint32_t transfer_spare,
+							int access_type,
+							int op)
+{
+	int status = PASS;
+	uint32_t addr = 0x0, cmd = 0x0, page_count = 1, status_type = 0,
+		 status_mask = 0;
+
+	if (op == DENALI_READ)
+		status_mask = INTR_STATUS__LOAD_COMP;
+	else if (op == DENALI_WRITE)
+		status_mask = 0;
+	else
+		BUG();
+
+	setup_ecc_for_xfer(denali, ecc_en, transfer_spare);
+
+	addr = BANK(denali->flash_bank) | denali->page;
+
+	if (op == DENALI_WRITE && access_type != SPARE_ACCESS) 
+    {
+		cmd = MODE_01 | addr;
+		writel(cmd, denali->flash_mem);
+	} 
+    else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) 
+    {
+		/* read spare area */
+		cmd = MODE_10 | addr;
+		index_addr(denali, (uint32_t)cmd, access_type);
+
+		cmd = MODE_01 | addr;
+		writel(cmd, denali->flash_mem);
+	} 
+    else if (op == DENALI_READ) 
+    {
+		/* setup page read request for access type */
+		cmd = MODE_10 | addr;
+		index_addr(denali, (uint32_t)cmd, access_type);
+
+		/* page 33 of the NAND controller spec indicates we should not
+		   use the pipeline commands in Spare area only mode. So we
+		   don't.
+		 */
+		if (access_type == SPARE_ACCESS) 
+        {
+			cmd = MODE_01 | addr;
+			writel(cmd, denali->flash_mem);
+		} 
+        else 
+        {
+			index_addr(denali, (uint32_t)cmd,
+					0x2000 | op | page_count);
+
+			/* wait for command to be accepted
+			 * can always use status0 bit as the
+			 * mask is identical for each
+			 * bank. */
+			status_type = wait_for_ready(denali, status_mask);
+
+			if (status_type == 0) 
+            {
+				debug("cmd, page, addr on timeout "
+						"(0x%x, 0x%x, 0x%x)\n",
+						cmd, denali->page, addr);
+				status = FAIL;
+			} 
+            else
+            {
+				cmd = MODE_01 | addr;
+				writel(cmd, denali->flash_mem);
+			}
+		}
+	}
+	return status;
+}
+
+/* helper function that simply writes a buffer to the flash */
+static int write_data_to_flash_mem(struct denali_nand_info *denali,
+							const uint8_t *buf,
+							int len)
+{
+	uint32_t i = 0, *buf32;
+
+	/* verify that the len is a multiple of 4. see comment in
+	 * read_data_from_flash_mem() */
+	BUG_ON((len % 4) != 0);
+
+	/* write the data to the flash memory */
+	buf32 = (uint32_t *)buf;
+	for (i = 0; i < len / 4; i++)
+		writel(*buf32++, denali->flash_mem + 0x10);
+	return i*4; /* intent is to return the number of bytes read */
+}
+
+/* helper function that simply reads a buffer from the flash */
+static int read_data_from_flash_mem(struct denali_nand_info *denali,
+								uint8_t *buf,
+								int len)
+{
+	uint32_t i = 0, *buf32;
+
+	/* we assume that len will be a multiple of 4, if not
+	 * it would be nice to know about it ASAP rather than
+	 * have random failures...
+	 * This assumption is based on the fact that this
+	 * function is designed to be used to read flash pages,
+	 * which are typically multiples of 4...
+	 */
+
+	BUG_ON((len % 4) != 0);
+
+	/* transfer the data from the flash */
+	buf32 = (uint32_t *)buf;
+	for (i = 0; i < len / 4; i++)
+		*buf32++ = readl(denali->flash_mem + 0x10);
+	return i*4; /* intent is to return the number of bytes read */
+}
+
+/* writes OOB data to the device */
+static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+{
+	struct denali_nand_info *denali = g_denali;
+	uint32_t status_type = 0;
+	uint32_t status_mask = INTR_STATUS__PROGRAM_COMP |
+						INTR_STATUS__PROGRAM_FAIL;
+	int status = 0, addr = 0x0, cmd = 0x0;
+
+	denali->page = page;
+
+    /* Modified by zhouqi for xxx, 2013/09/03 */
+	if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
+							DENALI_WRITE) == PASS) 
+    {
+		write_data_to_flash_mem(denali, buf, mtd->oobsize);
+
+		/* wait for operation to complete */
+		status_type = wait_for_ready(denali, status_mask);
+
+		if (status_type & INTR_STATUS__PROGRAM_FAIL)    //zhouqi
+        {
+			debug("OOB write failed\n");
+			status = -1;
+		}
+	} 
+    else 
+    {
+		debug("unable to send pipeline command\n");
+		status = -1;
+	}
+
+    /* Added by zhouqi for xxx, 2013/09/03 */
+    /* We set the device back to MAIN_ACCESS here as I observed
+	* instability with the controller if you do a block erase
+	* and the last transaction was a SPARE_ACCESS. Block erase
+	* is reliable (according to the MTD test infrastructure)
+	* if you are in MAIN_ACCESS.
+	*/
+	addr = BANK(denali->flash_bank) | denali->page;
+	cmd = MODE_10 | addr;
+	index_addr(denali, (uint32_t)cmd, MAIN_ACCESS);
+    /* End added. zhouqi, 2013/09/03 */
+        
+	return status;
+}
+
+/* reads OOB data from the device */
+static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page)
+{
+	struct denali_nand_info *denali = g_denali;
+	uint32_t status_mask = INTR_STATUS__LOAD_COMP | INTR_STATUS__TIME_OUT,  //zhouqi
+			 status_type = 0, addr = 0x0, cmd = 0x0;
+
+	denali->page = page;
+
+	if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS,
+							DENALI_READ) == PASS) 
+    {
+		read_data_from_flash_mem(denali, buf, mtd->oobsize);
+
+		/* wait for command to be accepted
+		 * can always use status0 bit as the mask is identical for each
+		 * bank. */
+		status_type = wait_for_ready(denali, status_mask);
+
+		if (status_type & INTR_STATUS__TIME_OUT)
+			debug("page on OOB timeout %d\n",denali->page);
+
+		/* We set the device back to MAIN_ACCESS here as I observed
+		 * instability with the controller if you do a block erase
+		 * and the last transaction was a SPARE_ACCESS. Block erase
+		 * is reliable (according to the MTD test infrastructure)
+		 * if you are in MAIN_ACCESS.
+		 */
+		addr = BANK(denali->flash_bank) | denali->page;
+		cmd = MODE_10 | addr;
+		index_addr(denali, (uint32_t)cmd, MAIN_ACCESS);
+	}
+}
+
+/* this function examines buffers to see if they contain data that
+ * indicate that the buffer is part of an erased region of flash.
+ */
+uint32_t is_erased(uint8_t *buf, int len)
+{
+	int i = 0;
+	for (i = 0; i < len; i++)
+		if (buf[i] != 0xFF)
+			return false;
+	return true;
+}
+#define ECC_SECTOR_SIZE 512
+
+#define ECC_SECTOR(x)	(((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12)
+#define ECC_BYTE(x)	(((x) & ECC_ERROR_ADDRESS__OFFSET))
+#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK)
+#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO__ERROR_TYPE))
+#define ECC_ERR_DEVICE(x)	(((x) & ERR_CORRECTION_INFO__DEVICE_NR) >> 8)
+#define ECC_LAST_ERR(x)		((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO)
+
+static uint32_t handle_ecc(struct denali_nand_info *denali, uint8_t *buf,
+					uint32_t status_type)
+{
+	int check_erased_page = 0;
+	uint32_t err_correction_value = 0;
+	uint32_t err_correction_info = 0;
+	
+#if 1
+    /*zx297520 use*/
+	if (status_type & INTR_STATUS__ECC_ERR) 
+	{
+        check_erased_page = 1;
+		
+	}
+	else
+	{
+	    
+		switch(denali->flash_bank)
+		{
+		    case 0:
+			err_correction_info = readl(denali->flash_reg +ERR_CORRECTION_INFO_B01);
+			err_correction_value = err_correction_info & ERR_CORRECTION_INFO_B01__MAX_ERRORS_B0;
+			break;
+
+			case 1:
+			err_correction_info = readl(denali->flash_reg +ERR_CORRECTION_INFO_B01);
+			err_correction_value = (err_correction_info & ERR_CORRECTION_INFO_B01__MAX_ERRORS_B1)>>8;
+			break;
+
+			case 2:
+			err_correction_info = readl(denali->flash_reg +ERR_CORRECTION_INFO_B23);
+			err_correction_value = err_correction_info & ERR_CORRECTION_INFO_B01__MAX_ERRORS_B2;
+			break;
+
+			case 3:
+			err_correction_info = readl(denali->flash_reg +ERR_CORRECTION_INFO_B23);
+			err_correction_value = (err_correction_info & ERR_CORRECTION_INFO_B01__MAX_ERRORS_B3)>>8;
+			break;
+
+			default:
+			break;
+				
+		}
+		if(err_correction_value)
+		    printk("correct %d bit errors on page %x.\n",err_correction_value,denali->page);
+		 
+				
+	}
+	return check_erased_page;
+#else
+		/* read the ECC errors. we'll ignore them for now */
+		uint32_t err_address = 0, err_correction_info = 0;
+		uint32_t err_byte = 0, err_sector = 0, err_device = 0;
+		uint32_t err_correction_value = 0;
+		denali_set_intr_modes(denali, false);
+
+		do 
+        {
+			err_address = readl(denali->flash_reg +
+						ECC_ERROR_ADDRESS);
+			err_sector = ECC_SECTOR(err_address);
+			err_byte = ECC_BYTE(err_address);
+
+			err_correction_info = readl(denali->flash_reg +
+						ERR_CORRECTION_INFO);
+			err_correction_value =
+				ECC_CORRECTION_VALUE(err_correction_info);
+			err_device = ECC_ERR_DEVICE(err_correction_info);
+
+			if (ECC_ERROR_CORRECTABLE(err_correction_info)) 
+            {
+				/* If err_byte is larger than ECC_SECTOR_SIZE,
+				 * means error happened in OOB, so we ignore
+				 * it. It's no need for us to correct it
+				 * err_device is represented the NAND error
+				 * bits are happened in if there are more
+				 * than one NAND connected.
+				 * */
+				if (err_byte < ECC_SECTOR_SIZE) 
+                {
+					int offset;
+					offset = (err_sector *
+							ECC_SECTOR_SIZE +
+							err_byte) *
+							denali->devnum +
+							err_device;
+					/* correct the ECC error */
+					buf[offset] ^= err_correction_value;
+					denali->mtd->ecc_stats.corrected++;
+				}
+			} else {
+				/* if the error is not correctable, need to
+				 * look at the page to see if it is an erased
+				 * page. if so, then it's not a real ECC error
+				 * */
+				check_erased_page = true;
+			}
+		} while (!ECC_LAST_ERR(err_correction_info));
+		/* Once handle all ecc errors, controller will triger
+		 * a ECC_TRANSACTION_DONE interrupt, so here just wait
+		 * for a while for this interrupt
+		 * */
+		while (!(read_interrupt_status(denali) &
+				INTR_STATUS__ECC_ERR))
+		clear_interrupts(denali);
+		denali_set_intr_modes(denali, false);		
+    #endif
+	}
+    
+
+
+
+/* programs the controller to either enable/disable DMA transfers */
+static void denali_enable_dma(struct denali_nand_info *denali, uint32_t en)
+{
+	uint32_t reg_val = 0x0;
+
+	if (en)
+		reg_val = DMA_ENABLE__FLAG;
+
+	writel(reg_val, denali->flash_reg + DMA_ENABLE);
+	readl(denali->flash_reg + DMA_ENABLE);
+}
+
+/* setups the HW to perform the data DMA */
+static void denali_setup_dma(struct denali_nand_info *denali, int op)
+                                        
+{
+	uint32_t mode = 0x0;
+	const int page_count = 1;
+	dma_addr_t addr = denali->buf.dma_buf; 
+
+	mode = MODE_10 | BANK(denali->flash_bank);
+
+	/* DMA is a four step process */
+
+	/* 1. setup transfer type and # of pages */
+	index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+
+	/* 2. set memory high address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200);
+
+	/* 3. set memory low address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300);
+
+	/* 4.  interrupt when complete, burst len = 64 bytes*/
+    //writel(NAND_BASE, MODE_10|0x10000|(4 << 8));//BurstLength =4
+	index_addr(denali, mode | 0x14000, 0x2400);     //zhouqi not interrupt 0X40
+}
+static void denali_setup_dma_buffer(struct denali_nand_info *denali, int op,char* buffer)
+                                        
+{
+	uint32_t mode = 0x0;
+	const int page_count = 1;
+	dma_addr_t addr = buffer; 
+
+	mode = MODE_10 | BANK(denali->flash_bank);
+
+	/* DMA is a four step process */
+
+	/* 1. setup transfer type and # of pages */
+	index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+
+	/* 2. set memory high address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200);
+
+	/* 3. set memory low address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300);
+
+	/* 4.  interrupt when complete, burst len = 64 bytes*/
+    //writel(NAND_BASE, MODE_10|0x10000|(4 << 8));//BurstLength =4
+	index_addr(denali, mode | 0x14000, 0x2400);     //zhouqi not interrupt 0X40
+}
+
+
+
+/* add by zhouqi */
+#if 0
+static void denali_setup_dma_derect(struct denali_nand_info *denali, int op,
+                                        dma_addr_t addr)    /* add by zhouqi */
+{
+	uint32_t mode = 0x0;
+	const int page_count = 1;
+//	dma_addr_t addr = denali->buf.dma_buf; 
+
+	mode = MODE_10 | BANK(denali->flash_bank);
+
+	/* DMA is a four step process */
+
+	/* 1. setup transfer type and # of pages */
+	index_addr(denali, mode | denali->page, 0x2000 | op | page_count);
+
+	/* 2. set memory high address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200);
+
+	/* 3. set memory low address bits 23:8 */
+	index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300);
+
+	/* 4.  interrupt when complete, burst len = 64 bytes*/
+    //writel(NAND_BASE, MODE_10|0x10000|(4 << 8));//BurstLength =4
+	index_addr(denali, mode | 0x14000, 0x2400);     //zhouqi not interrupt 0X40
+}
+#endif
+
+/* writes a page. user specifies type, and this function handles the
+ * configuration details. */
+static void write_page(struct mtd_info *mtd, struct nand_chip *chip,
+			const uint8_t *buf, uint32_t raw_xfer)
+{
+	struct denali_nand_info *denali = g_denali;
+
+	uint32_t status_type = 0;
+	uint32_t status_mask = INTR_STATUS__DMA_CMD_COMP |
+						INTR_STATUS__PROGRAM_FAIL;
+
+	/* if it is a raw xfer, we want to disable ecc, and send
+	 * the spare area.
+	 * !raw_xfer - enable ecc
+	 * raw_xfer - transfer spare
+	 */
+	setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer);
+
+	/* copy buffer into DMA buffer */
+	memcpy((void*)denali->buf.dma_buf, (void*)buf, mtd->writesize);
+
+	if (raw_xfer) 
+    	{
+		/* transfer the data to the spare area */
+		memcpy((void*)(denali->buf.dma_buf + mtd->writesize),
+			(void*)chip->oob_poi,mtd->oobsize);
+	}
+      
+	denali_enable_dma(denali, true);
+
+	denali_setup_dma(denali, DENALI_WRITE); //zhouqi p779-p789
+
+	/* wait for operation to complete */
+	status_type = wait_for_ready(denali, status_mask);
+
+	if (status_type & INTR_STATUS__TIME_OUT) 
+    {
+		debug("timeout on write_page (type = %d)\n",
+				raw_xfer);
+		denali->status =
+			(status_type & INTR_STATUS__PROGRAM_FAIL) ?
+			NAND_STATUS_FAIL : PASS;
+	}
+
+	denali_enable_dma(denali, false);
+    
+    setup_ecc_the_end(denali, false);   //zhouqi
+    
+}
+
+//add by zhouqi
+static void write_page_ops(struct mtd_info *mtd, struct nand_chip *chip,
+			const uint8_t *buf)
+{
+	int N,len,sector_size,ecc_bytes,i;
+	struct denali_nand_info *denali = g_denali;
+
+	uint32_t status_type = 0;
+	uint32_t status_mask = INTR_STATUS__DMA_CMD_COMP |
+						INTR_STATUS__PROGRAM_FAIL;
+
+	/* if it is a raw xfer, we want to disable ecc, and send
+	 * the spare area.
+	 * !raw_xfer - enable ecc
+	 * raw_xfer - transfer spare
+	 */
+	setup_ecc_for_xfer(denali, 1, 1);
+	memset((void *)(denali->buf.dma_buf),0xff,denali->mtd->writesize+denali->mtd->oobsize);
+
+	sector_size = denali->nand->ecc.size;
+    ecc_bytes = denali->nand->ecc.bytes;
+    N = denali->mtd->writesize/(sector_size+ecc_bytes) + 1;
+	len = sector_size;
+
+    for(i=0;i < N;i++)
+	{
+		if(i==N-1)
+		{
+			len = denali->mtd->writesize - (sector_size+ecc_bytes)*i;
+		}
+	
+	   memcpy((void *)(denali->buf.dma_buf+(sector_size+ecc_bytes)*i), (void *)(buf+sector_size*i), len);  ;
+		
+	}
+	
+	len = sector_size - len;
+	
+    memcpy((void *)(denali->buf.dma_buf + denali->mtd->writesize+2), (void *)(buf + sector_size*i -len), len); 
+	memcpy((void *)(denali->buf.dma_buf+denali->mtd->writesize+2+len+ecc_bytes), (void *)(chip->oob_poi + 2+len+ecc_bytes), \
+	 		denali->mtd->oobsize-2-len-ecc_bytes);  
+
+	denali_enable_dma(denali, true);
+
+	denali_setup_dma(denali, DENALI_WRITE);
+
+	/* wait for operation to complete */
+	status_type = wait_for_ready(denali, status_mask);
+
+	if (status_type & INTR_STATUS__TIME_OUT) 
+    {
+		debug("timeout on write_page (type = )\n");
+		denali->status =
+			(status_type & INTR_STATUS__PROGRAM_FAIL) ?
+			NAND_STATUS_FAIL : PASS;
+	}
+
+	denali_enable_dma(denali, false);
+	setup_ecc_the_end(denali, false);   //zhouqi
+}
+
+/* NAND core entry points */
+
+/* this is the callback that the NAND core calls to write a page. Since
+ * writing a page with ECC or without is similar, all the work is done
+ * by write_page above.
+ * */
+static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	struct denali_nand_info *denali = g_denali;
+	/* for regular page writes, we let HW handle all the ECC
+	 * data written to the device. */
+	int ecc_bits = readl(denali->flash_reg + ECC_CORRECTION);
+	if((ops->oobbuf != NULL) && (ops->ooblen != 0))
+	{
+        write_page_ops(mtd, chip, buf);
+	}
+    else
+    {
+		if(denali->page < 64) 
+		{
+			writel(0x8, denali->flash_reg + ECC_CORRECTION); 
+		}
+	    write_page(mtd, chip, buf, false);
+		writel(ecc_bits, denali->flash_reg + ECC_CORRECTION); 
+    }
+	update_led_twinkle();
+}
+
+/* This is the callback that the NAND core calls to write a page without ECC.
+ * raw access is similar to ECC page writes, so all the work is done in the
+ * write_page() function above.
+ */
+static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+					const uint8_t *buf)
+{
+	/* for raw page writes, we want to disable ECC and simply write
+	   whatever data is in the buffer. */
+	write_page(mtd, chip, buf, true);
+}
+
+static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			    int page)
+{
+	//printk(KERN_EMERG "[denali.c]  denali_write_oob: page = 0x%0x\n",page); //zhouqi
+	return write_oob_data(mtd, chip->oob_poi, page);
+}
+
+static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			   int page, int sndcmd)
+{
+    //printk(KERN_EMERG "[denali.c]  denali_read_oob: page = 0x%0x\n",page); //zhouqi
+	read_oob_data(mtd, chip->oob_poi, page);
+
+	return 0; /* notify NAND core to send command to
+			   NAND device. */
+}
+
+
+static int read_page(struct mtd_info *mtd, struct nand_chip *chip,
+			    uint8_t *buf, int page)
+{
+	struct denali_nand_info *denali = g_denali;
+
+	uint32_t status_type = 0;
+//	uint32_t status_mask = INTR_STATUS__ECC_ERR |
+//			    INTR_STATUS__ECC_ERR;
+    uint32_t status_mask = INTR_STATUS__DMA_CMD_COMP;
+	uint32_t check_erased_page = false;
+
+	if (page != denali->page) 
+    {
+		debug("IN %s: page %d is not"
+				" equal to denali->page %d, investigate!!",
+				__func__, page, denali->page);
+		BUG();
+	}
+
+	setup_ecc_for_xfer(denali, true, false); 
+
+	denali_enable_dma(denali, true);
+	
+	if(flash_dmabuf_disable_flag == 1)
+	{
+		denali_setup_dma(denali, DENALI_READ);
+		/* wait for operation to complete */
+		status_type = wait_for_ready(denali, status_mask);
+		memcpy(buf, (void *)denali->buf.dma_buf, mtd->writesize);   //zhouqi -p939
+	}
+	else
+	{
+		denali_setup_dma_buffer(denali, DENALI_READ,buf); //zhouqi
+		/* wait for operation to complete */
+		status_type = wait_for_ready(denali, status_mask);
+	}
+
+	check_erased_page = handle_ecc(denali, buf, status_type);
+	//check_erased_page = 0;
+	denali_enable_dma(denali, false);
+    setup_ecc_the_end(denali, false);   //zhouqi
+
+	if (check_erased_page) 
+    {
+		read_oob_data(denali->mtd, chip->oob_poi, denali->page);
+		
+		if (!is_erased(buf, denali->mtd->writesize))
+			denali->mtd->ecc_stats.failed++;
+		if (!is_erased(chip->oob_poi, denali->mtd->oobsize))
+			denali->mtd->ecc_stats.failed++;
+	}
+	return 0;
+}
+
+
+//add by zhouqi
+static int read_page_ops(struct mtd_info *mtd, struct nand_chip *chip,
+			    uint8_t *buf, int page)
+{
+	int N,len,sector_size,ecc_bytes,i;
+    struct denali_nand_info *denali = g_denali;
+
+	uint32_t status_type = 0;
+    uint32_t status_mask = INTR_STATUS__DMA_CMD_COMP;
+	uint32_t check_erased_page = false;
+    
+	if (page != denali->page) 
+    {
+		debug("IN %s: page %d is not"
+				" equal to denali->page %d, investigate!!",
+				__func__, page, denali->page);
+		BUG();
+	}
+
+	setup_ecc_for_xfer(denali, 1, 1);
+
+	denali_enable_dma(denali, true);
+
+	denali_setup_dma(denali, DENALI_READ);
+
+	/* wait for operation to complete */
+	status_type = wait_for_ready(denali, status_mask);
+
+    sector_size = denali->nand->ecc.size;
+    ecc_bytes = denali->nand->ecc.bytes;
+    N = denali->mtd->writesize/(sector_size+ecc_bytes) + 1;
+	len = sector_size;
+
+    for(i=0;i < N;i++)
+	{
+		if(i==N-1)
+		{
+			len = denali->mtd->writesize - (sector_size+ecc_bytes)*i;
+		}
+	
+	   memcpy((void *)(buf+sector_size*i),(void *)( denali->buf.dma_buf + (sector_size+ecc_bytes)*i),len);
+		
+	}
+	
+	len = sector_size - len;
+	memcpy((void *)(buf + sector_size*(N-1)+len), (void *)(denali->buf.dma_buf + denali->mtd->writesize +2), len);   
+   
+	memset((void *)(chip->oob_poi), 0xFF, len +ecc_bytes+2);
+	memcpy((void *)(chip->oob_poi + len+ecc_bytes+2), (void *)(denali->buf.dma_buf +  denali->mtd->writesize+len+ecc_bytes+2),\
+		   denali->mtd->oobsize-len -ecc_bytes-2);
+
+	check_erased_page = handle_ecc(denali, buf, status_type);
+	//check_erased_page = 0;
+	denali_enable_dma(denali, false);
+    setup_ecc_the_end(denali, false);   //zhouqi
+
+	if (check_erased_page) 
+    {
+		read_oob_data(denali->mtd, chip->oob_poi, denali->page);
+
+		/* check ECC failures that may have occurred on erased pages */
+		if (!is_erased(buf, denali->mtd->writesize))
+			denali->mtd->ecc_stats.failed++;
+		if (!is_erased(chip->oob_poi, denali->mtd->oobsize))
+			denali->mtd->ecc_stats.failed++;
+	}
+	return 0;
+}
+
+//add by zhouqi
+static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+			    uint8_t *buf, int page, struct mtd_oob_ops *ops)//add by zhouqi
+{
+
+	struct denali_nand_info *denali = g_denali;
+
+	int ecc_bits = readl(denali->flash_reg + ECC_CORRECTION);
+		
+	if((ops->oobbuf != NULL) && ops->ooblen != 0)
+	{
+        //denali_debug("[denali.c]:  read_page_ops: page = 0x%0x\n", page);//zhouqi
+        read_page_ops(mtd, chip, buf, page);
+	}
+    else
+    {
+    	if(denali->page < 64) 
+		{
+			writel(0x8, denali->flash_reg + ECC_CORRECTION); 
+		}
+        //denali_debug("[denali.c]:  read_page: page = 0x%0x\n", page);//zhouqi
+        read_page(mtd, chip, buf, page);
+		writel(ecc_bits, denali->flash_reg + ECC_CORRECTION); 
+    }
+	update_led_twinkle();
+    return 0;
+}
+
+static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page)
+{
+	struct denali_nand_info *denali = g_denali;
+
+	uint32_t status_type = 0;
+	uint32_t status_mask = INTR_STATUS__DMA_CMD_COMP;
+
+	if (page != denali->page) 
+    {
+		debug("IN %s: page %d is not"
+				" equal to denali->page %d, investigate!!",
+				__func__, page, denali->page);
+		BUG();
+	}
+
+	setup_ecc_for_xfer(denali, false, true);
+	denali_enable_dma(denali, true);
+
+	denali_setup_dma(denali, DENALI_READ);
+
+	/* wait for operation to complete */
+	status_type = wait_for_ready(denali, status_mask);
+
+	denali_enable_dma(denali, false);
+
+	memcpy(buf, (void*)denali->buf.dma_buf, mtd->writesize);
+	memcpy(chip->oob_poi, (void*)(denali->buf.dma_buf + mtd->writesize), mtd->oobsize);
+
+	return 0;
+}
+
+static uint8_t denali_read_byte(struct mtd_info *mtd)
+{
+	struct denali_nand_info *denali = g_denali;
+	uint8_t result = 0xff;
+
+	if (denali->buf.head < denali->buf.tail)
+		result = denali->buf.buf[denali->buf.head++];
+
+	return result;
+}
+
+static uint16_t denali_read_word(struct mtd_info *mtd)
+{
+    struct nand_chip *chip = mtd->priv;
+	uint16_t result = 0x0;
+
+    result = (uint16_t)(*(chip->oob_poi));
+    result = result << 8;
+    result |= (uint16_t)(*(chip->oob_poi + 1));
+    
+	return result;
+}
+
+static void denali_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct denali_nand_info *denali = g_denali;
+
+	denali->flash_bank = chip;
+}
+
+static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+	struct denali_nand_info *denali = g_denali;
+	int status = denali->status;
+	denali->status = 0;
+
+	return status;
+}
+
+static void denali_erase(struct mtd_info *mtd, int page)
+{
+    #if ECC_TEST_VER
+	return;
+	#endif
+	struct denali_nand_info *denali = g_denali;
+
+	uint32_t cmd = 0x0, status_type = 0;
+
+	/* setup page read request for access type */
+	cmd = MODE_10 | BANK(denali->flash_bank) | page;
+	index_addr(denali, (uint32_t)cmd, 0x1);
+
+	/* wait for erase to complete or failure to occur */
+	status_type = wait_for_ready(denali, INTR_STATUS__ERASE_COMP |
+					INTR_STATUS__ERASE_FAIL);
+
+	denali->status = (status_type & INTR_STATUS__ERASE_FAIL) ?
+						NAND_STATUS_FAIL : PASS;
+	update_led_twinkle();
+}
+
+static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
+			   int page)
+{
+	struct denali_nand_info *denali = g_denali;
+	uint32_t addr, id;
+	int i;
+    
+	switch (cmd) {
+	case NAND_CMD_PAGEPROG:
+		break;
+	case NAND_CMD_STATUS:
+        reset_buf(denali);
+		read_status(denali);
+		break;
+	case NAND_CMD_READID:
+	case NAND_CMD_PARAM:
+		reset_buf(denali);
+		/*sometimes ManufactureId read from register is not right
+		 * e.g. some of Micron MT29F32G08QAA MLC NAND chips
+		 * So here we send READID cmd to NAND insteand
+		 * */
+		addr = (uint32_t)MODE_11 | BANK(denali->flash_bank);
+		index_addr(denali, (uint32_t)addr | 0, 0x90);
+		index_addr(denali, (uint32_t)addr | 1, 0);
+		for (i = 0; i < 5; i++) 
+        {
+			index_addr_read_data(denali,(uint32_t)addr | 2,	&id);
+			write_byte_to_buf(denali, id);
+		}
+		break;
+	case NAND_CMD_READ0:
+	case NAND_CMD_SEQIN:
+		denali->page = page;
+		break;
+	case NAND_CMD_RESET:
+		reset_bank(denali);
+		break;
+	case NAND_CMD_READOOB:
+        reset_buf(denali);      //zhouqi
+        denali_read_oob(mtd, mtd->priv, page, 0);
+		/* TODO: Read OOB data */
+		break;
+	default:
+		debug(": unsupported command"
+				" received 0x%x\n", cmd);
+		break;
+	}
+}
+
+/* stubs for ECC functions not used by the NAND core */
+static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data,
+				uint8_t *ecc_code)
+{
+	debug("denali_ecc_calculate called unexpectedly\n");
+	BUG();
+	return -1;
+}
+
+static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data,
+				uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	debug("denali_ecc_correct called unexpectedly\n");
+	BUG();
+	return -1;
+}
+
+static void denali_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+	debug("denali_ecc_hwctl called unexpectedly\n");
+	BUG();
+}
+/* end NAND core entry points */
+
+
+/* Initialization code to bring the device up to a known good state */
+static void denali_hw_init(struct denali_nand_info *denali)
+{
+    uint32_t id_bytes[5], addr;
+	uint8_t i, maf_id, device_id, res_id;
+    struct nand_flash_device_para * table = nand_flash_para;
+
+    denali->flash_reg = (void __iomem *)NAND_FLASH_REG;
+	denali->flash_mem = (void __iomem *)NAND_FLASH_MEM;
+
+    detect_max_banks(denali);       /* test the max banks support*/
+    denali_nand_reset(denali);
+    writel(0, denali->flash_reg + DMA_ENABLE);      /* dma disable */
+    writel(0, denali->flash_reg + ECC_ENABLE);      /* ecc disable */
+	writel(2, denali->flash_reg + SPARE_AREA_SKIP_BYTES);   
+	denali->bbtskipbytes = readl(denali->flash_reg +
+						SPARE_AREA_SKIP_BYTES);	
+	writel(0x0F, denali->flash_reg + RB_PIN_ENABLED);
+	writel(0, denali->flash_reg + CHIP_ENABLE_DONT_CARE);
+	writel(1, denali->flash_reg + DEVICES_CONNECTED);
+	
+
+	writel(0xffff, denali->flash_reg + SPARE_AREA_MARKER);
+
+
+    /* Use read id method to get device ID and other */
+	addr = (uint32_t)MODE_11 | BANK(denali->flash_bank);
+	index_addr(denali, (uint32_t)addr | 0, 0x90);
+	index_addr(denali, (uint32_t)addr | 1, 0);
+	for (i = 0; i < 5; i++)
+		index_addr_read_data(denali, addr | 2, &id_bytes[i]);
+	maf_id = id_bytes[0];
+	device_id = id_bytes[1];
+    res_id = id_bytes[2];
+
+    for (; table->manuf_id != 0; table++)
+    {
+        if ((maf_id == table->manuf_id) && (device_id == table->device_id) 
+                                        && (res_id == table->res_id) )
+        {
+            break;
+        }		
+    }
+    g_nand_dev_info = table;
+    printf("maf_id=%x,dev_id=%x,res_id=%x\n",maf_id,device_id,res_id);   
+	/* Should set value for these registers when init */ 
+    denali_nand_register_set(denali, table);
+	denali_nand_timing_set(denali, table);
+    //find_valid_banks(denali); //zhouqi for 7520 fpga
+	//detect_partition_feature(denali);
+	denali_irq_init(denali);
+
+}
+
+/* Althogh controller spec said SLC ECC is forceb to be 4bit,
+ * but denali controller in MRST only support 15bit and 8bit ECC
+ * correction
+ * */
+
+static struct nand_ecclayout g_nand_oob;
+
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = bbt_pattern,
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = mirror_pattern,
+};
+
+/* initialize driver data structures */
+void denali_drv_init(struct denali_nand_info *denali)
+{
+	/* indicate that MTD has not selected a valid bank yet */
+	denali->flash_bank = CHIP_SELECT_INVALID;
+
+	/* initialize our status_type variable to indicate no interrupts */
+}
+
+static int denali_nand_ecc_init(struct denali_nand_info  *denali)
+{
+    int i,eccpos_start;
+    denali->nand->ecc.mode = NAND_ECC_HW_SYNDROME;
+	denali->nand->ecc.size = g_nand_dev_info->ecc.sector_size;
+    denali->nand->ecc.steps = g_nand_dev_info->page_size/g_nand_dev_info->ecc.sector_size;
+    denali->nand->ecc.strength = g_nand_dev_info->ecc.strength;
+	
+	switch (denali->nand->ecc.size) {
+	case 512:
+		denali->nand->ecc.bytes =
+			( denali->nand->ecc.strength  * 13 + 15) / 16 * 2;
+		break;
+	case 1024:
+		denali->nand->ecc.bytes =
+			( denali->nand->ecc.strength  * 14 + 15) / 16 * 2;
+		break;
+	default:
+		printk("Unsupported ECC sector size\n");
+
+		BUG_ON(1);
+		return -1;
+	}
+
+	denali->nand->ecc.total = denali->nand->ecc.bytes* denali->nand->ecc.steps;
+	if(g_nand_dev_info->oob_size >= (denali->nand->ecc.total+denali->bbtskipbytes + 8))
+	{
+		
+		writel(g_nand_dev_info->ecc.strength, denali->flash_reg + ECC_CORRECTION);
+        g_nand_oob.eccbytes = denali->nand->ecc.total;
+	
+		eccpos_start = denali->bbtskipbytes;
+
+		for (i = 0; i < g_nand_oob.eccbytes; i++)
+		{
+			g_nand_oob.eccpos[i] = eccpos_start + i;
+		}
+
+		g_nand_oob.oobfree[0].offset = g_nand_oob.eccbytes+denali->bbtskipbytes;
+		g_nand_oob.oobfree[0].length = g_nand_dev_info->oob_size -(g_nand_oob.eccbytes+denali->bbtskipbytes);
+		denali->nand->ecc.layout = &g_nand_oob;
+	}
+	else
+	{
+		printk("Unsupported ECC strength,please check the id table\n");
+		BUG();
+	}
+
+	
+	return 0;
+}
+
+
+/* driver entry point */
+int board_nand_init_denali(struct nand_chip *nand)
+{
+	int ret = -1; 
+	struct denali_nand_info *denali = g_denali;
+
+	denali_hw_init(denali);
+	denali_drv_init(denali);
+
+	denali->mtd = (struct mtd_info *)&nand_info;
+	denali->nand = (struct nand_chip *)&nand_chip;
+	denali->buf.dma_buf = (dma_addr_t)CONFIG_NAND_DMA_BUF_ADDR;
+
+	/* register the driver with the NAND core subsystem */
+	denali->nand->select_chip = denali_select_chip;
+	denali->nand->cmdfunc = denali_cmdfunc;
+	denali->nand->read_byte = denali_read_byte;
+	denali->nand->read_word = denali_read_word;
+	denali->nand->waitfunc = denali_waitfunc;
+
+	/* scan for NAND devices attached to the controller
+	 * this is the first stage in a two step process to register
+	 * with the nand subsystem */
+	
+	if (nand_scan_ident(denali->mtd, denali->total_used_banks, NULL)) 
+	{
+		debug("nand ident: cant ident this nand device");
+		return -1;
+	}
+
+	/* MTD supported page sizes vary by kernel. We validate our
+	 * kernel supports the device here.
+	 */
+	if (denali->mtd->writesize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) 
+	{
+		ret = -1;
+		debug("Spectra: device size not supported by this version of MTD.");
+		return ret;
+	}
+
+	/* support for multi nand
+	 * MTD known nothing about multi nand,
+	 * so we should tell it the real pagesize
+	 * and anything necessery
+	 */
+	denali->devnum = readl(denali->flash_reg + DEVICES_CONNECTED);
+	denali->nand->chipsize <<= (denali->devnum - 1);
+	denali->nand->page_shift += (denali->devnum - 1);
+	denali->nand->pagemask = (denali->nand->chipsize >> denali->nand->page_shift) - 1;
+	denali->nand->bbt_erase_shift += (denali->devnum - 1);
+	denali->nand->phys_erase_shift = denali->nand->bbt_erase_shift;
+	denali->nand->chip_shift += (denali->devnum - 1);
+	denali->mtd->writesize <<= (denali->devnum - 1);
+	denali->mtd->oobsize <<= (denali->devnum - 1);
+	denali->mtd->erasesize <<= (denali->devnum - 1);
+	denali->mtd->size = denali->nand->numchips * denali->nand->chipsize;
+	denali->bbtskipbytes *= denali->devnum;
+
+	/* second stage of the NAND scan
+	 * this stage requires information regarding ECC and
+	 * bad block management. */
+
+	/* Bad block management */
+	denali->nand->bbt_td = &bbt_main_descr;
+	denali->nand->bbt_md = &bbt_mirror_descr;
+
+	/* skip the scan for now until we have OOB read and write support */
+	denali->nand->options |= NAND_USE_FLASH_BBT;//NAND_SKIP_BBTSCAN
+	
+	// init ecc
+	 denali_nand_ecc_init(denali);
+    
+	/* Let driver know the total blocks number and
+	 * how many blocks contained by each nand chip.
+	 * blksperchip will help driver to know how many
+	 * blocks is taken by FW.
+	 * */
+	denali->totalblks = denali->mtd->size >> denali->nand->phys_erase_shift;
+	denali->blksperchip = denali->totalblks / denali->nand->numchips;
+
+	/* These functions are required by the NAND core framework, otherwise,
+	 * the NAND core will assert. However, we don't need them, so we'll stub
+	 * them out. */
+	denali->nand->ecc.calculate = denali_ecc_calculate;
+	denali->nand->ecc.correct = denali_ecc_correct;
+	denali->nand->ecc.hwctl = denali_ecc_hwctl;
+
+	/* override the default read operations */
+	denali->nand->ecc.read_page = denali_read_page;
+	denali->nand->ecc.read_page_raw = denali_read_page_raw;
+	denali->nand->ecc.write_page = denali_write_page;
+	denali->nand->ecc.write_page_raw = denali_write_page_raw;
+	denali->nand->ecc.read_oob = denali_read_oob;
+	denali->nand->ecc.write_oob = denali_write_oob;
+	denali->nand->erase_cmd = denali_erase;
+
+	if (nand_scan_tail(denali->mtd)) 
+	{
+		ret = -1;
+	}
+	
+	return 0;
+}
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/denali.h b/boot/common/src/uboot/drivers/mtd/nand/denali.h
new file mode 100644
index 0000000..183e223
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/denali.h
@@ -0,0 +1,500 @@
+/*
+ * NAND Flash Controller Device Driver
+ * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <linux/mtd/nand.h>
+
+#define DEVICE_RESET				0x0
+#define     DEVICE_RESET__BANK0				0x0001
+#define     DEVICE_RESET__BANK1				0x0002
+#define     DEVICE_RESET__BANK2				0x0004
+#define     DEVICE_RESET__BANK3				0x0008
+
+#define TRANSFER_SPARE_REG			0x10
+#define     TRANSFER_SPARE_REG__FLAG			0x0001
+
+#define LOAD_WAIT_CNT				0x20
+#define     LOAD_WAIT_CNT__VALUE			0xffff
+
+#define PROGRAM_WAIT_CNT			0x30
+#define     PROGRAM_WAIT_CNT__VALUE			0xffff
+
+#define ERASE_WAIT_CNT				0x40
+#define     ERASE_WAIT_CNT__VALUE			0xffff
+
+#define INT_MON_CYCCNT				0x50
+#define     INT_MON_CYCCNT__VALUE			0xffff
+
+#define RB_PIN_ENABLED				0x60
+#define     RB_PIN_ENABLED__BANK0			0x0001
+#define     RB_PIN_ENABLED__BANK1			0x0002
+#define     RB_PIN_ENABLED__BANK2			0x0004
+#define     RB_PIN_ENABLED__BANK3			0x0008
+
+#define MULTIPLANE_OPERATION			0x70
+#define     MULTIPLANE_OPERATION__FLAG			0x0001
+
+#define MULTIPLANE_READ_ENABLE			0x80
+#define     MULTIPLANE_READ_ENABLE__FLAG		0x0001
+
+#define COPYBACK_DISABLE			0x90
+#define     COPYBACK_DISABLE__FLAG			0x0001
+
+#define CACHE_WRITE_ENABLE			0xa0
+#define     CACHE_WRITE_ENABLE__FLAG			0x0001
+
+#define CACHE_READ_ENABLE			0xb0
+#define     CACHE_READ_ENABLE__FLAG			0x0001
+
+#define PREFETCH_MODE				0xc0
+#define     PREFETCH_MODE__PREFETCH_EN			0x0001
+#define     PREFETCH_MODE__PREFETCH_BURST_LENGTH	0xfff0
+
+#define CHIP_ENABLE_DONT_CARE			0xd0
+#define     CHIP_EN_DONT_CARE__FLAG			0x01
+
+#define ECC_ENABLE				0xe0
+#define     ECC_ENABLE__FLAG				0x0001
+
+#define GLOBAL_INT_ENABLE			0xf0
+#define     GLOBAL_INT_EN_FLAG				0x01
+
+#define WE_2_RE					0x100
+#define     WE_2_RE__VALUE				0x003f
+
+#define ADDR_2_DATA				0x110
+#define     ADDR_2_DATA__VALUE				0x003f
+
+#define RE_2_WE					0x120
+#define     RE_2_WE__VALUE				0x003f
+
+#define ACC_CLKS				0x130
+#define     ACC_CLKS__VALUE				0x000f
+
+#define NUMBER_OF_PLANES			0x140
+#define     NUMBER_OF_PLANES__VALUE			0x0007
+
+#define PAGES_PER_BLOCK				0x150
+#define     PAGES_PER_BLOCK__VALUE			0xffff
+
+#define DEVICE_WIDTH				0x160
+#define     DEVICE_WIDTH__VALUE				0x0003
+
+#define DEVICE_MAIN_AREA_SIZE			0x170
+#define     DEVICE_MAIN_AREA_SIZE__VALUE		0xffff
+
+#define DEVICE_SPARE_AREA_SIZE			0x180
+#define     DEVICE_SPARE_AREA_SIZE__VALUE		0xffff
+
+#define TWO_ROW_ADDR_CYCLES			0x190
+#define     TWO_ROW_ADDR_CYCLES__FLAG			0x0001
+
+#define MULTIPLANE_ADDR_RESTRICT		0x1a0
+#define     MULTIPLANE_ADDR_RESTRICT__FLAG		0x0001
+
+#define ECC_CORRECTION				0x1b0
+#define     ECC_CORRECTION__VALUE			0x001f
+
+#define READ_MODE				0x1c0
+#define     READ_MODE__VALUE				0x000f
+
+#define WRITE_MODE				0x1d0
+#define     WRITE_MODE__VALUE				0x000f
+
+#define COPYBACK_MODE				0x1e0
+#define     COPYBACK_MODE__VALUE			0x000f
+
+#define RDWR_EN_LO_CNT				0x1f0
+#define     RDWR_EN_LO_CNT__VALUE			0x001f
+
+#define RDWR_EN_HI_CNT				0x200
+#define     RDWR_EN_HI_CNT__VALUE			0x001f
+
+#define MAX_RD_DELAY				0x210
+#define     MAX_RD_DELAY__VALUE				0x000f
+
+#define CS_SETUP_CNT				0x220
+#define     CS_SETUP_CNT__VALUE				0x001f
+
+#define SPARE_AREA_SKIP_BYTES			0x230
+#define     SPARE_AREA_SKIP_BYTES__VALUE		0x003f
+
+#define SPARE_AREA_MARKER			0x240
+#define     SPARE_AREA_MARKER__VALUE			0xffff
+
+#define DEVICES_CONNECTED			0x250
+#define     DEVICES_CONNECTED__VALUE			0x0007
+
+#define DIE_MASK				0x260
+#define     DIE_MASK__VALUE				0x00ff
+
+#define FIRST_BLOCK_OF_NEXT_PLANE		0x270
+#define     FIRST_BLOCK_OF_NEXT_PLANE__VALUE		0xffff
+
+#define WRITE_PROTECT				0x280
+#define     WRITE_PROTECT__FLAG				0x0001
+
+#define RE_2_RE					0x290
+#define     RE_2_RE__VALUE				0x003f
+
+#define MANUFACTURER_ID				0x300
+#define     MANUFACTURER_ID__VALUE			0x00ff
+
+#define DEVICE_ID				0x310
+#define     DEVICE_ID__VALUE				0x00ff
+
+#define DEVICE_PARAM_0				0x320
+#define     DEVICE_PARAM_0__VALUE			0x00ff
+
+#define DEVICE_PARAM_1				0x330
+#define     DEVICE_PARAM_1__VALUE			0x00ff
+
+#define DEVICE_PARAM_2				0x340
+#define     DEVICE_PARAM_2__VALUE			0x00ff
+
+#define LOGICAL_PAGE_DATA_SIZE			0x350
+#define     LOGICAL_PAGE_DATA_SIZE__VALUE		0xffff
+
+#define LOGICAL_PAGE_SPARE_SIZE			0x360
+#define     LOGICAL_PAGE_SPARE_SIZE__VALUE		0xffff
+
+#define REVISION				0x370
+#define     REVISION__VALUE				0xffff
+
+#define ONFI_DEVICE_FEATURES			0x380
+#define     ONFI_DEVICE_FEATURES__VALUE			0x003f
+
+#define ONFI_OPTIONAL_COMMANDS			0x390
+#define     ONFI_OPTIONAL_COMMANDS__VALUE		0x003f
+
+#define ONFI_TIMING_MODE			0x3a0
+#define     ONFI_TIMING_MODE__VALUE			0x003f
+
+#define ONFI_PGM_CACHE_TIMING_MODE		0x3b0
+#define     ONFI_PGM_CACHE_TIMING_MODE__VALUE		0x003f
+
+#define ONFI_DEVICE_NO_OF_LUNS			0x3c0
+#define     ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS		0x00ff
+#define     ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE		0x0100
+
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L	0x3d0
+#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE	0xffff
+
+#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U	0x3e0
+#define     ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE	0xffff
+
+#define FEATURES					0x3f0
+#define     FEATURES__N_BANKS				0x0003
+#define     FEATURES__ECC_MAX_ERR			0x003c
+#define     FEATURES__DMA				0x0040
+#define     FEATURES__CMD_DMA				0x0080
+#define     FEATURES__PARTITION				0x0100
+#define     FEATURES__XDMA_SIDEBAND			0x0200
+#define     FEATURES__GPREG				0x0400
+#define     FEATURES__INDEX_ADDR			0x0800
+
+#define TRANSFER_MODE				0x400
+#define     TRANSFER_MODE__VALUE			0x0003
+
+#define INTR_STATUS(__bank)	(0x410 + ((__bank) * 0x50))
+#define INTR_EN(__bank)		(0x420 + ((__bank) * 0x50))
+
+
+#define     INTR_STATUS__ECC_ERR			0x0001
+#define     INTR_STATUS__ECC_TRANSACTION_DONE		0x0002
+#define     INTR_STATUS__DMA_CMD_COMP			0x0004
+#define     INTR_STATUS__TIME_OUT			0x0008
+#define     INTR_STATUS__PROGRAM_FAIL			0x0010
+#define     INTR_STATUS__ERASE_FAIL			0x0020
+#define     INTR_STATUS__LOAD_COMP			0x0040
+#define     INTR_STATUS__PROGRAM_COMP			0x0080
+#define     INTR_STATUS__ERASE_COMP			0x0100
+#define     INTR_STATUS__PIPE_CPYBCK_CMD_COMP		0x0200
+#define     INTR_STATUS__LOCKED_BLK			0x0400
+#define     INTR_STATUS__UNSUP_CMD			0x0800
+#define     INTR_STATUS__INT_ACT			0x1000
+#define     INTR_STATUS__RST_COMP			0x2000
+#define     INTR_STATUS__PIPE_CMD_ERR			0x4000
+#define     INTR_STATUS__PAGE_XFER_INC			0x8000
+
+#define     INTR_EN__ECC_TRANSACTION_DONE		0x0001
+#define     INTR_EN__ECC_ERR				0x0002
+#define     INTR_EN__DMA_CMD_COMP			0x0004
+#define     INTR_EN__TIME_OUT				0x0008
+#define     INTR_EN__PROGRAM_FAIL			0x0010
+#define     INTR_EN__ERASE_FAIL				0x0020
+#define     INTR_EN__LOAD_COMP				0x0040
+#define     INTR_EN__PROGRAM_COMP			0x0080
+#define     INTR_EN__ERASE_COMP				0x0100
+#define     INTR_EN__PIPE_CPYBCK_CMD_COMP		0x0200
+#define     INTR_EN__LOCKED_BLK				0x0400
+#define     INTR_EN__UNSUP_CMD				0x0800
+#define     INTR_EN__INT_ACT				0x1000
+#define     INTR_EN__RST_COMP				0x2000
+#define     INTR_EN__PIPE_CMD_ERR			0x4000
+#define     INTR_EN__PAGE_XFER_INC			0x8000
+
+#define PAGE_CNT(__bank)	(0x430 + ((__bank) * 0x50))
+#define ERR_PAGE_ADDR(__bank)	(0x440 + ((__bank) * 0x50))
+#define ERR_BLOCK_ADDR(__bank)	(0x450 + ((__bank) * 0x50))
+
+#define DATA_INTR				0x550
+#define     DATA_INTR__WRITE_SPACE_AV			0x0001
+#define     DATA_INTR__READ_DATA_AV			0x0002
+
+#define DATA_INTR_EN				0x560
+#define     DATA_INTR_EN__WRITE_SPACE_AV		0x0001
+#define     DATA_INTR_EN__READ_DATA_AV			0x0002
+
+#define GPREG_0					0x570
+#define     GPREG_0__VALUE				0xffff
+
+#define GPREG_1					0x580
+#define     GPREG_1__VALUE				0xffff
+
+#define GPREG_2					0x590
+#define     GPREG_2__VALUE				0xffff
+
+#define GPREG_3					0x5a0
+#define     GPREG_3__VALUE				0xffff
+
+#define ECC_THRESHOLD				0x600
+#define     ECC_THRESHOLD__VALUE			0x03ff
+
+#define ECC_ERROR_BLOCK_ADDRESS			0x610
+#define     ECC_ERROR_BLOCK_ADDRESS__VALUE		0xffff
+
+#define ECC_ERROR_PAGE_ADDRESS			0x620
+#define     ECC_ERROR_PAGE_ADDRESS__VALUE		0x0fff
+#define     ECC_ERROR_PAGE_ADDRESS__BANK		0xf000
+
+#define ECC_ERROR_ADDRESS			0x630
+#define     ECC_ERROR_ADDRESS__OFFSET			0x0fff
+#define     ECC_ERROR_ADDRESS__SECTOR_NR		0xf000
+
+#define ERR_CORRECTION_INFO_B01			0x650
+#define     ERR_CORRECTION_INFO__BYTEMASK		0x00ff
+#define     ERR_CORRECTION_INFO__DEVICE_NR		0x0f00
+#define     ERR_CORRECTION_INFO__ERROR_TYPE		0x4000
+#define     ERR_CORRECTION_INFO__LAST_ERR_INFO		0x8000
+#define     ERR_CORRECTION_INFO_B01__MAX_ERRORS_B0   0x007f
+#define     ERR_CORRECTION_INFO_B01__MAX_ERRORS_B1   0x7f00
+
+#define ERR_CORRECTION_INFO_B23		0x660
+#define     ERR_CORRECTION_INFO_B01__MAX_ERRORS_B2   0x007f
+#define     ERR_CORRECTION_INFO_B01__MAX_ERRORS_B3   0x7f00
+
+
+#define DMA_ENABLE				0x700
+#define     DMA_ENABLE__FLAG				0x0001
+
+#define IGNORE_ECC_DONE				0x710
+#define     IGNORE_ECC_DONE__FLAG			0x0001
+
+#define DMA_INTR				0x720
+#define     DMA_INTR__TARGET_ERROR			0x0001
+#define     DMA_INTR__DESC_COMP_CHANNEL0		0x0002
+#define     DMA_INTR__DESC_COMP_CHANNEL1		0x0004
+#define     DMA_INTR__DESC_COMP_CHANNEL2		0x0008
+#define     DMA_INTR__DESC_COMP_CHANNEL3		0x0010
+#define     DMA_INTR__MEMCOPY_DESC_COMP		0x0020
+
+#define DMA_INTR_EN				0x730
+#define     DMA_INTR_EN__TARGET_ERROR			0x0001
+#define     DMA_INTR_EN__DESC_COMP_CHANNEL0		0x0002
+#define     DMA_INTR_EN__DESC_COMP_CHANNEL1		0x0004
+#define     DMA_INTR_EN__DESC_COMP_CHANNEL2		0x0008
+#define     DMA_INTR_EN__DESC_COMP_CHANNEL3		0x0010
+#define     DMA_INTR_EN__MEMCOPY_DESC_COMP		0x0020
+
+#define TARGET_ERR_ADDR_LO			0x740
+#define     TARGET_ERR_ADDR_LO__VALUE			0xffff
+
+#define TARGET_ERR_ADDR_HI			0x750
+#define     TARGET_ERR_ADDR_HI__VALUE			0xffff
+
+#define CHNL_ACTIVE				0x760
+#define     CHNL_ACTIVE__CHANNEL0			0x0001
+#define     CHNL_ACTIVE__CHANNEL1			0x0002
+#define     CHNL_ACTIVE__CHANNEL2			0x0004
+#define     CHNL_ACTIVE__CHANNEL3			0x0008
+
+#define ACTIVE_SRC_ID				0x800
+#define     ACTIVE_SRC_ID__VALUE			0x00ff
+
+#define PTN_INTR					0x810
+#define     PTN_INTR__CONFIG_ERROR			0x0001
+#define     PTN_INTR__ACCESS_ERROR_BANK0		0x0002
+#define     PTN_INTR__ACCESS_ERROR_BANK1		0x0004
+#define     PTN_INTR__ACCESS_ERROR_BANK2		0x0008
+#define     PTN_INTR__ACCESS_ERROR_BANK3		0x0010
+#define     PTN_INTR__REG_ACCESS_ERROR			0x0020
+
+#define PTN_INTR_EN				0x820
+#define     PTN_INTR_EN__CONFIG_ERROR			0x0001
+#define     PTN_INTR_EN__ACCESS_ERROR_BANK0		0x0002
+#define     PTN_INTR_EN__ACCESS_ERROR_BANK1		0x0004
+#define     PTN_INTR_EN__ACCESS_ERROR_BANK2		0x0008
+#define     PTN_INTR_EN__ACCESS_ERROR_BANK3		0x0010
+#define     PTN_INTR_EN__REG_ACCESS_ERROR		0x0020
+
+#define PERM_SRC_ID(__bank)	(0x830 + ((__bank) * 0x40))
+#define     PERM_SRC_ID__SRCID				0x00ff
+#define     PERM_SRC_ID__DIRECT_ACCESS_ACTIVE		0x0800
+#define     PERM_SRC_ID__WRITE_ACTIVE			0x2000
+#define     PERM_SRC_ID__READ_ACTIVE			0x4000
+#define     PERM_SRC_ID__PARTITION_VALID		0x8000
+
+#define MIN_BLK_ADDR(__bank)	(0x840 + ((__bank) * 0x40))
+#define     MIN_BLK_ADDR__VALUE				0xffff
+
+#define MAX_BLK_ADDR(__bank)	(0x850 + ((__bank) * 0x40))
+#define     MAX_BLK_ADDR__VALUE				0xffff
+
+#define MIN_MAX_BANK(__bank)	(0x860 + ((__bank) * 0x40))
+#define     MIN_MAX_BANK__MIN_VALUE			0x0003
+#define     MIN_MAX_BANK__MAX_VALUE			0x000c
+
+
+/* ffsdefs.h */
+#define CLEAR 0                 /*use this to clear a field instead of "fail"*/
+#define SET   1                 /*use this to set a field instead of "pass"*/
+#define FAIL 1                  /*failed flag*/
+#define PASS 0                  /*success flag*/
+#define ERR -1                  /*error flag*/
+
+/* lld.h */
+#define GOOD_BLOCK 0
+#define DEFECTIVE_BLOCK 1
+#define READ_ERROR 2
+
+#define CLK_X  5
+#define CLK_MULTI 4
+
+/* spectraswconfig.h */
+#define CMD_DMA 0
+
+#define SPECTRA_PARTITION_ID    0
+/**** Block Table and Reserved Block Parameters *****/
+#define SPECTRA_START_BLOCK     3
+#define NUM_FREE_BLOCKS_GATE    30
+
+/* KBV - Updated to LNW scratch register address */
+#define SCRATCH_REG_ADDR    CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR
+#define SCRATCH_REG_SIZE    64
+
+#define GLOB_HWCTL_DEFAULT_BLKS    2048
+
+#define SUPPORT_15BITECC        1
+#define SUPPORT_8BITECC         1
+
+#define CUSTOM_CONF_PARAMS      0
+
+#define ONFI_BLOOM_TIME         1
+#define MODE5_WORKAROUND        0
+
+/* lld_nand.h */
+/*
+ * NAND Flash Controller Device Driver
+ * Copyright (c) 2009, Intel Corporation and its suppliers.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifndef _LLD_NAND_
+#define _LLD_NAND_
+
+#define MODE_00    0x00000000
+#define MODE_01    0x04000000
+#define MODE_10    0x08000000
+#define MODE_11    0x0C000000
+
+
+#define DATA_TRANSFER_MODE              0
+#define PROTECTION_PER_BLOCK            1
+#define LOAD_WAIT_COUNT                 2
+#define PROGRAM_WAIT_COUNT              3
+#define ERASE_WAIT_COUNT                4
+#define INT_MONITOR_CYCLE_COUNT         5
+#define READ_BUSY_PIN_ENABLED           6
+#define MULTIPLANE_OPERATION_SUPPORT    7
+#define PRE_FETCH_MODE                  8
+#define CE_DONT_CARE_SUPPORT            9
+#define COPYBACK_SUPPORT                10
+#define CACHE_WRITE_SUPPORT             11
+#define CACHE_READ_SUPPORT              12
+#define NUM_PAGES_IN_BLOCK              13
+#define ECC_ENABLE_SELECT               14
+#define WRITE_ENABLE_2_READ_ENABLE      15
+#define ADDRESS_2_DATA                  16
+#define READ_ENABLE_2_WRITE_ENABLE      17
+#define TWO_ROW_ADDRESS_CYCLES          18
+#define MULTIPLANE_ADDRESS_RESTRICT     19
+#define ACC_CLOCKS                      20
+#define READ_WRITE_ENABLE_LOW_COUNT     21
+#define READ_WRITE_ENABLE_HIGH_COUNT    22
+
+#define ECC_SECTOR_SIZE     512
+
+#define DENALI_BUF_SIZE		(NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE)
+
+struct nand_buf {
+	int head;
+	int tail;
+	uint8_t buf[DENALI_BUF_SIZE];
+	dma_addr_t dma_buf;
+};
+
+#define INTEL_CE4100	1
+#define INTEL_MRST	2
+
+struct denali_nand_info { 
+	struct mtd_info *mtd;       //zhouqi
+	struct nand_chip *nand;     //zhouqi
+	int flash_bank; /* currently selected chip */
+	int status;
+	int platform;
+	struct nand_buf buf;
+	struct device *dev;
+	int total_used_banks;
+	uint32_t block;  /* stored for future use */
+	uint32_t page;
+	void __iomem *flash_reg;  /* Mapped io reg base address */
+	void __iomem *flash_mem;  /* Mapped io reg base address */
+
+	uint32_t devnum;	/* represent how many nands connected */
+	uint32_t fwblks; /* represent how many blocks FW used */
+	uint32_t totalblks;
+	uint32_t blksperchip;
+	uint32_t bbtskipbytes;
+	uint32_t max_banks;
+};
+
+#endif /*_LLD_NAND_*/
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand.c b/boot/common/src/uboot/drivers/mtd/nand/nand.c
new file mode 100755
index 0000000..ec3f79a
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand.c
@@ -0,0 +1,184 @@
+/*
+ * (C) Copyright 2005
+ * 2N Telekomunikace, a.s. <www.2n.cz>
+ * Ladislav Michl <michl@2n.cz>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <part.h>
+
+#include <boot_mode.h>
+
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static __attribute__((unused)) char dev_name[CONFIG_SYS_MAX_NAND_DEVICE][8];
+
+int nand_curr_device = -1;
+nand_info_t nand_info[CONFIG_SYS_MAX_NAND_DEVICE];
+
+struct nand_chip nand_chip[CONFIG_SYS_MAX_NAND_DEVICE];  //zhouqi
+static ulong base_address[CONFIG_SYS_MAX_NAND_DEVICE] = CONFIG_SYS_NAND_BASE_LIST;
+
+static const char default_nand_name[] = "nand";
+struct flash_ops flash;
+
+
+/*******************************************************************************
+ * Function:    
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+int board_nand_init(struct nand_chip *nand)
+{
+    int ret = 0;
+	int boot_flash_type = 0;
+
+	boot_flash_type = read_boot_flashtype();
+	
+	if(boot_flash_type== IF_TYPE_NAND)
+	{
+		printf("nand: ");
+		ret = board_nand_init_denali(nand);
+		return ret;
+	}
+	else if(boot_flash_type == IF_TYPE_SPI_NAND)
+	{
+		printf("spi-nand: ");
+
+		ret = board_nand_init_spifc(nand);
+		return ret;
+	}
+	else if(boot_flash_type == IF_TYPE_NOR)
+	{
+		printf("spi-nor: ");
+		ret = fsl_qspi_probe();
+		return ret;
+	}
+	else
+	{
+		printf("boot_flash_type err\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+struct mtd_info* get_mtd_info(void)
+{
+	if (-1 == nand_curr_device || nand_curr_device > CONFIG_SYS_MAX_NAND_DEVICE)
+	{
+		return NULL;
+	}
+
+	return &nand_info[nand_curr_device];
+}
+
+static void nand_init_chip(struct mtd_info *mtd, struct nand_chip *nand,
+			   ulong base_addr)
+{
+	mtd->priv = nand;
+	nand->IO_ADDR_R = nand->IO_ADDR_W = (void  __iomem *)base_addr;
+
+	if (board_nand_init(nand) == 0) 
+	{
+		if (!mtd->name)
+		{
+			mtd->name = (char *)default_nand_name;
+		}
+
+#ifdef CONFIG_MTD_DEVICE
+		/*
+		 * Add MTD device so that we can reference it later
+		 * via the mtdcore infrastructure (e.g. ubi).
+		 */
+		sprintf(dev_name[i], "nand%d", i);
+		mtd->name = dev_name[i++];
+		add_mtd_device(mtd);
+#endif
+	}
+	else 
+	{
+		mtd->name = NULL;
+		mtd->size = 0;
+	}
+}
+
+void set_flash_opt(void)
+{
+	nand_info_t *nand = &nand_info[nand_curr_device];
+
+	flash.read = nand_read_skip_bad;
+	flash.read_no_ecc = nand_read_page_with_no_ecc;
+	flash.read_with_ecc = nand_read_page_with_ecc;
+	flash.write = nand_write_skip_bad;
+	flash.page_size = nand->writesize;
+	flash.erase = nand_erase_opts;
+ }
+
+struct flash_ops *get_flash_ops(void)
+{
+	struct flash_ops *m = NULL;
+	m = &flash;
+	
+	return m;
+}
+extern int jffs2_lzma_init(void);
+
+void nand_init(void)
+{
+	int i;
+	unsigned int size = 0;
+	for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) 
+	{
+		nand_init_chip(&nand_info[i], &nand_chip[i], base_address[i]);
+		size += nand_info[i].size / 1024;
+		if (nand_curr_device == -1)
+		{
+			nand_curr_device = i;
+		}
+	}
+	BOOT_PRINTF(UBOOT_NOTICE, "%u MiB.\n", (size / 1024));
+
+#ifdef CONFIG_SYS_NAND_SELECT_DEVICE
+	/*
+	 * Select the chip in the board/cpu specific driver
+	 */
+	board_nand_select_device(nand_info[nand_curr_device].priv, nand_curr_device);
+#endif
+
+	set_flash_opt();
+	jffs2_lzma_init();
+}
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand_base.c b/boot/common/src/uboot/drivers/mtd/nand/nand_base.c
new file mode 100755
index 0000000..56812f5
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_base.c
@@ -0,0 +1,3098 @@
+/*
+ *  drivers/mtd/nand.c
+ *
+ *  Overview:
+ *   This is the generic MTD driver for NAND flash devices. It should be
+ *   capable of working with almost all NAND chips currently available.
+ *   Basic support for AG-AND chips is provided.
+ *
+ *	Additional technical information is available on
+ *	http://www.linux-mtd.infradead.org/doc/nand.html
+ *
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *		  2002-2006 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *  Credits:
+ *	David Woodhouse for adding multichip support
+ *
+ *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ *	rework for 2K page size chips
+ *
+ *  TODO:
+ *	Enable cached programming for 2k page size chips
+ *	Check, if mtd->ecctype should be set to MTD_ECC_HW
+ *	if we have HW ecc support.
+ *	The AG-AND chips have nice features for speed improvement,
+ *	which are not supported yet. Read / program 4 pages in one go.
+ *	BBT table is not serialized, has to be fixed
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h>
+
+#define ENOTSUPP	524	/* Operation is not supported */
+
+#include <malloc.h>
+#include <watchdog.h>
+#include <linux/err.h>
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#include <asm/arch/nand.h> 
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#include <asm/io.h>
+#include <asm/errno.h>
+
+/*
+ * CONFIG_SYS_NAND_RESET_CNT is used as a timeout mechanism when resetting
+ * a flash.  NAND flash is initialized prior to interrupts so standard timers
+ * can't be used.  CONFIG_SYS_NAND_RESET_CNT should be set to a value
+ * which is greater than (max NAND reset time / NAND status read time).
+ * A conservative default of 200000 (500 us / 25 ns) is used as a default.
+ */
+#ifndef CONFIG_SYS_NAND_RESET_CNT
+#define CONFIG_SYS_NAND_RESET_CNT 200000
+#endif
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_ecclayout nand_oob_8 = {
+	.eccbytes = 3,
+	.eccpos = {0, 1, 2},
+	.oobfree = {
+		{.offset = 3,
+		 .length = 2},
+		{.offset = 6,
+		 .length = 2}}
+};
+
+static struct nand_ecclayout nand_oob_16 = {
+	.eccbytes = 6,
+	.eccpos = {0, 1, 2, 3, 6, 7},
+	.oobfree = {
+		{.offset = 8,
+		 . length = 8}}
+};
+
+static struct nand_ecclayout nand_oob_64 = {
+	.eccbytes = 24,
+	.eccpos = {
+		   40, 41, 42, 43, 44, 45, 46, 47,
+		   48, 49, 50, 51, 52, 53, 54, 55,
+		   56, 57, 58, 59, 60, 61, 62, 63},
+	.oobfree = {
+		{.offset = 2,
+		 .length = 38}}
+};
+
+static struct nand_ecclayout nand_oob_128 = {
+	.eccbytes = 48,
+	.eccpos = {
+		    80,  81,  82,  83,	84,  85,  86,  87,
+		    88,  89,  90,  91,	92,  93,  94,  95,
+		    96,  97,  98,  99, 100, 101, 102, 103,
+		   104, 105, 106, 107, 108, 109, 110, 111,
+		   112, 113, 114, 115, 116, 117, 118, 119,
+		   120, 121, 122, 123, 124, 125, 126, 127},
+	.oobfree = {
+		{.offset = 2,
+		 .length = 78}}
+};
+
+extern struct nand_flash_device_para *g_nand_dev_info;
+extern int winbond_dev_id2;
+
+static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
+			   int new_state);
+
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops);
+
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *this);
+
+/**
+ * nand_release_device - [GENERIC] release chip
+ * @mtd:	MTD device structure
+ *
+ * Deselect, release chip lock and wake up anyone waiting on the device
+ */
+static void nand_release_device (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	this->select_chip(mtd, -1);	/* De-select the NAND device */
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 8bit buswith
+ */
+static uint8_t nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	return readb(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith with
+ * endianess conversion
+ */
+static uint8_t nand_read_byte16(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith without
+ * endianess conversion
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	return readw(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd:	MTD device structure
+ * @chipnr:	chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	switch (chipnr) {
+	case -1:
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+		break;
+	case 0:
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+
+	for (i = 0; i < len; i++)
+		writeb(buf[i], chip->IO_ADDR_W);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 8bit buswith
+ */
+static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+
+	for (i = 0; i < len; i++)
+		buf[i] = readb(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf - [DEFAULT] Verify chip data against buffer
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 8bit buswith
+ */
+static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+
+	for (i = 0; i < len; i++)
+		if (buf[i] != readb(chip->IO_ADDR_R))
+			return -EFAULT;
+	return 0;
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i = 0; i < len; i++)
+		writew(p[i], chip->IO_ADDR_W);
+
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 16bit buswith
+ */
+static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i = 0; i < len; i++)
+		p[i] = readw(chip->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 16bit buswith
+ */
+static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len)
+{
+	int i;
+	struct nand_chip *chip = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i = 0; i < len; i++)
+		if (p[i] != readw(chip->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ *
+ * Check, if the block is bad.
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	int page, chipnr, res = 0;
+	struct nand_chip *chip = mtd->priv;
+	u16 bad;
+
+	page = (int)(ofs >> chip->page_shift) & chip->pagemask;
+
+	if (getchip) {
+		chipnr = (int)(ofs >> chip->chip_shift);
+
+		nand_get_device(chip, mtd, FL_READING);
+
+		/* Select the NAND device */
+		chip->select_chip(mtd, chipnr);
+	}
+
+	if (chip->options & NAND_BUSWIDTH_16) 
+    {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,
+			      page);
+		bad = cpu_to_le16(chip->read_word(mtd));
+		if (chip->badblockpos & 0x1)
+			bad >>= 8;
+		if ((bad & 0xFF) != 0xff)
+			res = 1;
+	} 
+    else 
+    {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);
+		if (chip->read_byte(mtd) != 0xff)
+			res = 1;
+	}
+
+	if (getchip)
+		nand_release_device(mtd);
+
+	return res;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+*/
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *chip = mtd->priv;
+	uint8_t buf[2] = { 0, 0 };
+	int block, ret;
+
+    /* Modified by zhouqi for xxx, 2013/09/02 */
+    
+    /* Attempt erase before marking OOB */
+    struct erase_info einfo; 
+    memset(&einfo, 0, sizeof(einfo));
+	einfo.mtd = mtd;
+	einfo.addr = ofs;
+	einfo.len = 1 << chip->phys_erase_shift;
+	ret = nand_erase_nand(mtd, &einfo, 0);
+
+	/* Get block number */
+	block = (int)(ofs >> chip->bbt_erase_shift);
+	if (chip->bbt)
+		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);	
+
+    struct mtd_oob_ops ops;
+	loff_t wr_ofs = ofs;
+    
+    nand_get_device(chip, mtd, FL_WRITING);   
+    ops.datbuf = NULL;
+	ops.oobbuf = buf;
+	ops.ooboffs = chip->badblockpos;
+	ops.len = ops.ooblen = 2;
+	ops.mode = MTD_OOB_PLACE;       	
+	ret = nand_do_write_oob(mtd, wr_ofs, &ops); 
+    if( ret != 0 )
+        printf("[nand_default_block_markbad] write oob error..\n");
+	nand_release_device(mtd);
+    
+    /* Update flash-based bad block table */
+    if (chip->options & NAND_USE_FLASH_BBT)
+		ret = nand_update_bbt(mtd, ofs);
+   
+    /* End modified. zhouqi, 2013/09/02 */
+   
+	if (!ret)
+		mtd->ecc_stats.badblocks++;
+
+	return ret;
+}
+
+/**
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd:	MTD device structure
+ * Check, if the device is write protected
+ *
+ * The function expects, that the device is already selected
+ */
+static int nand_check_wp(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+	/* Check the WP bit */
+	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ * @allowbbt:	1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
+			       int allowbbt)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	if (!(chip->options & NAND_BBT_SCANNED)) 
+    {
+		chip->options |= NAND_BBT_SCANNED;
+		chip->scan_bbt(mtd);
+	}
+
+	if (!chip->bbt)
+		return chip->block_bad(mtd, ofs, getchip);
+
+	/* Return info from the table */
+	return nand_isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/*
+ * Wait for the ready pin, after a command
+ * The timeout is catched later.
+ */
+void nand_wait_ready(struct mtd_info *mtd)
+{
+    #if 0
+	struct nand_chip *chip = mtd->priv;
+	u32 timeo = (CONFIG_SYS_HZ * 20) / 1000;
+	u32 time_start;
+
+	time_start = get_timer(0);
+
+	/* wait until command is processed or timeout occures */
+	while (get_timer(time_start) < timeo) {
+		if (chip->dev_ready)
+			if (chip->dev_ready(mtd))
+				break;
+	}
+    #endif
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page
+ * devices (256/512 Bytes per page)
+ */
+static void nand_command(struct mtd_info *mtd, unsigned int command,
+			 int column, int page_addr)
+{
+	register struct nand_chip *chip = mtd->priv;
+	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;
+	uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT;
+
+	/*
+	 * Write out the command to the device.
+	 */
+	if (command == NAND_CMD_SEQIN) {
+		int readcmd;
+
+		if (column >= mtd->writesize) {
+			/* OOB area */
+			column -= mtd->writesize;
+			readcmd = NAND_CMD_READOOB;
+		} else if (column < 256) {
+			/* First 256 bytes --> READ0 */
+			readcmd = NAND_CMD_READ0;
+		} else {
+			column -= 256;
+			readcmd = NAND_CMD_READ1;
+		}
+		chip->cmd_ctrl(mtd, readcmd, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
+	}
+	chip->cmd_ctrl(mtd, command, ctrl);
+
+	/*
+	 * Address cycle, when necessary
+	 */
+	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;
+	/* Serially input address */
+	if (column != -1) {
+		/* Adjust columns for 16 bit buswidth */
+		if (chip->options & NAND_BUSWIDTH_16)
+			column >>= 1;
+		chip->cmd_ctrl(mtd, column, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
+	}
+	if (page_addr != -1) {
+		chip->cmd_ctrl(mtd, page_addr, ctrl);
+		ctrl &= ~NAND_CTRL_CHANGE;
+		chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);
+		/* One more address cycle for devices > 32MiB */
+		if (chip->chipsize > (32 << 20))
+			chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);
+	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * program and erase have their own busy handlers
+	 * status and sequential in needs no delay
+	 */
+	switch (command) {
+
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_STATUS:
+		return;
+
+	case NAND_CMD_RESET:
+		if (chip->dev_ready)
+			break;
+		udelay(chip->chip_delay);
+		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+			       NAND_CTRL_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd,
+			       NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+		while (!(chip->read_byte(mtd) & NAND_STATUS_READY) &&
+			(rst_sts_cnt--));
+		return;
+
+		/* This applies to read commands */
+	default:
+		/*
+		 * If we don't have access to the busy pin, we apply the given
+		 * command delay
+		 */
+		if (!chip->dev_ready) {
+			udelay(chip->chip_delay);
+			return;
+		}
+	}
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay(100);
+
+	nand_wait_ready(mtd);
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page
+ * devices We dont have the separate regions as we have in the small page
+ * devices.  We must emulate NAND_CMD_READOOB to keep the code compatible.
+ */
+static void nand_command_lp(struct mtd_info *mtd, unsigned int command,
+			    int column, int page_addr)
+{
+	register struct nand_chip *chip = mtd->priv;
+	uint32_t rst_sts_cnt = CONFIG_SYS_NAND_RESET_CNT;
+
+	/* Emulate NAND_CMD_READOOB */
+	if (command == NAND_CMD_READOOB) {
+		column += mtd->writesize;
+		command = NAND_CMD_READ0;
+	}
+
+	/* Command latch cycle */
+	chip->cmd_ctrl(mtd, command & 0xff,
+		       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+
+	if (column != -1 || page_addr != -1) {
+		int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (chip->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			chip->cmd_ctrl(mtd, column, ctrl);
+			ctrl &= ~NAND_CTRL_CHANGE;
+			chip->cmd_ctrl(mtd, column >> 8, ctrl);
+		}
+		if (page_addr != -1) {
+			chip->cmd_ctrl(mtd, page_addr, ctrl);
+			chip->cmd_ctrl(mtd, page_addr >> 8,
+				       NAND_NCE | NAND_ALE);
+			/* One more address cycle for devices > 128MiB */
+			if (chip->chipsize > (128 << 20))
+				chip->cmd_ctrl(mtd, page_addr >> 16,
+					       NAND_NCE | NAND_ALE);
+		}
+	}
+	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
+
+	/*
+	 * program and erase have their own busy handlers
+	 * status, sequential in, and deplete1 need no delay
+	 */
+	switch (command) {
+
+	case NAND_CMD_CACHEDPROG:
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_RNDIN:
+	case NAND_CMD_STATUS:
+	case NAND_CMD_DEPLETE1:
+		return;
+
+		/*
+		 * read error status commands require only a short delay
+		 */
+	case NAND_CMD_STATUS_ERROR:
+	case NAND_CMD_STATUS_ERROR0:
+	case NAND_CMD_STATUS_ERROR1:
+	case NAND_CMD_STATUS_ERROR2:
+	case NAND_CMD_STATUS_ERROR3:
+		udelay(chip->chip_delay);
+		return;
+
+	case NAND_CMD_RESET:
+		if (chip->dev_ready)
+			break;
+		udelay(chip->chip_delay);
+		chip->cmd_ctrl(mtd, NAND_CMD_STATUS,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+		while (!(chip->read_byte(mtd) & NAND_STATUS_READY) &&
+			(rst_sts_cnt--));
+		return;
+
+	case NAND_CMD_RNDOUT:
+		/* No ready / busy check necessary */
+		chip->cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+		return;
+
+	case NAND_CMD_READ0:
+		chip->cmd_ctrl(mtd, NAND_CMD_READSTART,
+			       NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
+		chip->cmd_ctrl(mtd, NAND_CMD_NONE,
+			       NAND_NCE | NAND_CTRL_CHANGE);
+
+		/* This applies to read commands */
+	default:
+		/*
+		 * If we don't have access to the busy pin, we apply the given
+		 * command delay
+		 */
+		if (!chip->dev_ready) {
+			udelay(chip->chip_delay);
+			return;
+		}
+	}
+
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay(100);
+
+	nand_wait_ready(mtd);
+}
+
+/**
+ * nand_get_device - [GENERIC] Get chip for selected access
+ * @chip:	the nand chip descriptor
+ * @mtd:	MTD device structure
+ * @new_state:	the state which is requested
+ *
+ * Get the device and lock it for exclusive access
+ */
+static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+{
+	this->state = new_state;
+	return 0;
+}
+
+/**
+ * nand_wait - [DEFAULT]  wait until the command is done
+ * @mtd:	MTD device structure
+ * @chip:	NAND chip structure
+ *
+ * Wait for command done. This applies to erase and program only
+ * Erase can take up to 400ms and program up to 20ms according to
+ * general NAND and SmartMedia specs
+ */
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *this)
+{
+    #if 0
+	unsigned long	timeo;
+	int state = this->state;
+	u32 time_start;
+
+	if (state == FL_ERASING)
+		timeo = (CONFIG_SYS_HZ * 400) / 1000;
+	else
+		timeo = (CONFIG_SYS_HZ * 20) / 1000;
+
+	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+		this->cmdfunc(mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+	else
+		this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+
+	time_start = get_timer(0);
+
+	while (1) {
+		if (get_timer(time_start) > timeo) {
+			printf("Timeout!");
+			return 0x01;
+		}
+
+		if (this->dev_ready) {
+			if (this->dev_ready(mtd))
+				break;
+		} else {
+			if (this->read_byte(mtd) & NAND_STATUS_READY)
+				break;
+		}
+	}
+#ifdef PPCHAMELON_NAND_TIMER_HACK
+	time_start = get_timer(0);
+	while (get_timer(time_start) < 10)
+		;
+#endif /*  PPCHAMELON_NAND_TIMER_HACK */
+
+	return this->read_byte(mtd);
+    #endif
+    return 0;
+}
+
+/**
+ * nand_read_page_raw - [Intern] read raw page data without ecc
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
+ */
+#if 0
+static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+			      uint8_t *buf, int page, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	chip->read_buf(mtd, buf, mtd->writesize);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return 0;
+}
+#endif
+
+/**
+ * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+			      uint8_t *buf, int page)
+{
+	int eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	uint8_t *oob = chip->oob_poi;
+	int steps, size;
+
+	for (steps = chip->ecc.steps; steps > 0; steps--) {
+		chip->read_buf(mtd, buf, eccsize);
+		buf += eccsize;
+
+		if (chip->ecc.prepad) {
+			chip->read_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
+
+		chip->read_buf(mtd, oob, eccbytes);
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->read_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	size = mtd->oobsize - (oob - chip->oob_poi);
+	if (size)
+		chip->read_buf(mtd, oob, size);
+
+	return 0;
+}
+
+/**
+ * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ */
+#if 0
+static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->buffers->ecccalc;
+	uint8_t *ecc_code = chip->buffers->ecccode;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+	chip->ecc.read_page_raw(mtd, chip, buf, page);
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+	eccsteps = chip->ecc.steps;
+	p = buf;
+
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat < 0)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
+}
+#endif
+
+
+/**
+ * nand_read_subpage - [REPLACABLE] software ecc based sub-page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @data_offs:	offset of requested data within the page
+ * @readlen:	data length
+ * @bufpoi:	buffer to store read data
+ */
+#if 0
+static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip, uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+{
+	int start_step, end_step, num_steps;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+	uint8_t *p;
+	int data_col_addr, i, gaps = 0;
+	int datafrag_len, eccfrag_len, aligned_len, aligned_pos;
+	int busw = (chip->options & NAND_BUSWIDTH_16) ? 2 : 1;
+
+	/* Column address wihin the page aligned to ECC size (256bytes). */
+	start_step = data_offs / chip->ecc.size;
+	end_step = (data_offs + readlen - 1) / chip->ecc.size;
+	num_steps = end_step - start_step + 1;
+
+	/* Data size aligned to ECC ecc.size*/
+	datafrag_len = num_steps * chip->ecc.size;
+	eccfrag_len = num_steps * chip->ecc.bytes;
+
+	data_col_addr = start_step * chip->ecc.size;
+	/* If we read not a page aligned data */
+	if (data_col_addr != 0)
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+
+	p = bufpoi + data_col_addr;
+	chip->read_buf(mtd, p, datafrag_len);
+
+	/* Calculate  ECC */
+	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size)
+		chip->ecc.calculate(mtd, p, &chip->buffers->ecccalc[i]);
+
+	/* The performance is faster if to position offsets
+	   according to ecc.pos. Let make sure here that
+	   there are no gaps in ecc positions */
+	for (i = 0; i < eccfrag_len - 1; i++) {
+		if (eccpos[i + start_step * chip->ecc.bytes] + 1 !=
+			eccpos[i + start_step * chip->ecc.bytes + 1]) {
+			gaps = 1;
+			break;
+		}
+	}
+	if (gaps) {
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize, -1);
+		chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	} else {
+		/* send the command to read the particular ecc bytes */
+		/* take care about buswidth alignment in read_buf */
+		aligned_pos = eccpos[start_step * chip->ecc.bytes] & ~(busw - 1);
+		aligned_len = eccfrag_len;
+		if (eccpos[start_step * chip->ecc.bytes] & (busw - 1))
+			aligned_len++;
+		if (eccpos[(start_step + num_steps) * chip->ecc.bytes] & (busw - 1))
+			aligned_len++;
+
+		chip->cmdfunc(mtd, NAND_CMD_RNDOUT, mtd->writesize + aligned_pos, -1);
+		chip->read_buf(mtd, &chip->oob_poi[aligned_pos], aligned_len);
+	}
+
+	for (i = 0; i < eccfrag_len; i++)
+		chip->buffers->ecccode[i] = chip->oob_poi[eccpos[i + start_step * chip->ecc.bytes]];
+
+	p = bufpoi + data_col_addr;
+	for (i = 0; i < eccfrag_len ; i += chip->ecc.bytes, p += chip->ecc.size) {
+		int stat;
+
+		stat = chip->ecc.correct(mtd, p, &chip->buffers->ecccode[i], &chip->buffers->ecccalc[i]);
+		if (stat == -1)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
+}
+#endif
+
+/**
+ * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ *
+ * Not for syndrome calculating ecc controllers which need a special oob layout
+ */
+static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_calc = chip->buffers->ecccalc;
+	uint8_t *ecc_code = chip->buffers->ecccode;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+	}
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+	eccsteps = chip->ecc.steps;
+	p = buf;
+
+	for (i = 0 ; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], &ecc_calc[i]);
+		if (stat < 0)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
+}
+
+/**
+ * nand_read_page_hwecc_oob_first - [REPLACABLE] hw ecc, read oob first
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first.
+ * For this ECC mode, the write_page method is re-used from ECC_HW.
+ * These methods read/write ECC from the OOB area, unlike the
+ * ECC_HW_SYNDROME support with multiple ECC steps, follows the
+ * "infix ECC" scheme and reads/writes ECC from the data area, by
+ * overwriting the NAND manufacturer bad block markings.
+ */
+#if 0
+static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
+	struct nand_chip *chip, uint8_t *buf, int page, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *ecc_code = chip->buffers->ecccode;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+	uint8_t *ecc_calc = chip->buffers->ecccalc;
+
+	/* Read the OOB area first */
+	chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+	for (i = 0; i < chip->ecc.total; i++)
+		ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+		stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+		if (stat < 0)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+	}
+	return 0;
+}
+#endif
+
+/**
+ * nand_read_page_syndrome - [REPLACABLE] hardware ecc syndrom based page read
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	buffer to store read data
+ * @page:	page number to read
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static int nand_read_page_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				   uint8_t *buf, int page, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *p = buf;
+	uint8_t *oob = chip->oob_poi;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		int stat;
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READ);
+		chip->read_buf(mtd, p, eccsize);
+
+		if (chip->ecc.prepad) {
+			chip->read_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
+
+		chip->ecc.hwctl(mtd, NAND_ECC_READSYN);
+		chip->read_buf(mtd, oob, eccbytes);
+		stat = chip->ecc.correct(mtd, p, oob, NULL);
+
+		if (stat < 0)
+			mtd->ecc_stats.failed++;
+		else
+			mtd->ecc_stats.corrected += stat;
+
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->read_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->read_buf(mtd, oob, i);
+
+	return 0;
+}
+
+/**
+ * nand_transfer_oob - [Internal] Transfer oob to client buffer
+ * @chip:	nand chip structure
+ * @oob:	oob destination address
+ * @ops:	oob ops structure
+ * @len:	size of oob to transfer
+ */
+static uint8_t *nand_transfer_oob(struct nand_chip *chip, uint8_t *oob,
+				  struct mtd_oob_ops *ops, size_t len)
+{
+	switch(ops->mode) {
+
+	case MTD_OOB_PLACE:
+	case MTD_OOB_RAW:
+		memcpy(oob, chip->oob_poi + ops->ooboffs, len);
+		return oob + len;
+
+	case MTD_OOB_AUTO: {
+		struct nand_oobfree *free = chip->ecc.layout->oobfree;
+		uint32_t boffs = 0, roffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for(; free->length && len; free++, len -= bytes) {
+			/* Read request not from offset 0 ? */
+			if (unlikely(roffs)) {
+				if (roffs >= free->length) {
+					roffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + roffs;
+				bytes = min_t(size_t, len,
+					      (free->length - roffs));
+				roffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(oob, chip->oob_poi + boffs, bytes);
+			oob += bytes;
+		}
+		return oob;
+	}
+	default:
+		BUG();
+	}
+	return NULL;
+}
+
+/**
+ * nand_do_read_ops - [Internal] Read data with ECC
+ *
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob ops structure
+ *
+ * Internal function. Called with chip held.
+ */
+static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
+{
+	int chipnr, page, realpage, col, bytes, aligned;
+	struct nand_chip *chip = mtd->priv;
+	struct mtd_ecc_stats stats;
+	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+	int sndcmd = 1;
+	int ret = 0;
+	uint32_t readlen = ops->len;
+	uint32_t oobreadlen = ops->ooblen;
+	uint8_t *bufpoi, *oob, *buf;
+
+	stats = mtd->ecc_stats;
+
+	chipnr = (int)(from >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	realpage = (int)(from >> chip->page_shift);
+	page = realpage & chip->pagemask;
+
+	col = (int)(from & (mtd->writesize - 1));
+
+	buf = ops->datbuf;
+	oob = ops->oobbuf;
+
+	while(1) {
+		//WATCHDOG_RESET();
+
+		bytes = min(mtd->writesize - col, readlen);
+		aligned = (bytes == mtd->writesize);
+
+		/* Is the current page in the buffer ? */
+		if (realpage != chip->pagebuf || oob) 
+        {
+			bufpoi = aligned ? buf : chip->buffers->databuf;
+
+			if (likely(sndcmd)) 
+            {
+				chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
+				sndcmd = 0;
+			}
+
+			/* Now read the page into the buffer */
+			if (unlikely(ops->mode == MTD_OOB_RAW))
+				ret = chip->ecc.read_page_raw(mtd, chip,
+						bufpoi, page);
+			else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)/*ÔÚʹÓÃÈí¼þECCʱʹÓã¬Ò»°ãÏÖÔÚ²»Ê¹ÓÃ*/
+				ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);
+			else
+				ret = chip->ecc.read_page(mtd, chip, bufpoi, page, ops);//zhouqi add ops
+			if (ret < 0)
+				break;
+
+			/* Transfer not aligned data */
+			if (!aligned) 
+            {
+				if (!NAND_SUBPAGE_READ(chip) && !oob)
+					chip->pagebuf = realpage;
+				memcpy(buf, chip->buffers->databuf + col, bytes);
+			}
+
+			buf += bytes;
+
+			if (unlikely(oob)) 
+            {
+				/* Raw mode does data:oob:data:oob */
+				if (ops->mode != MTD_OOB_RAW) 
+                {
+					int toread = min(oobreadlen,
+						chip->ecc.layout->oobavail);
+					if (toread) 
+                    {
+						oob = nand_transfer_oob(chip,oob, ops, toread);
+						oobreadlen -= toread;
+					}
+				} 
+                else
+					buf = nand_transfer_oob(chip,buf, ops, mtd->oobsize);
+			}
+
+			if (!(chip->options & NAND_NO_READRDY)) 
+            {
+				/*
+				 * Apply delay or wait for ready/busy pin. Do
+				 * this before the AUTOINCR check, so no
+				 * problems arise if a chip which does auto
+				 * increment is marked as NOAUTOINCR by the
+				 * board driver.
+				 */
+				#if 0
+				if (!chip->dev_ready)
+					udelay(chip->chip_delay);
+				else
+					nand_wait_ready(mtd);
+				#endif
+			}
+		}
+        else 
+        {
+			memcpy(buf, chip->buffers->databuf + col, bytes);
+			buf += bytes;
+		}
+
+		readlen -= bytes;
+
+		if (!readlen)
+			break;
+
+		/* For subsequent reads align to page boundary. */
+		col = 0;
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & chip->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) 
+        {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+		}
+
+		/* Check, if the chip supports auto page increment
+		 * or if we have hit a block boundary.
+		 */
+		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
+			sndcmd = 1;
+	}
+
+	ops->retlen = ops->len - (size_t) readlen;
+	if (oob)
+		ops->oobretlen = ops->ooblen - oobreadlen;
+
+	if (ret)
+		return ret;
+
+	if (mtd->ecc_stats.failed - stats.failed)
+		return -EBADMSG;
+
+	return	mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;
+}
+
+/**
+ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * Get hold of the chip and call nand_do_read
+ */
+static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,
+		     size_t *retlen, uint8_t *buf)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	nand_get_device(chip, mtd, FL_READING);
+
+	chip->ops.len = len;
+	chip->ops.datbuf = buf;
+	chip->ops.oobbuf = NULL;
+
+	ret = nand_do_read_ops(mtd, from, &chip->ops);
+
+	*retlen = chip->ops.retlen;
+
+	nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * nand_read_oob_std - [REPLACABLE] the most common OOB data read function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to read
+ * @sndcmd:	flag whether to issue read command or not
+ */
+#if 0
+static int nand_read_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+			     int page, int sndcmd)
+{
+	if (sndcmd) {
+		chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+		sndcmd = 0;
+	}
+	chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+	return sndcmd;
+}
+#endif
+
+
+/**
+ * nand_read_oob_syndrome - [REPLACABLE] OOB data read function for HW ECC
+ *			    with syndromes
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to read
+ * @sndcmd:	flag whether to issue read command or not
+ */
+static int nand_read_oob_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				  int page, int sndcmd)
+{
+	uint8_t *buf = chip->oob_poi;
+	int length = mtd->oobsize;
+	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+	int eccsize = chip->ecc.size;
+	uint8_t *bufpoi = buf;
+	int i, toread, sndrnd = 0, pos;
+
+	chip->cmdfunc(mtd, NAND_CMD_READ0, chip->ecc.size, page);
+	for (i = 0; i < chip->ecc.steps; i++) {
+		if (sndrnd) {
+			pos = eccsize + i * (eccsize + chunk);
+			if (mtd->writesize > 512)
+				chip->cmdfunc(mtd, NAND_CMD_RNDOUT, pos, -1);
+			else
+				chip->cmdfunc(mtd, NAND_CMD_READ0, pos, page);
+		} else
+			sndrnd = 1;
+		toread = min_t(int, length, chunk);
+		chip->read_buf(mtd, bufpoi, toread);
+		bufpoi += toread;
+		length -= toread;
+	}
+	if (length > 0)
+		chip->read_buf(mtd, bufpoi, length);
+
+	return 1;
+}
+
+/**
+ * nand_write_oob_std - [REPLACABLE] the most common OOB data write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to write
+ */
+#if 0
+static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
+			      int page)
+{
+	int status = 0;
+	const uint8_t *buf = chip->oob_poi;
+	int length = mtd->oobsize;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, mtd->writesize, page);
+	chip->write_buf(mtd, buf, length);
+	/* Send command to program the OOB data */
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	status = chip->waitfunc(mtd, chip);
+
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+#endif
+
+
+/**
+ * nand_write_oob_syndrome - [REPLACABLE] OOB data write function for HW ECC
+ *			     with syndrome - only for large page flash !
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @page:	page number to write
+ */
+static int nand_write_oob_syndrome(struct mtd_info *mtd,
+				   struct nand_chip *chip, int page)
+{
+	int chunk = chip->ecc.bytes + chip->ecc.prepad + chip->ecc.postpad;
+	int eccsize = chip->ecc.size, length = mtd->oobsize;
+	int i, len, pos, status = 0, sndcmd = 0, steps = chip->ecc.steps;
+	const uint8_t *bufpoi = chip->oob_poi;
+
+	/*
+	 * data-ecc-data-ecc ... ecc-oob
+	 * or
+	 * data-pad-ecc-pad-data-pad .... ecc-pad-oob
+	 */
+	if (!chip->ecc.prepad && !chip->ecc.postpad) {
+		pos = steps * (eccsize + chunk);
+		steps = 0;
+	} else
+		pos = eccsize;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, pos, page);
+	for (i = 0; i < steps; i++) {
+		if (sndcmd) {
+			if (mtd->writesize <= 512) {
+				uint32_t fill = 0xFFFFFFFF;
+
+				len = eccsize;
+				while (len > 0) {
+					int num = min_t(int, len, 4);
+					chip->write_buf(mtd, (uint8_t *)&fill,
+							num);
+					len -= num;
+				}
+			} else {
+				pos = eccsize + i * (eccsize + chunk);
+				chip->cmdfunc(mtd, NAND_CMD_RNDIN, pos, -1);
+			}
+		} else
+			sndcmd = 1;
+		len = min_t(int, length, chunk);
+		chip->write_buf(mtd, bufpoi, len);
+		bufpoi += len;
+		length -= len;
+	}
+	if (length > 0)
+		chip->write_buf(mtd, bufpoi, length);
+
+	chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+	status = chip->waitfunc(mtd, chip);
+
+	return status & NAND_STATUS_FAIL ? -EIO : 0;
+}
+
+/**
+ * nand_do_read_oob - [Intern] NAND read out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob operations description structure
+ *
+ * NAND read out-of-band data from the spare area
+ */
+static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
+			    struct mtd_oob_ops *ops)
+{
+	int page, realpage, chipnr, sndcmd = 1;
+	struct nand_chip *chip = mtd->priv;
+	int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+	int readlen = ops->ooblen;
+	int len;
+	uint8_t *buf = ops->oobbuf;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n",
+		  (unsigned long long)from, readlen);
+
+	if (ops->mode == MTD_OOB_AUTO)
+		len = chip->ecc.layout->oobavail;
+	else
+		len = mtd->oobsize;
+
+	if (unlikely(ops->ooboffs >= len)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt to start read outside oob\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow reads past end of device */
+	if (unlikely(from >= mtd->size ||
+		     ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) -
+					(from >> chip->page_shift)) * len)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	chipnr = (int)(from >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	/* Shift to get page */
+	realpage = (int)(from >> chip->page_shift);
+	page = realpage & chip->pagemask;
+
+	while(1) {
+		//WATCHDOG_RESET();
+		sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
+
+		len = min(len, readlen);
+		buf = nand_transfer_oob(chip, buf, ops, len);
+
+		if (!(chip->options & NAND_NO_READRDY)) {
+			/*
+			 * Apply delay or wait for ready/busy pin. Do this
+			 * before the AUTOINCR check, so no problems arise if a
+			 * chip which does auto increment is marked as
+			 * NOAUTOINCR by the board driver.
+			 */
+			 
+			#if 0
+			if (!chip->dev_ready)
+				udelay(chip->chip_delay);
+			else
+				nand_wait_ready(mtd);
+			#endif
+		}
+
+		readlen -= len;
+		if (!readlen)
+			break;
+
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & chip->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+		}
+
+		/* Check, if the chip supports auto page increment
+		 * or if we have hit a block boundary.
+		 */
+		if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))
+			sndcmd = 1;
+	}
+
+	ops->oobretlen = ops->ooblen;
+	return 0;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read data and/or out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @ops:	oob operation description structure
+ *
+ * NAND read data and/or out-of-band data
+ */
+static int nand_read_oob(struct mtd_info *mtd, loff_t from,
+			 struct mtd_oob_ops *ops)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+
+	/* Do not allow reads past end of device */
+	if (ops->datbuf && (from + ops->len) > mtd->size) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	nand_get_device(chip, mtd, FL_READING);
+
+	switch(ops->mode) {
+	case MTD_OOB_PLACE:
+	case MTD_OOB_AUTO:
+	case MTD_OOB_RAW:
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = nand_do_read_oob(mtd, from, ops);
+	else
+		ret = nand_do_read_ops(mtd, from, ops);
+
+ out:
+	nand_release_device(mtd);
+	return ret;
+}
+
+
+/**
+ * nand_write_page_raw - [Intern] raw page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
+ */
+#if 0
+
+static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	chip->write_buf(mtd, buf, mtd->writesize);
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+#endif
+
+
+/**
+ * nand_write_page_raw_syndrome - [Intern] raw page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf)
+{
+	int eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	uint8_t *oob = chip->oob_poi;
+	int steps, size;
+
+	for (steps = chip->ecc.steps; steps > 0; steps--) {
+		chip->write_buf(mtd, buf, eccsize);
+		buf += eccsize;
+
+		if (chip->ecc.prepad) {
+			chip->write_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
+
+		chip->read_buf(mtd, oob, eccbytes);
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->write_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	size = mtd->oobsize - (oob - chip->oob_poi);
+	if (size)
+		chip->write_buf(mtd, oob, size);
+}
+/**
+ * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ */
+#if 0
+static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
+				  const uint8_t *buf, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *ecc_calc = chip->buffers->ecccalc;
+	const uint8_t *p = buf;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+	/* Software ecc calculation */
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize)
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+	for (i = 0; i < chip->ecc.total; i++)
+		chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+	chip->ecc.write_page_raw(mtd, chip, buf);
+}
+#endif
+
+
+/**
+ * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ */
+static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
+				  const uint8_t *buf, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	uint8_t *ecc_calc = chip->buffers->ecccalc;
+	const uint8_t *p = buf;
+	uint32_t *eccpos = chip->ecc.layout->eccpos;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+		chip->write_buf(mtd, p, eccsize);
+		chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+	}
+
+	for (i = 0; i < chip->ecc.total; i++)
+		chip->oob_poi[eccpos[i]] = ecc_calc[i];
+
+	chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+/**
+ * nand_write_page_syndrome - [REPLACABLE] hardware ecc syndrom based page write
+ * @mtd:	mtd info structure
+ * @chip:	nand chip info structure
+ * @buf:	data buffer
+ *
+ * The hw generator calculates the error syndrome automatically. Therefor
+ * we need a special oob layout and handling.
+ */
+static void nand_write_page_syndrome(struct mtd_info *mtd,
+				    struct nand_chip *chip, const uint8_t *buf, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int i, eccsize = chip->ecc.size;
+	int eccbytes = chip->ecc.bytes;
+	int eccsteps = chip->ecc.steps;
+	const uint8_t *p = buf;
+	uint8_t *oob = chip->oob_poi;
+
+	for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
+
+		chip->ecc.hwctl(mtd, NAND_ECC_WRITE);
+		chip->write_buf(mtd, p, eccsize);
+
+		if (chip->ecc.prepad) {
+			chip->write_buf(mtd, oob, chip->ecc.prepad);
+			oob += chip->ecc.prepad;
+		}
+
+		chip->ecc.calculate(mtd, p, oob);
+		chip->write_buf(mtd, oob, eccbytes);
+		oob += eccbytes;
+
+		if (chip->ecc.postpad) {
+			chip->write_buf(mtd, oob, chip->ecc.postpad);
+			oob += chip->ecc.postpad;
+		}
+	}
+
+	/* Calculate remaining oob bytes */
+	i = mtd->oobsize - (oob - chip->oob_poi);
+	if (i)
+		chip->write_buf(mtd, oob, i);
+}
+
+/**
+ * nand_write_page - [REPLACEABLE] write one page
+ * @mtd:	MTD device structure
+ * @chip:	NAND chip descriptor
+ * @buf:	the data to write
+ * @page:	page number to write
+ * @cached:	cached programming
+ * @raw:	use _raw version of write_page
+ */
+static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+			   const uint8_t *buf, int page, int cached, int raw, struct mtd_oob_ops *ops)//zhouqi add ops
+{
+	int status;
+
+	chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	if (unlikely(raw))
+		chip->ecc.write_page_raw(mtd, chip, buf);
+	else
+		chip->ecc.write_page(mtd, chip, buf, ops);//zhouqi add ops
+
+	/*
+	 * Cached progamming disabled for now, Not sure if its worth the
+	 * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s)
+	 */
+	cached = 0;
+
+	if (!cached || !(chip->options & NAND_CACHEPRG)) {
+
+		chip->cmdfunc(mtd, NAND_CMD_PAGEPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+		/*
+		 * See if operation failed and additional status checks are
+		 * available
+		 */
+		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+			status = chip->errstat(mtd, chip, FL_WRITING, status,
+					       page);
+
+		if (status & NAND_STATUS_FAIL)
+			return -EIO;
+	} else {
+		chip->cmdfunc(mtd, NAND_CMD_CACHEDPROG, -1, -1);
+		status = chip->waitfunc(mtd, chip);
+	}
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+
+	if (chip->verify_buf(mtd, buf, mtd->writesize))
+		return -EIO;
+#endif
+	return 0;
+}
+
+/**
+ * nand_fill_oob - [Internal] Transfer client buffer to oob
+ * @chip:	nand chip structure
+ * @oob:	oob data buffer
+ * @ops:	oob ops structure
+ */
+static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
+				  struct mtd_oob_ops *ops)
+{
+	size_t len = ops->ooblen;
+
+	switch(ops->mode) {
+
+	case MTD_OOB_PLACE:
+	case MTD_OOB_RAW:
+		memcpy(chip->oob_poi + ops->ooboffs, oob, len);
+		return oob + len;
+
+	case MTD_OOB_AUTO: {
+		struct nand_oobfree *free = chip->ecc.layout->oobfree;
+		uint32_t boffs = 0, woffs = ops->ooboffs;
+		size_t bytes = 0;
+
+		for(; free->length && len; free++, len -= bytes) {
+			/* Write request not from offset 0 ? */
+			if (unlikely(woffs)) {
+				if (woffs >= free->length) {
+					woffs -= free->length;
+					continue;
+				}
+				boffs = free->offset + woffs;
+				bytes = min_t(size_t, len,
+					      (free->length - woffs));
+				woffs = 0;
+			} else {
+				bytes = min_t(size_t, len, free->length);
+				boffs = free->offset;
+			}
+			memcpy(chip->oob_poi + boffs, oob, bytes);
+			oob += bytes;
+		}
+		return oob;
+	}
+	default:
+		BUG();
+	}
+	return NULL;
+}
+
+#define NOTALIGNED(x)	(x & (chip->subpagesize - 1)) != 0
+
+/**
+ * nand_do_write_ops - [Internal] NAND write with ECC
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @ops:	oob operations description structure
+ *
+ * NAND write with ECC
+ */
+static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	int chipnr, realpage, page, blockmask, column;
+	struct nand_chip *chip = mtd->priv;
+	uint32_t writelen = ops->len;
+	uint8_t *oob = ops->oobbuf;
+	uint8_t *buf = ops->datbuf;
+	int ret, subpage;
+
+	ops->retlen = 0;
+	if (!writelen)
+		return 0;
+
+	column = to & (mtd->writesize - 1);
+	subpage = column || (writelen & (mtd->writesize - 1));
+
+	if (subpage && oob)
+		return -EINVAL;
+
+	chipnr = (int)(to >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd)) {
+		printk (KERN_NOTICE "nand_do_write_ops: Device is write protected\n");
+		return -EIO;
+	}
+
+	realpage = (int)(to >> chip->page_shift);
+	page = realpage & chip->pagemask;
+	blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
+
+	/* Invalidate the page cache, when we write to the cached page */
+	if (to <= (chip->pagebuf << chip->page_shift) &&
+	    (chip->pagebuf << chip->page_shift) < (to + ops->len))
+		chip->pagebuf = -1;
+
+	/* If we're not given explicit OOB data, let it be 0xFF */
+	if (likely(!oob))
+		memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+	while(1) {
+		//WATCHDOG_RESET();
+
+		int bytes = mtd->writesize;
+		int cached = writelen > bytes && page != blockmask;
+		uint8_t *wbuf = buf;
+
+		/* Partial page write ? */
+		if (unlikely(column || writelen <= (mtd->writesize - 1))) {
+			cached = 0;
+			bytes = min_t(int, bytes - column, (int) writelen);
+			chip->pagebuf = -1;
+			memset(chip->buffers->databuf, 0xff, mtd->writesize);
+			memcpy(&chip->buffers->databuf[column], buf, bytes);
+			wbuf = chip->buffers->databuf;
+		}
+
+		if (unlikely(oob))
+			oob = nand_fill_oob(chip, oob, ops);
+
+		ret = chip->write_page(mtd, chip, wbuf, page, cached,
+				       (ops->mode == MTD_OOB_RAW), ops);//zhouqi add ops
+		if (ret)
+			break;
+
+		writelen -= bytes;
+		if (!writelen)
+			break;
+
+		column = 0;
+		buf += bytes;
+		realpage++;
+
+		page = realpage & chip->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+		}
+	}
+
+	ops->retlen = ops->len - writelen;
+	if (unlikely(oob))
+		ops->oobretlen = ops->ooblen;
+	return ret;
+}
+
+/**
+ * nand_write - [MTD Interface] NAND write with ECC
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * NAND write with ECC
+ */
+static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
+			  size_t *retlen, const uint8_t *buf)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret;
+
+	/* Do not allow writes past end of device */
+	if ((to + len) > mtd->size)
+		return -EINVAL;
+	if (!len)
+		return 0;
+
+	nand_get_device(chip, mtd, FL_WRITING);
+
+	chip->ops.len = len;
+	chip->ops.datbuf = (uint8_t *)buf;
+	chip->ops.oobbuf = NULL;
+
+	ret = nand_do_write_ops(mtd, to, &chip->ops);
+
+	*retlen = chip->ops.retlen;
+
+	nand_release_device(mtd);
+
+	return ret;
+}
+
+/**
+ * nand_do_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @ops:	oob operation description structure
+ *
+ * NAND write out-of-band
+ */
+static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
+			     struct mtd_oob_ops *ops)
+{
+	int chipnr, page, status, len;
+	struct nand_chip *chip = mtd->priv;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n",
+		  (unsigned int)to, (int)ops->ooblen);
+
+	if (ops->mode == MTD_OOB_AUTO)
+		len = chip->ecc.layout->oobavail;
+	else
+		len = mtd->oobsize;
+
+	/* Do not allow write past end of page */
+	if ((ops->ooboffs + ops->ooblen) > len) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: "
+			  "Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+	if (unlikely(ops->ooboffs >= len)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt to start write outside oob\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow reads past end of device */
+	if (unlikely(to >= mtd->size ||
+		     ops->ooboffs + ops->ooblen >
+			((mtd->size >> chip->page_shift) -
+			 (to >> chip->page_shift)) * len)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt write beyond end of device\n");
+		return -EINVAL;
+	}
+
+	chipnr = (int)(to >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	/* Shift to get page */
+	page = (int)(to >> chip->page_shift);
+
+	/*
+	 * Reset the chip. Some chips (like the Toshiba TC5832DC found in one
+	 * of my DiskOnChip 2000 test units) will clear the whole data page too
+	 * if we don't do this. I have no clue why, but I seem to have 'fixed'
+	 * it in the doc2000 driver in August 1999.  dwmw2.
+	 */
+	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		return -EROFS;
+
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page == chip->pagebuf)
+		chip->pagebuf = -1;
+
+	memset(chip->oob_poi, 0xff, mtd->oobsize);
+	nand_fill_oob(chip, ops->oobbuf, ops);
+	status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
+	memset(chip->oob_poi, 0xff, mtd->oobsize);
+
+	if (status)
+		return status;
+
+	ops->oobretlen = ops->ooblen;
+
+	return 0;
+}
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write data and/or out-of-band
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @ops:	oob operation description structure
+ */
+static int nand_write_oob(struct mtd_info *mtd, loff_t to,
+			  struct mtd_oob_ops *ops)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret = -ENOTSUPP;
+
+	ops->retlen = 0;
+
+	/* Do not allow writes past end of device */
+	if (ops->datbuf && (to + ops->len) > mtd->size) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: "
+			  "Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	nand_get_device(chip, mtd, FL_WRITING);
+
+	switch(ops->mode) {
+	case MTD_OOB_PLACE:
+	case MTD_OOB_AUTO:
+	case MTD_OOB_RAW:
+		break;
+
+	default:
+		goto out;
+	}
+
+	if (!ops->datbuf)
+		ret = nand_do_write_oob(mtd, to, ops);
+	else
+		ret = nand_do_write_ops(mtd, to, ops);
+
+ out:
+	nand_release_device(mtd);
+	return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+static void single_erase_cmd(struct mtd_info *mtd, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	/* Send commands to erase a block */
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+static void multi_erase_cmd(struct mtd_info *mtd, int page)
+{
+	struct nand_chip *chip = mtd->priv;
+	/* Send commands to erase a block */
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page++);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE1, -1, page);
+	chip->cmdfunc(mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int nand_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	return nand_erase_nand(mtd, instr, 0);
+}
+
+#define BBT_PAGE_MASK	0xffffff3f
+/**
+ * nand_erase_nand - [Internal] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ * @allowbbt:	allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
+		    int allowbbt)
+{
+	int page, status, pages_per_block, ret, chipnr;
+	struct nand_chip *chip = mtd->priv;
+	loff_t rewrite_bbt[CONFIG_SYS_NAND_MAX_CHIPS] = {0};
+	unsigned int bbt_masked_page = 0xffffffff;
+	loff_t len;
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3, "nand_erase: start = 0x%012llx, "
+		 "len = %llu\n", (unsigned long long) instr->addr,
+		 (unsigned long long) instr->len);
+
+	/* Start address must align on block boundary */
+	if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+		return -EINVAL;
+	}
+
+	/* Length must align on block boundary */
+	if (instr->len & ((1 << chip->phys_erase_shift) - 1)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0,
+			  "nand_erase: Length not block aligned\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((instr->len + instr->addr) > mtd->size) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0,
+			  "nand_erase: Erase past end of device\n");
+		return -EINVAL;
+	}
+
+	instr->fail_addr = 0xffffffff;
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device(chip, mtd, FL_ERASING);
+
+	/* Shift to get first page */
+	page = (int)(instr->addr >> chip->page_shift);
+	chipnr = (int)(instr->addr >> chip->chip_shift);
+
+	/* Calculate pages in each block */
+	pages_per_block = 1 << (chip->phys_erase_shift - chip->page_shift);
+
+	/* Select the NAND device */
+	chip->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd)) {
+		MTDDEBUG (MTD_DEBUG_LEVEL0,
+			  "nand_erase: Device is write protected!!!\n");
+		instr->state = MTD_ERASE_FAILED;
+		goto erase_exit;
+	}
+
+	/*
+	 * If BBT requires refresh, set the BBT page mask to see if the BBT
+	 * should be rewritten. Otherwise the mask is set to 0xffffffff which
+	 * can not be matched. This is also done when the bbt is actually
+	 * erased to avoid recusrsive updates
+	 */
+	if (chip->options & BBT_AUTO_REFRESH && !allowbbt)
+		bbt_masked_page = chip->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
+
+	/* Loop through the pages */
+	len = instr->len;
+
+	instr->state = MTD_ERASING;
+
+	while (len) {
+		//WATCHDOG_RESET();
+		/*
+		 * heck if we have a bad block, we do not erase bad blocks !
+		 */
+		if (nand_block_checkbad(mtd, ((loff_t) page) <<
+					chip->page_shift, 0, allowbbt)) {
+			printk(KERN_WARNING "nand_erase: attempt to erase a "
+			       "bad block at page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+
+		/*
+		 * Invalidate the page cache, if we erase the block which
+		 * contains the current cached page
+		 */
+		if (page <= chip->pagebuf && chip->pagebuf <
+		    (page + pages_per_block))
+			chip->pagebuf = -1;
+
+		chip->erase_cmd(mtd, page & chip->pagemask);
+
+		status = chip->waitfunc(mtd, chip);
+
+		/*
+		 * See if operation failed and additional status checks are
+		 * available
+		 */
+		if ((status & NAND_STATUS_FAIL) && (chip->errstat))
+			status = chip->errstat(mtd, chip, FL_ERASING,
+					       status, page);
+
+		/* See if block erase succeeded */
+		if (status & NAND_STATUS_FAIL) {
+			MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase: "
+				  "Failed erase, page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			instr->fail_addr = ((loff_t)page << chip->page_shift);
+			goto erase_exit;
+		}
+
+		/*
+		 * If BBT requires refresh, set the BBT rewrite flag to the
+		 * page being erased
+		 */
+		if (bbt_masked_page != 0xffffffff &&
+		    (page & BBT_PAGE_MASK) == bbt_masked_page)
+			rewrite_bbt[chipnr] =
+				((loff_t)page << chip->page_shift);
+
+		/* Increment page address and decrement length */
+		len -= (1 << chip->phys_erase_shift);
+		page += pages_per_block;
+
+		/* Check, if we cross a chip boundary */
+		if (len && !(page & chip->pagemask)) {
+			chipnr++;
+			chip->select_chip(mtd, -1);
+			chip->select_chip(mtd, chipnr);
+
+			/*
+			 * If BBT requires refresh and BBT-PERCHIP, set the BBT
+			 * page mask to see if this BBT should be rewritten
+			 */
+			if (bbt_masked_page != 0xffffffff &&
+			    (chip->bbt_td->options & NAND_BBT_PERCHIP))
+				bbt_masked_page = chip->bbt_td->pages[chipnr] &
+					BBT_PAGE_MASK;
+		}
+	}
+	instr->state = MTD_ERASE_DONE;
+
+ erase_exit:
+
+	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_device(mtd);
+
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(instr);
+
+	/*
+	 * If BBT requires refresh and erase was successful, rewrite any
+	 * selected bad block tables
+	 */
+	if (bbt_masked_page == 0xffffffff || ret)
+		return ret;
+
+	for (chipnr = 0; chipnr < chip->numchips; chipnr++) {
+		if (!rewrite_bbt[chipnr])
+			continue;
+		/* update the BBT for chip */
+		MTDDEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt "
+			  "(%d:0x%0llx 0x%0x)\n", chipnr, (u64)rewrite_bbt[chipnr],
+			  chip->bbt_td->pages[chipnr]);
+		nand_update_bbt(mtd, rewrite_bbt[chipnr]);
+	}
+
+	/* Return more or less happy */
+	return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd:	MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void nand_sync(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+	/* Grab the lock and see if the device is available */
+	nand_get_device(chip, mtd, FL_SYNCING);
+	/* Release it and go back */
+	nand_release_device(mtd);
+}
+
+/**
+ * nand_block_isbad - [MTD Interface] Check if block at offset is bad
+ * @mtd:	MTD device structure
+ * @offs:	offset relative to mtd start
+ */
+static int nand_block_isbad(struct mtd_info *mtd, loff_t offs)
+{
+	/* Check for invalid offset */
+	if (offs > mtd->size)
+		return -EINVAL;
+
+	return nand_block_checkbad(mtd, offs, 1, 0);
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark block at the given offset as bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset relative to mtd start
+ */
+static int nand_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *chip = mtd->priv;
+	int ret;
+
+	ret = nand_block_isbad(mtd, ofs);
+	
+	if (ret) 
+	{
+		/* If it was bad already, return success and do nothing. */
+		if (ret > 0)
+			return 0;
+		return ret;
+	}
+
+	return chip->block_markbad(mtd, ofs);
+}
+
+/*
+ * Set default functions
+ */
+static void nand_set_defaults(struct nand_chip *chip, int busw)
+{
+	/* check for proper chip_delay setup, set 20us if not */
+	if (!chip->chip_delay)
+		chip->chip_delay = 20;
+
+	/* check, if a user supplied command function given */
+	if (chip->cmdfunc == NULL)
+		chip->cmdfunc = nand_command;
+
+	/* check, if a user supplied wait function given */
+	if (chip->waitfunc == NULL)
+		chip->waitfunc = nand_wait;
+
+	if (!chip->select_chip)
+		chip->select_chip = nand_select_chip;
+	if (!chip->read_byte)
+		chip->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+	if (!chip->read_word)
+		chip->read_word = nand_read_word;
+	if (!chip->block_bad)
+		chip->block_bad = nand_block_bad;
+	if (!chip->block_markbad)
+		chip->block_markbad = nand_default_block_markbad;
+	if (!chip->write_buf)
+		chip->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+	if (!chip->read_buf)
+		chip->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+	if (!chip->verify_buf)
+		chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+	if (!chip->scan_bbt)
+		chip->scan_bbt = nand_default_bbt;
+	if (!chip->controller)
+		chip->controller = &chip->hwcontrol;
+}
+
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+static u16 onfi_crc16(u16 crc, u8 const *p, size_t len)
+{
+	int i;
+
+	while (len--) {
+		crc ^= *p++ << 8;
+		for (i = 0; i < 8; i++)
+			crc = (crc << 1) ^ ((crc & 0x8000) ? 0x8005 : 0);
+	}
+
+	return crc;
+}
+
+/*
+ * Check if the NAND chip is ONFI compliant, returns 1 if it is, 0 otherwise
+ */
+static int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	struct nand_onfi_params *p = &chip->onfi_params;
+	int i;
+	int val;
+
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x20, -1);
+	if (chip->read_byte(mtd) != 'O' || chip->read_byte(mtd) != 'N' ||
+		chip->read_byte(mtd) != 'F' || chip->read_byte(mtd) != 'I')
+		return 0;
+
+	printk(KERN_INFO "ONFI flash detected\n");
+	chip->cmdfunc(mtd, NAND_CMD_PARAM, 0, -1);
+	for (i = 0; i < 3; i++) {
+		chip->read_buf(mtd, (uint8_t *)p, sizeof(*p));
+		if (onfi_crc16(ONFI_CRC_BASE, (uint8_t *)p, 254) ==
+						le16_to_cpu(p->crc)) {
+			printk(KERN_INFO "ONFI param page %d valid\n", i);
+			break;
+		}
+	}
+
+	if (i == 3)
+		return 0;
+
+	/* check version */
+	val = le16_to_cpu(p->revision);
+	if (val & (1 << 5))
+		chip->onfi_version = 23;
+	else if (val & (1 << 4))
+		chip->onfi_version = 22;
+	else if (val & (1 << 3))
+		chip->onfi_version = 21;
+	else if (val & (1 << 2))
+		chip->onfi_version = 20;
+	else if (val & (1 << 1))
+		chip->onfi_version = 10;
+	else
+		chip->onfi_version = 0;
+
+	if (!chip->onfi_version) {
+		printk(KERN_INFO "%s: unsupported ONFI "
+					"version: %d\n", __func__, val);
+		return 0;
+	}
+
+	if (!mtd->name)
+		mtd->name = p->model;
+
+	mtd->writesize = le32_to_cpu(p->byte_per_page);
+	mtd->erasesize = le32_to_cpu(p->pages_per_block) * mtd->writesize;
+	mtd->oobsize = le16_to_cpu(p->spare_bytes_per_page);
+	chip->chipsize = (uint64_t)le32_to_cpu(p->blocks_per_lun) * mtd->erasesize;
+	*busw = 0;
+	if (le16_to_cpu(p->features) & 1)
+		*busw = NAND_BUSWIDTH_16;
+
+	return 1;
+}
+#else
+static inline int nand_flash_detect_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					int *busw)
+{
+	return 0;
+}
+#endif
+
+void nand_flash_detect_non_onfi(struct mtd_info *mtd,
+					struct nand_chip *chip,
+					const struct nand_flash_dev *type,
+					int *busw)
+{
+	/* Newer devices have all the information in additional id bytes */
+	if (!type->pagesize) {
+		int extid;
+		/* The 3rd id byte holds MLC / multichip data */
+		chip->cellinfo = chip->read_byte(mtd);
+		/* The 4th id byte is the important one */
+		extid = chip->read_byte(mtd);
+		/* Calc pagesize */
+		mtd->writesize = 1024 << (extid & 0x3);
+		extid >>= 2;
+		/* Calc oobsize */
+		mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9);
+		extid >>= 2;
+		/* Calc blocksize. Blocksize is multiples of 64KiB */
+		mtd->erasesize = (64 * 1024) << (extid & 0x03);
+		extid >>= 2;
+		/* Get buswidth information */
+		*busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
+
+	} else {
+		/*
+		 * Old devices have chip data hardcoded in the device id table
+		 */
+		mtd->erasesize = type->erasesize;
+		mtd->writesize = type->pagesize;
+		mtd->oobsize = g_nand_dev_info->oob_size;   //zhouqi
+		*busw = type->options & NAND_BUSWIDTH_16;
+	}
+}
+
+/*
+ * Get the flash and manufacturer id and lookup if the type is supported
+ */
+const struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
+						  struct nand_chip *chip,
+						  int busw,
+						  int *maf_id, int *dev_id,
+						  const struct nand_flash_dev *type)
+{
+	int ret, maf_idx;
+	int tmp_id, tmp_manf;
+
+	/* Select the device */
+	chip->select_chip(mtd, 0);
+
+	/*
+	 * Reset the chip, required by some chips (e.g. Micron MT29FxGxxxxx)
+	 * after power-up
+	 */
+	chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Send the command for reading device ID */
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+	/* Read manufacturer and device IDs */
+	*maf_id = chip->read_byte(mtd);
+	*dev_id = chip->read_byte(mtd);
+
+	/* Try again to make sure, as some systems the bus-hold or other
+	 * interface concerns can cause random data which looks like a
+	 * possibly credible NAND flash to appear. If the two results do
+	 * not match, ignore the device completely.
+	 */
+
+	chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+
+	/* Read manufacturer and device IDs */
+
+	tmp_manf = chip->read_byte(mtd);
+	tmp_id = chip->read_byte(mtd);
+
+	if (tmp_manf != *maf_id || tmp_id != *dev_id) {
+		printk(KERN_INFO "%s: second ID read did not match "
+		       "%02x,%02x against %02x,%02x\n", __func__,
+		       *maf_id, *dev_id, tmp_manf, tmp_id);
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (!type)
+		type = nand_flash_ids;
+
+	for (; type->name != NULL; type++)
+		if (*dev_id == type->id)
+			break;
+
+	if (!type->name) {
+		/* supress warning if there is no nand */
+		if (*maf_id != 0x00 && *maf_id != 0xff &&
+		    *dev_id  != 0x00 && *dev_id  != 0xff)
+			printk(KERN_INFO "%s: unknown NAND device: "
+				"Manufacturer ID: 0x%02x, Chip ID: 0x%02x\n",
+				__func__, *maf_id, *dev_id);
+		return ERR_PTR(-ENODEV);
+	}
+
+	if (!mtd->name)
+		mtd->name = type->name;
+
+	if((winbond_dev_id2 == 0x20) 
+		||((tmp_manf == NAND_MFR_DOSILICON) && (tmp_id == NAND_DEVID_DOSILICON_512M)))
+	{
+		chip->chipsize = (uint64_t)type->chipsize << 19;
+	}
+	else
+	{
+		chip->chipsize = (uint64_t)type->chipsize << 20;
+	}
+	
+	chip->onfi_version = 0;
+	
+#ifdef CONFIG_SYS_NAND_ONFI_DETECTION
+	ret = nand_flash_detect_onfi(mtd, chip, &busw);
+	if (!ret)
+#endif
+	{
+		nand_flash_detect_non_onfi(mtd, chip, type, &busw);
+	}
+
+	/* Get chip options, preserve non chip based options */
+	chip->options &= ~NAND_CHIPOPTIONS_MSK;
+	chip->options |= type->options & NAND_CHIPOPTIONS_MSK;
+
+	/*
+	 * Set chip as a default. Board drivers can override it, if necessary
+	 */
+	chip->options |= NAND_NO_AUTOINCR;
+
+	/* Try to identify manufacturer */
+	for (maf_idx = 0; nand_manuf_ids[maf_idx].id != 0x0; maf_idx++) {
+		if (nand_manuf_ids[maf_idx].id == *maf_id)
+			break;
+	}
+
+	/*
+	 * Check, if buswidth is correct. Hardware drivers should set
+	 * chip correct !
+	 */
+	if (busw != (chip->options & NAND_BUSWIDTH_16)) {
+		printk(KERN_INFO "NAND device: Manufacturer ID:"
+		       " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
+		       *dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
+		printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
+		       (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
+		       busw ? 16 : 8);
+		return ERR_PTR(-EINVAL);
+	}
+
+	/* Calculate the address shift from the page size */
+	chip->page_shift = ffs(mtd->writesize) - 1;
+	/* Convert chipsize to number of pages per chip -1. */
+	chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+
+	chip->bbt_erase_shift = chip->phys_erase_shift =
+		ffs(mtd->erasesize) - 1;
+	if (chip->chipsize & 0xffffffff)
+		chip->chip_shift = ffs((unsigned)chip->chipsize) - 1;
+	else
+		chip->chip_shift = ffs((unsigned)(chip->chipsize >> 32)) + 31;
+
+	/* Set the bad block position */
+	chip->badblockpos = mtd->writesize > 512 ?
+		NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+	/* Check if chip is a not a samsung device. Do not clear the
+	 * options for chips which are not having an extended id.
+	 */
+	if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize)
+		chip->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+
+	/* Check for AND chips with 4 page planes */
+	if (chip->options & NAND_4PAGE_ARRAY)
+		chip->erase_cmd = multi_erase_cmd;
+	else
+		chip->erase_cmd = single_erase_cmd;
+
+	/* Do not replace user supplied command function ! */
+	if (mtd->writesize > 512 && chip->cmdfunc == nand_command)
+		chip->cmdfunc = nand_command_lp;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL0, "NAND device: Manufacturer ID:"
+		  " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id, *dev_id,
+		  nand_manuf_ids[maf_idx].name, type->name);
+
+	return type;
+}
+
+/**
+ * nand_scan_ident - [NAND Interface] Scan for the NAND device
+ * @mtd:	     MTD device structure
+ * @maxchips:	     Number of chips to scan for
+ * @table:	     Alternative NAND ID table
+ *
+ * This is the first phase of the normal nand_scan() function. It
+ * reads the flash ID and sets up MTD fields accordingly.
+ *
+ * The mtd->owner field must be set to the module of the caller.
+ */
+int nand_scan_ident(struct mtd_info *mtd, int maxchips,
+		    const struct nand_flash_dev *table)
+{
+	int i, busw, nand_maf_id, nand_dev_id;
+	struct nand_chip *chip = mtd->priv;
+	const struct nand_flash_dev *type;
+
+	/* Get buswidth to select the correct functions */
+	busw = chip->options & NAND_BUSWIDTH_16;
+	/* Set the default functions */
+	nand_set_defaults(chip, busw);
+
+	/* Read the flash type */
+	type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, &nand_dev_id, table);
+
+	if (IS_ERR(type)) {
+#ifndef CONFIG_SYS_NAND_QUIET_TEST
+		printk(KERN_WARNING "No NAND device found!!!\n");
+#endif
+		chip->select_chip(mtd, -1);
+		return PTR_ERR(type);
+	}
+
+	/* Check for a chip array */
+	for (i = 1; i < maxchips; i++) 
+    {
+		chip->select_chip(mtd, i);
+		/* See comment in nand_get_flash_type for reset */
+		chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+		/* Send the command for reading device ID */
+		chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+		/* Read manufacturer and device IDs */
+		if (nand_maf_id != chip->read_byte(mtd) ||
+		    nand_dev_id != chip->read_byte(mtd))
+			break;
+	}
+#if DEBUG
+	if (i > 1)
+		printk(KERN_INFO "%d NAND chips detected\n", i);
+#endif
+
+	/* Store the number of chips and calc total size for mtd */
+	chip->numchips = i;
+	mtd->size = i * chip->chipsize;
+
+	return 0;
+}
+
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd:	    MTD device structure
+ *
+ * This is the second phase of the normal nand_scan() function. It
+ * fills out all the uninitialized function pointers with the defaults
+ * and scans for a bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+	int i;
+	struct nand_chip *chip = NULL;
+	
+	if(mtd->priv == NULL)
+	{
+		return -EINVAL;
+	}
+	chip = mtd->priv;
+
+	if (!(chip->options & NAND_OWN_BUFFERS))
+		chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+	if (!chip->buffers)
+		return -ENOMEM;
+
+	/* Set the internal oob buffer location, just after the page data */
+	chip->oob_poi = chip->buffers->databuf + mtd->writesize;
+
+	/*
+	 * If no default placement scheme is given, select an appropriate one
+	 */
+	if (!chip->ecc.layout) 
+    {
+		switch (mtd->oobsize) 
+        {
+    		case 8:
+    			chip->ecc.layout = &nand_oob_8;
+    			break;
+    		case 16:
+    			chip->ecc.layout = &nand_oob_16;
+    			break;
+    		case 64:
+    			chip->ecc.layout = &nand_oob_64;
+    			break;
+    		case 128:
+    			chip->ecc.layout = &nand_oob_128;
+    			break;
+    		default:
+    			printk(KERN_WARNING "No oob scheme defined for "
+    			       "oobsize %d\n", mtd->oobsize);
+				return -1;
+		}
+	}
+
+	if (!chip->write_page)
+		chip->write_page = nand_write_page;
+
+	/*
+	 * check ECC mode, default to software if 3byte/512byte hardware ECC is
+	 * selected and we have 256 byte pagesize fallback to software ECC
+	 */
+
+	switch (chip->ecc.mode) 
+    {
+    	case NAND_ECC_HW_OOB_FIRST:
+            #if 0
+    		/* Similar to NAND_ECC_HW, but a separate read_page handle */
+    		if (!chip->ecc.calculate || !chip->ecc.correct ||
+    		     !chip->ecc.hwctl) {
+    			printk(KERN_WARNING "No ECC functions supplied, "
+    			       "Hardware ECC not possible\n");
+    			BUG();
+    		}
+    		if (!chip->ecc.read_page)
+    			chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+            #endif
+    	case NAND_ECC_HW:
+            #if 0
+    		/* Use standard hwecc read page function ? */
+    		if (!chip->ecc.read_page)
+    			chip->ecc.read_page = nand_read_page_hwecc;
+    		if (!chip->ecc.write_page)
+    			chip->ecc.write_page = nand_write_page_hwecc;
+    		if (!chip->ecc.read_page_raw)
+    			chip->ecc.read_page_raw = nand_read_page_raw;
+    		if (!chip->ecc.write_page_raw)
+    			chip->ecc.write_page_raw = nand_write_page_raw;
+    		if (!chip->ecc.read_oob)
+    			chip->ecc.read_oob = nand_read_oob_std;
+    		if (!chip->ecc.write_oob)
+    			chip->ecc.write_oob = nand_write_oob_std;
+            #endif
+    	case NAND_ECC_HW_SYNDROME:      //denali controller use
+    		if ((!chip->ecc.calculate || !chip->ecc.correct ||
+    		     !chip->ecc.hwctl) &&
+    		    (!chip->ecc.read_page ||
+    		     chip->ecc.read_page == nand_read_page_hwecc ||
+    		     !chip->ecc.write_page ||
+    		     chip->ecc.write_page == nand_write_page_hwecc)) 
+    		{
+    			printk(KERN_WARNING "No ECC functions supplied, "
+    			       "Hardware ECC not possible\n");
+    			BUG();
+    		}
+    		/* Use standard syndrome read/write page function ? */
+    		if (!chip->ecc.read_page)
+    			chip->ecc.read_page = nand_read_page_syndrome;
+    		if (!chip->ecc.write_page)
+    			chip->ecc.write_page = nand_write_page_syndrome;
+    		if (!chip->ecc.read_page_raw)
+    			chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
+    		if (!chip->ecc.write_page_raw)
+    			chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
+    		if (!chip->ecc.read_oob)
+    			chip->ecc.read_oob = nand_read_oob_syndrome;
+    		if (!chip->ecc.write_oob)
+    			chip->ecc.write_oob = nand_write_oob_syndrome;
+
+    		if (mtd->writesize >= chip->ecc.size)
+    			break; 
+            printf("[nand_scan_tail] error...Please modify...\n");  /* add by zhouqi */
+    		printk(KERN_WARNING "%d byte HW ECC not possible on "
+    		       "%d byte page size, fallback to SW ECC\n",
+    		       chip->ecc.size, mtd->writesize);
+    		chip->ecc.mode = NAND_ECC_SOFT;
+
+    	case NAND_ECC_SOFT:
+            #if 0
+    		chip->ecc.calculate = nand_calculate_ecc;
+    		chip->ecc.correct = nand_correct_data;
+    		chip->ecc.read_page = nand_read_page_swecc;
+    		chip->ecc.read_subpage = nand_read_subpage;
+    		chip->ecc.write_page = nand_write_page_swecc;
+    		chip->ecc.read_page_raw = nand_read_page_raw;
+    		chip->ecc.write_page_raw = nand_write_page_raw;
+    		chip->ecc.read_oob = nand_read_oob_std;
+    		chip->ecc.write_oob = nand_write_oob_std;
+    		chip->ecc.size = 256;
+    		chip->ecc.bytes = 3;
+    		break;
+            #endif
+    	case NAND_ECC_NONE:
+            #if 0
+    		printk(KERN_WARNING "NAND_ECC_NONE selected by board driver. "
+    		       "This is not recommended !!\n");
+    		chip->ecc.read_page = nand_read_page_raw;
+    		chip->ecc.write_page = nand_write_page_raw;
+    		chip->ecc.read_oob = nand_read_oob_std;
+    		chip->ecc.read_page_raw = nand_read_page_raw;
+    		chip->ecc.write_page_raw = nand_write_page_raw;
+    		chip->ecc.write_oob = nand_write_oob_std;
+    		chip->ecc.size = mtd->writesize;
+    		chip->ecc.bytes = 0;
+    		break;
+            #endif
+    	default:
+    		printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
+    		       chip->ecc.mode);
+		BUG();
+	}
+
+	/*
+	 * The number of bytes available for a client to place data into
+	 * the out of band area
+	 */
+	chip->ecc.layout->oobavail = 0;
+	for (i = 0; i < ARRAY_SIZE(chip->ecc.layout->oobfree)
+			&& chip->ecc.layout->oobfree[i].length; i++)
+		chip->ecc.layout->oobavail +=
+			chip->ecc.layout->oobfree[i].length;
+	mtd->oobavail = chip->ecc.layout->oobavail;
+
+	/*
+	 * Set the number of read / write steps for one page depending on ECC
+	 * mode
+	 */
+	chip->ecc.steps = mtd->writesize / chip->ecc.size;
+	if(chip->ecc.steps * chip->ecc.size != mtd->writesize) 
+    {
+		printk(KERN_WARNING "Invalid ecc parameters\n");
+		BUG();
+	}
+	chip->ecc.total = chip->ecc.steps * chip->ecc.bytes;
+
+	/*
+	 * Allow subpage writes up to ecc.steps. Not possible for MLC
+	 * FLASH.
+	 */
+	if (!(chip->options & NAND_NO_SUBPAGE_WRITE) &&
+	    !(chip->cellinfo & NAND_CI_CELLTYPE_MSK)) {
+		switch(chip->ecc.steps) {
+		case 2:
+			mtd->subpage_sft = 1;
+			break;
+		case 4:
+		case 8:
+		case 16:
+			mtd->subpage_sft = 2;
+			break;
+		}
+	}
+	chip->subpagesize = mtd->writesize >> mtd->subpage_sft;
+
+	/* Initialize state */
+	chip->state = FL_READY;
+
+	/* De-select the device */
+	chip->select_chip(mtd, -1);
+
+	/* Invalidate the pagebuffer reference */
+	chip->pagebuf = -1;
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->erase = nand_erase;
+	mtd->point = NULL;
+	mtd->unpoint = NULL;
+	mtd->read = nand_read;
+	mtd->write = nand_write;
+	mtd->read_oob = nand_read_oob;
+	mtd->write_oob = nand_write_oob;
+	mtd->sync = nand_sync;
+	mtd->lock = NULL;
+	mtd->unlock = NULL;
+	mtd->block_isbad = nand_block_isbad;
+	mtd->block_markbad = nand_block_markbad;
+
+	/* propagate ecc.layout to mtd_info */
+	mtd->ecclayout = chip->ecc.layout;
+
+	/* Check, if we should skip the bad block table scan */
+	if (chip->options & NAND_SKIP_BBTSCAN)
+		chip->options |= NAND_BBT_SCANNED;
+    
+    // ½áÊøÊ±É¨Ã裨²¢Èç¹ûÓбØÒªÖؽ¨£©bbt±í£¬×¢ÒâÖñêÖ¾£¬±ÜÃâÖØÈë
+    chip->options |= NAND_BBT_SCANNED;  //zhouqi
+	return chip->scan_bbt(mtd);         //zhouqi
+}
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd:	MTD device structure
+ * @maxchips:	Number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ * The mtd->owner field must be set to the module of the caller
+ *
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+	int ret;
+
+	ret = nand_scan_ident(mtd, maxchips, NULL);
+	if (!ret)
+		ret = nand_scan_tail(mtd);
+	return ret;
+}
+
+/**
+ * nand_release - [NAND Interface] Free resources held by the NAND device
+ * @mtd:	MTD device structure
+*/
+void nand_release(struct mtd_info *mtd)
+{
+	struct nand_chip *chip = mtd->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	/* Deregister partitions */
+	del_mtd_partitions(mtd);
+#endif
+
+	/* Free bad block table memory */
+	kfree(chip->bbt);
+	if (!(chip->options & NAND_OWN_BUFFERS))
+		kfree(chip->buffers);
+}
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand_bbt.c b/boot/common/src/uboot/drivers/mtd/nand/nand_bbt.c
new file mode 100644
index 0000000..cd5d9d8
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_bbt.c
@@ -0,0 +1,1323 @@
+/*
+ *  drivers/mtd/nand_bbt.c
+ *
+ *  Overview:
+ *   Bad block table support for the NAND driver
+ *
+ *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table
+ * depending on the options in the bbt descriptor(s). If a bbt is found
+ * then the contents are read and the memory based bbt is created. If a
+ * mirrored bbt is selected then the mirror is searched too and the
+ * versions are compared. If the mirror has a greater version number
+ * than the mirror bbt is used to build the memory based bbt.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the bbt's is out of date or does not exist it is (re)created.
+ * If no bbt exists at all then the device is scanned for factory marked
+ * good / bad blocks and the bad block tables are created.
+ *
+ * For manufacturer created bbts like the one found on M-SYS DOC devices
+ * the bbt is searched and read but never created
+ *
+ * The autogenerated bad block table is located in the last good blocks
+ * of the device. The table is mirrored, so it can be updated eventually.
+ * The table is marked in the oob area with an ident pattern and a version
+ * number which indicates which of both tables is more up to date.
+ *
+ * The table uses 2 bits per block
+ * 11b:	block is good
+ * 00b:	block is factory marked bad
+ * 01b, 10b:	block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b:		block is good
+ * 01b:		block is marked bad due to wear
+ * 10b:		block is reserved (to protect the bbt area)
+ * 11b:		block is factory marked bad
+ *
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space necessary for a bbt in FLASH does not exceed a block boundary
+ *
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <linux/mtd/compat.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+
+#include <asm/errno.h>
+
+/**
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:	the buffer to search
+ * @len:	the length of buffer to search
+ * @paglen:	the pagelength
+ * @td:		search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers.
+ * If the SCAN_EMPTY option is set then check, if all bytes except the
+ * pattern area contain 0xff
+ *
+*/
+static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+	int i, end = 0;
+	uint8_t *p = buf;
+
+	end = paglen + td->offs;
+	if (td->options & NAND_BBT_SCANEMPTY) 
+    {
+		for (i = 0; i < end; i++) 
+        {
+			if (p[i] != 0xff)
+				return -1;
+		}
+	}
+	p += end;
+
+	/* Compare the pattern */
+	for (i = 0; i < td->len; i++) 
+    {
+		if (p[i] != td->pattern[i])
+			return -1;
+	}
+
+	if (td->options & NAND_BBT_SCANEMPTY) 
+    {
+		p += td->len;
+		end += td->len;
+		for (i = end; i < len; i++) {
+			if (*p++ != 0xff)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:	the buffer to search
+ * @td:		search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but
+ * no optional empty check
+ *
+*/
+static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
+{
+	int i;
+	uint8_t *p = buf;
+
+	/* Compare the pattern */
+	for (i = 0; i < td->len; i++) 
+    {
+		if (p[td->offs + i] != td->pattern[i])
+			return -1;
+	}
+	return 0;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @page:	the starting page
+ * @num:	the number of bbt descriptors to read
+ * @bits:	number of bits per block
+ * @offs:	offset in the memory table
+ * @reserved_block_code:	Pattern to identify reserved blocks
+ *
+ * Read the bad block table starting from page.
+ *
+ */
+static int read_bbt(struct mtd_info *mtd, uint8_t *buf, int page, int num,
+		    int bits, int offs, int reserved_block_code)
+{
+	int res, i, j, act = 0;
+	struct nand_chip *this = mtd->priv;
+	size_t retlen, len, totlen;
+	loff_t from;
+	uint8_t msk = (uint8_t) ((1 << bits) - 1);
+    mtd_oob_mode_t mode_temp = this->ops.mode;  //add zhouqi
+	totlen = (num * bits) >> 3;
+	from = ((loff_t) page) << this->page_shift;
+
+	while (totlen) 
+    {
+		len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
+        this->ops.mode = MTD_OOB_RAW;   //zhouqi add  ʹ¶ÁÈ¡BBTʱ£¬Ê¹Óà ecc.read_page_raw
+		res = mtd->read(mtd, from, len, &retlen, buf);  
+		if (res < 0) 
+        {
+			if (retlen != len) 
+            {
+				printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
+				return res;
+			}
+			printk(KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+		}
+
+		/* Analyse data */
+		for (i = 0; i < len; i++) 
+        {
+			uint8_t dat = buf[i];
+			for (j = 0; j < 8; j += bits, act += 2) 
+            {
+				uint8_t tmp = (dat >> j) & msk;
+				if (tmp == msk)
+					continue;
+				if (reserved_block_code && (tmp == reserved_block_code)) 
+                {
+                    #if NAND_BAD_DEBUG
+					printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%012llx\n",
+						(loff_t)((offs << 2) +
+						(act >> 1)) <<
+						this->bbt_erase_shift);
+                    #endif
+					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+					mtd->ecc_stats.bbtblocks++;
+					continue;
+				}
+				/* Leave it for now, if its matured we can move this
+				 * message to MTD_DEBUG_LEVEL0 */
+				#if NAND_BAD_DEBUG
+				printk(KERN_DEBUG "nand_read_bbt: Bad block at 0x%012llx\n",
+					(loff_t)((offs << 2) + (act >> 1)) <<
+					this->bbt_erase_shift);
+                #endif
+				/* Factory marked bad or worn out ? */
+				if (tmp == 0)
+					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+				else
+					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+				mtd->ecc_stats.badblocks++;
+			}
+		}
+		totlen -= len;
+		from += len;
+	}
+    this->ops.mode = mode_temp;     //add zhouqi
+	return 0;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ * @chip:	read the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+*/
+static int read_abs_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int res = 0, i;
+	int bits;
+
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+	if (td->options & NAND_BBT_PERCHIP) 
+    {
+		int offs = 0;
+		for (i = 0; i < this->numchips; i++) 
+        {
+			if (chip == -1 || chip == i)
+				res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+			if (res)
+				return res;
+			offs += this->chipsize >> (this->bbt_erase_shift + 2);
+		}
+	} 
+    else 
+    {
+		res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+		if (res)
+			return res;
+	}
+	return 0;
+}
+
+/*
+ * Scan read raw data from flash
+ */
+static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
+			 size_t len)
+{
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OOB_RAW;
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = buf;
+	ops.datbuf = buf;
+	ops.len = len;
+
+	return mtd->read_oob(mtd, offs, &ops);
+}
+
+/*
+ * Scan write data with oob to flash
+ */
+static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
+			  uint8_t *buf, uint8_t *oob)
+{
+	struct mtd_oob_ops ops;
+
+	ops.mode = MTD_OOB_RAW;//MTD_OOB_PLACE  zhouqi
+	ops.ooboffs = 0;
+	ops.ooblen = mtd->oobsize;
+	ops.datbuf = buf;
+	ops.oobbuf = oob;
+	ops.len = len;
+
+	return mtd->write_oob(mtd, offs, &ops);
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+ *
+*/
+static void read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
+			 struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Read the primary version, if available */
+	if (td->options & NAND_BBT_VERSION) 
+    {
+		scan_read_raw(mtd, buf, (loff_t)td->pages[0] << this->page_shift, mtd->writesize);
+		td->version[0] = buf[mtd->writesize + td->veroffs];
+        #if NAND_BAD_DEBUG
+		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+		       td->pages[0], td->version[0]); 
+        #endif
+	}
+
+	/* Read the mirror version, if available */
+	if (md && (md->options & NAND_BBT_VERSION)) 
+    {
+		scan_read_raw(mtd, buf, (loff_t)md->pages[0] <<	this->page_shift, mtd->writesize);
+		md->version[0] = buf[mtd->writesize + md->veroffs];
+        #if NAND_BAD_DEBUG
+		printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
+		       md->pages[0], md->version[0]);
+        #endif
+	}
+}
+
+/*
+ * Scan a given block full
+ */
+static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+			   loff_t offs, uint8_t *buf, size_t readlen,
+			   int scanlen, int len)
+{
+	int ret, j;
+
+	ret = scan_read_raw(mtd, buf, offs, readlen);
+	if (ret)
+		return ret;
+
+	for (j = 0; j < len; j++, buf += scanlen) 
+    {
+		if (check_pattern(buf, scanlen, mtd->writesize, bd))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Scan a given block partially
+ */
+static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
+			   loff_t offs, uint8_t *buf, int len)
+{
+	struct mtd_oob_ops ops;
+	int j, ret;
+
+	ops.ooblen = mtd->oobsize;
+	ops.oobbuf = buf;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OOB_PLACE;
+
+	for (j = 0; j < len; j++) 
+    {
+		/*
+		 * Read the full oob until read_oob is fixed to
+		 * handle single byte reads for 16 bit
+		 * buswidth
+		 */
+		ret = mtd->read_oob(mtd, offs, &ops);
+		if (ret)
+			return ret;
+
+		if (check_short_pattern(buf, bd))
+			return 1;
+
+		offs += mtd->writesize;
+	}
+	return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ * @chip:	create the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
+	struct nand_bbt_descr *bd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, numblocks, len, scanlen;
+	int startblock;
+	loff_t from;
+	size_t readlen;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL0, "Scanning device for bad blocks\n");
+
+	if (bd->options & NAND_BBT_SCANALLPAGES)
+		len = 1 << (this->bbt_erase_shift - this->page_shift);
+	else 
+    {
+		if (bd->options & NAND_BBT_SCAN2NDPAGE)
+			len = 2;
+		else
+			len = 1;
+	}
+
+	if (!(bd->options & NAND_BBT_SCANEMPTY)) 
+    {
+		/* We need only read few bytes from the OOB area */
+		scanlen = 0;
+		readlen = bd->len;
+	} 
+    else 
+    {
+		/* Full page content should be read */
+		scanlen = mtd->writesize + mtd->oobsize;
+		readlen = len * mtd->writesize;
+	}
+
+	if (chip == -1) 
+    {
+		/* Note that numblocks is 2 * (real numblocks) here, see i+=2
+		 * below as it makes shifting and masking less painful */
+		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+		startblock = 0;
+		from = 0;
+	} 
+    else 
+    {
+		if (chip >= this->numchips) 
+        {
+			printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+			       chip + 1, this->numchips);
+			return -EINVAL;
+		}
+		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+		startblock = chip * numblocks;
+		numblocks += startblock;
+		from = (loff_t)startblock << (this->bbt_erase_shift - 1);
+	}
+
+	for (i = startblock; i < numblocks;) 
+    {
+		int ret;
+
+		if (bd->options & NAND_BBT_SCANALLPAGES)
+			ret = scan_block_full(mtd, bd, from, buf, readlen,
+					      scanlen, len);
+		else
+			ret = scan_block_fast(mtd, bd, from, buf, len);
+
+		if (ret < 0)
+			return ret;
+
+		if (ret) 
+        {
+			this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+			MTDDEBUG (MTD_DEBUG_LEVEL0,
+				  "Bad eraseblock %d at 0x%012llx\n",
+				  i >> 1, (unsigned long long)from);
+			mtd->ecc_stats.badblocks++;
+		}
+
+		i += 2;
+		from += (1 << this->bbt_erase_shift);
+	}
+	return 0;
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern.
+ * Search is preformed either from the beginning up or from the end of
+ * the device downwards. The search starts always at the start of a
+ * block.
+ * If the option NAND_BBT_PERCHIP is given, each chip is searched
+ * for a bbt, which contains the bad block information of this chip.
+ * This is necessary to provide support for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page
+ * in a block.
+ */
+static int search_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, chips;
+	int bits, startblock, block, dir;
+	int scanlen = mtd->writesize + mtd->oobsize;
+	int bbtblocks;
+	int blocktopage = this->bbt_erase_shift - this->page_shift;
+
+	/* Search direction top -> down ? */
+	if (td->options & NAND_BBT_LASTBLOCK) 
+    {
+		startblock = (mtd->size >> this->bbt_erase_shift) - 1;
+		dir = -1;
+	} 
+    else 
+    {
+		startblock = 0;
+		dir = 1;
+	}
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+    {
+		chips = this->numchips;
+		bbtblocks = this->chipsize >> this->bbt_erase_shift;
+		startblock &= bbtblocks - 1;
+	} 
+    else 
+    {
+		chips = 1;
+		bbtblocks = mtd->size >> this->bbt_erase_shift;
+	}
+
+	/* Number of bits for each erase block in the bbt */
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+
+	for (i = 0; i < chips; i++) 
+    {
+		/* Reset version information */
+		td->version[i] = 0;
+		td->pages[i] = -1;
+		/* Scan the maximum number of blocks */
+		for (block = 0; block < td->maxblocks; block++) 
+        {
+
+			int actblock = startblock + dir * block;
+			loff_t offs = (loff_t)actblock << this->bbt_erase_shift;
+
+			/* Read first page */
+			scan_read_raw(mtd, buf, offs, mtd->writesize);
+			if (!check_pattern(buf, scanlen, mtd->writesize, td)) 
+            {
+				td->pages[i] = actblock << blocktopage;
+				if (td->options & NAND_BBT_VERSION) 
+                {
+					td->version[i] = buf[mtd->writesize + td->veroffs];
+				}
+				break;
+			}
+		}
+		startblock += this->chipsize >> this->bbt_erase_shift;
+	}
+	/* Check, if we found a bbt for each requested chip */
+	for (i = 0; i < chips; i++) 
+    {
+        #if NAND_BAD_DEBUG
+		if (td->pages[i] == -1)
+			printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
+		else
+			printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
+			       td->version[i]);
+        #endif
+	}
+	return 0;
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s)
+*/
+static void search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+	/* Search the primary table */
+	search_bbt(mtd, buf, td);
+
+	/* Search the mirror table */
+	if (md)
+		search_bbt(mtd, buf, md);
+}
+
+/**
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ *
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ * @md:		descriptor for the bad block table mirror
+ * @chipsel:	selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table
+ *
+*/
+static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
+		     struct nand_bbt_descr *td, struct nand_bbt_descr *md,
+		     int chipsel)
+{
+	struct nand_chip *this = mtd->priv;
+	struct erase_info einfo;
+	int i, j, res, chip = 0;
+	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+	int nrchips, bbtoffs, pageoffs, ooboffs;
+	uint8_t msk[4];
+	uint8_t rcode = td->reserved_block_code;
+	size_t retlen, len = 0;
+	loff_t to;
+	struct mtd_oob_ops ops;
+
+	ops.ooblen = mtd->oobsize;
+	ops.ooboffs = 0;
+	ops.datbuf = NULL;
+	ops.mode = MTD_OOB_PLACE;
+
+	if (!rcode)
+		rcode = 0xff;
+	/* Write bad block table per chip rather than per device ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+    {
+		numblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+		/* Full device write or specific chip ? */
+		if (chipsel == -1) {
+			nrchips = this->numchips;
+		} else {
+			nrchips = chipsel + 1;
+			chip = chipsel;
+		}
+	} 
+    else 
+    {
+		numblocks = (int)(mtd->size >> this->bbt_erase_shift);
+		nrchips = 1;
+	}
+
+	/* Loop through the chips */
+	for (; chip < nrchips; chip++) 
+    {
+
+		/* There was already a version of the table, reuse the page
+		 * This applies for absolute placement too, as we have the
+		 * page nr. in td->pages.
+		 */
+		if (td->pages[chip] != -1) 
+        {
+			page = td->pages[chip];
+			goto write;
+		}
+
+		/* Automatic placement of the bad block table */
+		/* Search direction top -> down ? */
+		if (td->options & NAND_BBT_LASTBLOCK) 
+        {
+			startblock = numblocks * (chip + 1) - 1;
+			dir = -1;
+		}
+        else 
+        {
+			startblock = chip * numblocks;
+			dir = 1;
+		}
+
+		for (i = 0; i < td->maxblocks; i++) 
+        {
+			int block = startblock + dir * i;
+			/* Check, if the block is bad */
+			switch ((this->bbt[block >> 2] >>
+				 (2 * (block & 0x03))) & 0x03) 
+		    {
+    			case 0x01:
+    			case 0x03:
+    				continue;
+			}
+			page = block <<
+				(this->bbt_erase_shift - this->page_shift);
+			/* Check, if the block is used by the mirror table */
+			if (!md || md->pages[chip] != page)
+				goto write;
+		}
+		printk(KERN_ERR "No space left to write bad block table\n");
+		return -ENOSPC;
+	write:
+
+		/* Set up shift count and masks for the flash table */
+		bits = td->options & NAND_BBT_NRBITS_MSK;
+		msk[2] = ~rcode;
+		switch (bits) 
+        {
+    		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
+    			msk[3] = 0x01;
+    			break;
+    		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
+    			msk[3] = 0x03;
+    			break;
+    		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
+    			msk[3] = 0x0f;
+    			break;
+    		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
+    			msk[3] = 0xff;
+    			break;
+    		default: return -EINVAL;
+		}
+
+		bbtoffs = chip * (numblocks >> 2);
+
+		to = ((loff_t) page) << this->page_shift;
+
+		/* Must we save the block contents ? */
+		if (td->options & NAND_BBT_SAVECONTENT) 
+        {
+			/* Make it block aligned */
+			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+			len = 1 << this->bbt_erase_shift;
+			res = mtd->read(mtd, to, len, &retlen, buf);
+			if (res < 0) 
+            {
+				if (retlen != len) 
+                {
+					printk(KERN_INFO "nand_bbt: Error "
+					       "reading block for writing "
+					       "the bad block table\n");
+					return res;
+				}
+				printk(KERN_WARNING "nand_bbt: ECC error "
+				       "while reading block for writing "
+				       "bad block table\n");
+			}
+			/* Read oob data */
+			ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
+			ops.oobbuf = &buf[len];
+			res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
+			if (res < 0 || ops.oobretlen != ops.ooblen)
+				goto outerr;
+
+			/* Calc the byte offset in the buffer */
+			pageoffs = page - (int)(to >> this->page_shift);
+			offs = pageoffs << this->page_shift;
+			/* Preset the bbt area with 0xff */
+			memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
+			ooboffs = len + (pageoffs * mtd->oobsize);
+
+		} 
+        else 
+        {
+		
+			/* Calc length */
+			len = (size_t) (numblocks >> sft);
+			/* Make it page aligned ! */
+			len = (len + (mtd->writesize - 1)) &
+				~(mtd->writesize - 1);
+			/* Preset the buffer with 0xff */
+			memset(buf, 0xff, len +
+			       (len >> this->page_shift)* mtd->oobsize);
+			offs = 0;
+			ooboffs = len;
+			/* Pattern is located in oob area of first page */
+			memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
+		}
+
+		if (td->options & NAND_BBT_VERSION)
+			buf[ooboffs + td->veroffs] = td->version[chip];
+
+		/* walk through the memory table */
+		for (i = 0; i < numblocks;) 
+        {
+			uint8_t dat;
+			dat = this->bbt[bbtoffs + (i >> 2)];
+			for (j = 0; j < 4; j++, i++) 
+            {
+				int sftcnt = (i << (3 - sft)) & sftmsk;
+				/* Do not store the reserved bbt blocks ! */
+				buf[offs + (i >> sft)] &=
+					~(msk[dat & 0x03] << sftcnt);
+				dat >>= 2;
+			}
+		}
+
+		memset(&einfo, 0, sizeof(einfo));
+		einfo.mtd = mtd;
+		einfo.addr = to;
+		einfo.len = 1 << this->bbt_erase_shift;
+		res = nand_erase_nand(mtd, &einfo, 1);
+		if (res < 0)
+			goto outerr;
+
+		res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
+		if (res < 0)
+			goto outerr;
+
+        #if NAND_BAD_DEBUG
+		printk(KERN_DEBUG "Bad block table written to 0x%012llx, "
+		       "version 0x%02X\n", (unsigned long long)to,
+		       td->version[chip]);
+        #endif
+		/* Mark it as used */
+		td->pages[chip] = page;
+	}
+	return 0;
+
+ outerr:
+	printk(KERN_WARNING
+	       "nand_bbt: Error while writing bad block table %d\n", res);
+	return res;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device
+ * for manufacturer / software marked good / bad blocks
+*/
+static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	bd->options &= ~NAND_BBT_SCANEMPTY;
+	return create_bbt(mtd, this->buffers->databuf, bd, -1);
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if necessary
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt
+ * and creates / updates the bbt(s) if necessary
+ * Creation is necessary if no bbt was found for the chip/device
+ * Update is necessary if one of the tables is missing or the
+ * version nr. of one table is less than the other
+*/
+static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+	int i, chips, writeops, chipsel, res;
+	struct nand_chip *this = mtd->priv;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+	struct nand_bbt_descr *rd, *rd2;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP)
+		chips = this->numchips;
+	else
+		chips = 1;
+
+	for (i = 0; i < chips; i++) 
+    {
+		writeops = 0;
+		rd = NULL;
+		rd2 = NULL;
+		/* Per chip or per device ? */
+		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+		/* Mirrored table avilable ? */
+		if (md) 
+        {
+			if (td->pages[i] == -1 && md->pages[i] == -1) 
+            {
+				writeops = 0x03;
+				goto create;
+			}
+
+			if (td->pages[i] == -1) 
+            {
+				rd = md;
+				td->version[i] = md->version[i];
+				writeops = 1;
+				goto writecheck;
+			}
+
+			if (md->pages[i] == -1) 
+            {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+				goto writecheck;
+			}
+
+			if (td->version[i] == md->version[i]) 
+            {
+				rd = td;
+				if (!(td->options & NAND_BBT_VERSION))
+					rd2 = md;
+				goto writecheck;
+			}
+
+			if (((int8_t) (td->version[i] - md->version[i])) > 0) 
+            {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+			} 
+            else 
+            {
+				rd = md;
+				td->version[i] = md->version[i];
+				writeops = 1;
+			}
+
+			goto writecheck;
+
+		} 
+        else 
+        {
+		
+			if (td->pages[i] == -1) 
+            {
+				writeops = 0x01;
+				goto create;
+			}
+			rd = td;
+			goto writecheck;
+		}
+	create:
+		/* Create the bad block table by scanning the device ? */
+		if (!(td->options & NAND_BBT_CREATE))
+			continue;
+
+		/* Create the table in memory by scanning the chip(s) */
+		create_bbt(mtd, buf, bd, chipsel);
+
+		td->version[i] = 1;
+		if (md)
+			md->version[i] = 1;
+	writecheck:
+		/* read back first ? */
+		if (rd)
+			read_abs_bbt(mtd, buf, rd, chipsel);
+		/* If they weren't versioned, read both. */
+		if (rd2)
+			read_abs_bbt(mtd, buf, rd2, chipsel);
+
+		/* Write the bad block table to the device ? */
+		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) 
+        {
+			res = write_bbt(mtd, buf, td, md, chipsel);
+			if (res < 0)
+				return res;
+		}
+
+		/* Write the mirror bad block table to the device ? */
+		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) 
+        {
+			res = write_bbt(mtd, buf, md, td, chipsel);
+			if (res < 0)
+				return res;
+		}
+	}
+	return 0;
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions
+ * @mtd:	MTD device structure
+ * @td:		bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent
+ * accidental erasures / writes. The regions are identified by
+ * the mark 0x02.
+*/
+static void mark_bbt_region(struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, j, chips, block, nrblocks, update;
+	uint8_t oldval, newval;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+    {
+		chips = this->numchips;
+		nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+	} 
+    else 
+    {
+		chips = 1;
+		nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+	}
+
+	for (i = 0; i < chips; i++) 
+    {
+		if ((td->options & NAND_BBT_ABSPAGE) ||
+		    !(td->options & NAND_BBT_WRITE)) 
+		{
+			if (td->pages[i] == -1)
+				continue;
+			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+			block <<= 1;
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if ((oldval != newval) && td->reserved_block_code)
+				nand_update_bbt(mtd, (loff_t)block << (this->bbt_erase_shift - 1));
+			continue;
+		}
+		update = 0;
+		if (td->options & NAND_BBT_LASTBLOCK)
+			block = ((i + 1) * nrblocks) - td->maxblocks;
+		else
+			block = i * nrblocks;
+		block <<= 1;
+		for (j = 0; j < td->maxblocks; j++) 
+        {
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if (oldval != newval)
+				update = 1;
+			block += 2;
+		}
+		/* If we want reserved blocks to be recorded to flash, and some
+		   new ones have been marked, then we need to update the stored
+		   bbts.  This should only happen once. */
+		if (update && td->reserved_block_code)
+			nand_update_bbt(mtd, (loff_t)(block - 2) <<	(this->bbt_erase_shift - 1));
+	}
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed
+ * by calling the nand_free_bbt function.
+ *
+*/
+int nand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	len = mtd->size >> (this->bbt_erase_shift + 2);
+	/* Allocate memory (2bit per block) and clear the memory bad block table */
+	this->bbt = kzalloc(len, GFP_KERNEL);
+	if (!this->bbt) 
+    {
+		printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+
+	/* If no primary table decriptor is given, scan the device
+	 * to build a memory based bad block table
+	 */
+	if (!td) 
+    {
+		if ((res = nand_memory_bbt(mtd, bd))) 
+        {
+			printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
+			kfree(this->bbt);
+			this->bbt = NULL;
+		}
+		return res;
+	}
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = vmalloc(len);
+	if (!buf) 
+    {
+		printk(KERN_ERR "nand_bbt: Out of memory\n");
+		kfree(this->bbt);
+		this->bbt = NULL;
+		return -ENOMEM;
+	}
+
+	/* Is the bbt at a given page ? */
+	if (td->options & NAND_BBT_ABSPAGE) 
+    {
+		read_abs_bbts(mtd, buf, td, md);
+	} 
+    else
+    {
+		/* Search the bad block table using a pattern in oob */
+		search_read_bbts(mtd, buf, td, md);
+	}
+
+	res = check_create(mtd, buf, bd);
+
+	/* Prevent the bbt regions from erasing / writing */
+	mark_bbt_region(mtd, td);
+	if (md)
+		mark_bbt_region(mtd, md);
+
+	vfree(buf);
+	return res;
+}
+
+/**
+ * nand_update_bbt - [NAND Interface] update bad block table(s)
+ * @mtd:	MTD device structure
+ * @offs:	the offset of the newly marked block
+ *
+ * The function updates the bad block table(s)
+*/
+int nand_update_bbt(struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0;
+	int chip, chipsel;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	if (!this->bbt || !td)
+		return -EINVAL;
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) 
+    {
+		printk(KERN_ERR "nand_update_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+    {
+		chip = (int)(offs >> this->chip_shift);
+		chipsel = chip;
+	} 
+    else 
+    {
+		chip = 0;
+		chipsel = -1;
+	}
+
+	td->version[chip]++;
+	if (md)
+		md->version[chip]++;
+
+	/* Write the bad block table to the device ? */
+	if (td->options & NAND_BBT_WRITE) 
+    {
+		res = write_bbt(mtd, buf, td, md, chipsel);
+		if (res < 0)
+			goto out;
+	}
+	/* Write the mirror bad block table to the device ? */
+	if (md && (md->options & NAND_BBT_WRITE)) 
+    {
+		res = write_bbt(mtd, buf, md, td, chipsel);
+	}
+
+ out:
+	kfree(buf);
+	return res;
+}
+
+/* Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks. */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+	.options = NAND_BBT_SCAN2NDPAGE,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+	.options = 0,
+	.offs = 0,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr smallpage_flashbased = {
+	.options = NAND_BBT_SCAN2NDPAGE,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_flashbased = {
+	.options = NAND_BBT_SCAN2NDPAGE,
+	.offs = 0,
+	.len = 1,   /* ÔÚʹÓÃZFTLдµÚһҳʱ£¬»á²úÉú 0xff 0x00 µÄÏÖÏó  so 2->1  zhouqi */
+	.pattern = scan_ff_pattern
+};
+
+static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
+
+static struct nand_bbt_descr agand_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 0x20,
+	.len = 6,
+	.pattern = scan_agand_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
+ * @mtd:	MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the nand_scan_bbt function
+ *
+*/
+int nand_default_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Default for AG-AND. We must use a flash based
+	 * bad block table as the devices have factory marked
+	 * _good_ blocks. Erasing those blocks leads to loss
+	 * of the good / bad information, so we _must_ store
+	 * this information in a good / bad table during
+	 * startup
+	 */
+	if (this->options & NAND_IS_AND) 
+    {
+		/* Use the default pattern descriptors */
+		if (!this->bbt_td) 
+        {
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}
+		this->options |= NAND_USE_FLASH_BBT;
+		return nand_scan_bbt(mtd, &agand_flashbased);
+	}
+
+	/* Is a flash based bad block table requested ? */
+	if (this->options & NAND_USE_FLASH_BBT) 
+    {
+		/* Use the default pattern descriptors */
+		if (!this->bbt_td) 
+        {
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}
+		if (!this->badblock_pattern) 
+        {
+			this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
+		}
+	} 
+    else 
+    {
+		this->bbt_td = NULL;
+		this->bbt_md = NULL;
+		if (!this->badblock_pattern) 
+        {
+			this->badblock_pattern = (mtd->writesize > 512) ?
+			    &largepage_memorybased : &smallpage_memorybased;
+		}
+	}
+	return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad
+ * @mtd:	MTD device structure
+ * @offs:	offset in the device
+ * @allowbbt:	allow access to bad block table region
+ *
+*/
+int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+	struct nand_chip *this = mtd->priv;
+	int block;
+	uint8_t res;
+
+	/* Get block number * 2 */
+	block = (int)(offs >> (this->bbt_erase_shift - 1));
+	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+	MTDDEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: "
+	          "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);
+
+	switch ((int)res) {
+	case 0x00:
+		return 0;
+	case 0x01:
+		return 1;
+	case 0x02:
+		return allowbbt ? 0 : 1;
+	}
+	return 1;
+}
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand_ecc.c b/boot/common/src/uboot/drivers/mtd/nand/nand_ecc.c
new file mode 100644
index 0000000..52bc916
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_ecc.c
@@ -0,0 +1,202 @@
+/*
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
+ *
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ *                         Toshiba America Electronics Components, Inc.
+ *
+ * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
+ *
+ * This file is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ *
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
+ */
+
+#include <common.h>
+
+#include <asm/errno.h>
+#include <linux/mtd/mtd.h>
+
+/* The PPC4xx NDFC uses Smart Media (SMC) bytes order */
+#ifdef CONFIG_NAND_NDFC
+#define CONFIG_MTD_NAND_ECC_SMC
+#endif
+
+/*
+ * NAND-SPL has no sofware ECC for now, so don't include nand_calculate_ecc(),
+ * only nand_correct_data() is needed
+ */
+
+#ifndef CONFIG_NAND_SPL
+/*
+ * Pre-calculated 256-way 1 byte column parity
+ */
+static const u_char nand_ecc_precalc_table[] = {
+	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
+	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+	0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
+	0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
+	0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
+	0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
+	0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
+	0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
+	0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
+	0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
+};
+
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3-byte ECC for 256-byte block
+ * @mtd:	MTD block structure
+ * @dat:	raw data
+ * @ecc_code:	buffer for ECC
+ */
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+		       u_char *ecc_code)
+{
+	uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
+	int i;
+
+	/* Initialize variables */
+	reg1 = reg2 = reg3 = 0;
+
+	/* Build up column parity */
+	for(i = 0; i < 256; i++) {
+		/* Get CP0 - CP5 from table */
+		idx = nand_ecc_precalc_table[*dat++];
+		reg1 ^= (idx & 0x3f);
+
+		/* All bit XOR = 1 ? */
+		if (idx & 0x40) {
+			reg3 ^= (uint8_t) i;
+			reg2 ^= ~((uint8_t) i);
+		}
+	}
+
+	/* Create non-inverted ECC code from line parity */
+	tmp1  = (reg3 & 0x80) >> 0; /* B7 -> B7 */
+	tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
+	tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
+	tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
+	tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
+	tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
+	tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
+	tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
+
+	tmp2  = (reg3 & 0x08) << 4; /* B3 -> B7 */
+	tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
+	tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
+	tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
+	tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
+	tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
+	tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
+	tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
+
+	/* Calculate final ECC code */
+#ifdef CONFIG_MTD_NAND_ECC_SMC
+	ecc_code[0] = ~tmp2;
+	ecc_code[1] = ~tmp1;
+#else
+	ecc_code[0] = ~tmp1;
+	ecc_code[1] = ~tmp2;
+#endif
+	ecc_code[2] = ((~reg1) << 2) | 0x03;
+
+	return 0;
+}
+#endif /* CONFIG_NAND_SPL */
+
+static inline int countbits(uint32_t byte)
+{
+	int res = 0;
+
+	for (;byte; byte >>= 1)
+		res += byte & 0x01;
+	return res;
+}
+
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd:	MTD block structure
+ * @dat:	raw data read from the chip
+ * @read_ecc:	ECC from the chip
+ * @calc_ecc:	the ECC calculated from raw data
+ *
+ * Detect and correct a 1 bit error for 256 byte block
+ */
+int nand_correct_data(struct mtd_info *mtd, u_char *dat,
+		      u_char *read_ecc, u_char *calc_ecc)
+{
+	uint8_t s0, s1, s2;
+
+#ifdef CONFIG_MTD_NAND_ECC_SMC
+	s0 = calc_ecc[0] ^ read_ecc[0];
+	s1 = calc_ecc[1] ^ read_ecc[1];
+	s2 = calc_ecc[2] ^ read_ecc[2];
+#else
+	s1 = calc_ecc[0] ^ read_ecc[0];
+	s0 = calc_ecc[1] ^ read_ecc[1];
+	s2 = calc_ecc[2] ^ read_ecc[2];
+#endif
+	if ((s0 | s1 | s2) == 0)
+		return 0;
+
+	/* Check for a single bit error */
+	if( ((s0 ^ (s0 >> 1)) & 0x55) == 0x55 &&
+	    ((s1 ^ (s1 >> 1)) & 0x55) == 0x55 &&
+	    ((s2 ^ (s2 >> 1)) & 0x54) == 0x54) {
+
+		uint32_t byteoffs, bitnum;
+
+		byteoffs = (s1 << 0) & 0x80;
+		byteoffs |= (s1 << 1) & 0x40;
+		byteoffs |= (s1 << 2) & 0x20;
+		byteoffs |= (s1 << 3) & 0x10;
+
+		byteoffs |= (s0 >> 4) & 0x08;
+		byteoffs |= (s0 >> 3) & 0x04;
+		byteoffs |= (s0 >> 2) & 0x02;
+		byteoffs |= (s0 >> 1) & 0x01;
+
+		bitnum = (s2 >> 5) & 0x04;
+		bitnum |= (s2 >> 4) & 0x02;
+		bitnum |= (s2 >> 3) & 0x01;
+
+		dat[byteoffs] ^= (1 << bitnum);
+
+		return 1;
+	}
+
+	if(countbits(s0 | ((uint32_t)s1 << 8) | ((uint32_t)s2 <<16)) == 1)
+		return 1;
+
+	return -EBADMSG;
+}
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand_ids.c b/boot/common/src/uboot/drivers/mtd/nand/nand_ids.c
new file mode 100755
index 0000000..3a18ff7
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_ids.c
@@ -0,0 +1,158 @@
+/*
+ *  drivers/mtd/nandids.c
+ *
+ *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <common.h> 
+#include <linux/mtd/nand.h>
+#include <asm/arch/nand.h> 
+
+const struct nand_flash_device_para nand_flash_para[] = {
+	/* MT29F4G08ABBDAH4 for 7520V5 evb 512MB X 8 */
+	{0x2C,0xAC,0x90,0,2048,64,2,3,0x20000,64,4096,1,0,{60,25,100,25,100,20,20,15,25, 60},{4, 512}},
+	/* H9TA4GG2GDMCPR for 7520 evb 512MB X 8 */
+	{0xAD,0xAC,0x90,0,2048,128,2,3,0x20000,64,4096,1,0,{60,25,100,25,100,20,25,15,35,80},{8,512}},
+	/* K9F2G08U0B for 7520 FPGA */
+	{0xEC,0xDA,0x10,0,2048,64,2,3,0x20000,64,2048,1,0,{60,25,100,25,100,20,20,10,20,100},{4,512}},
+	/* HY27UF082G2B for 7520 FPGA */
+	{0xAD,0xDA,0x10,0,2048,64,2,3,0x20000,64,2048,1,0,{60,25,100,25,100,20,20,10,20,100},{4,512}},
+	/* MT29F2G08ABBEA for 7520 evb 256MB X 8 */
+	{0x2C,0xAA,0x90,0,2048,64,2,3,0x20000,64,2048,1,0,{60,25,100,25,100,20,20,15,25, 60},{4,512}},
+	/* JSFBA3YHABB for 7510 evb 256MB X 8 */
+	{0xAD,0xAA,0x90,0,2048,128,2,3,0x20000,64,2048,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{8,512}},
+	/* FM6BD2G1GA for 7510 evb 256MB X 8 */
+	{0xC8,0xAA,0x90,0,2048,64,2,3,0x20000,64,2048,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{4,512}},
+	/* JSFCBX3Y7ABB for 7510 evb 512MB X 8 */
+	{0x01,0xAC,0x90,0,2048,128,2,3,0x20000,64,4096,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{8,512}},
+	/* NM1281KSLAXAJ for 7510 evb 256MB X 8 */	
+	{0x98,0xAA,0x90,0,2048,128,2,3,0x20000,64,2048,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 60},{8,512}},
+	/* H9TA4GG2GDMCPR for 7520 evb 512MB X 8 */
+	{0xAD,0xAC,0x90,0,2048,128,2,3,0x20000,64,4096,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{8,512}},
+	/* JSFCBX3Y7ABB for 7510 evb 512MB X 8 */
+	{0x01,0xAC,0x90,0,2048,128,2,3,0x20000,64,4096,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{8,512}},
+	/* FM6BD4G2GA for 7510 evb 512MB X 8 */
+	{0xC8,0xAC,0x90,0,2048,64,2,3,0x20000,64,4096,1,0,{60, 25, 100, 25, 100, 20, 25, 15, 35, 80},{4,512}},
+	/* W71NW20GF3FW for 7510 evb 256MB X 8 */
+	{0xEF,0xAA,0x90,0,2048,64,2,3,0x20000,64,2048,1,0,{80, 25, 100, 25, 100, 20, 25, 15, 35, 80},{4,512}},
+
+
+
+	/* GIGADEVICE GD5F1GQ4R for 128MB 1.8V SPI-NAND */
+	{0xC8,0xC1,0x00,0,2048,128,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* GIGADEVICE GD5F1GQ5RExxG for 128MB 1.8V SPI-NAND */
+	{0xC8,0x41,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* GIGADEVICE GD2G4RBY1G for 7520 FPGA 256MB SPI-NAND */
+	{0xC8,0xC2,0x00,0,2048,128,2,3,0x20000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* GIGADEVICE GD5F4GQ4R for 7520 evb 512MB SPI-NAND */
+	{0xC8,0xC4,0x00,0,4096,256,0,0,0x40000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+
+	/* PARAGON PN26Q01AWSIUG for 7520 evb 128MB SPI-NAND */
+	{0xA1,0xC1,0x00,0,2048,128,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* PARAGON PN26Q02AWSIUG for 7520 evb 256MB SPI-NAND */
+	{0xA1,0xC2,0x00,0,2048,128,0,0,0x20000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	 /* HYF1GQ4IAACAE SPI-NAND  */	
+	{0xC9,0x51,0x00,0,2048,128,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	 /* HYF2GQ4IAACAE SPI-NAND  */	
+	{0xC9,0x52,0x00,0,2048,128,0,0,0x20000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/*winbond W25N01G*/
+	{0xEF,0xBA,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/*winbond W25N02G*/
+	{0xEF,0xBB,0x00,0,2048,64,0,0,0x20000,64,2048,2,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* TOSHIBA TC58CYG0S3HRAIG 128MB SPI-NAND*/
+	{0x98,0xB2,0x00,0,2048,128,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* TOSHIBA TC58CYG1S3HRAIG 256MB SPI-NAND*/
+	{0x98,0xBB,0x00,0,2048,128,0,0,0x20000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* ZETTA ZD35X1GAXXX 128MB SPI-NAND*/
+	{0xBA,0x21,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* DOSILICON DS35X1GAXXX 128MB SPI-NAND*/
+	{0xE5,0x21,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* DOSILICON DS35X12BXXX 64MB SPI-NAND*/
+	{0xE5,0xA5,0x00,0,2048,128,0,0,0x20000,64,512,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* FUDANWEI FM25LS01 128MB SPI-NAND*/
+	{0xA1,0xA5,0x00,0,2048,128,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* hosin HG-SPIXGb-1XAIA  128MB SPI-NAND*/
+	{0xD6,0x21,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* EMST F50D1G41LB (2M)  128MB SPI-NAND*/
+	{0xC8,0x11,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* FORESEE F35UQA001G  128MB SPI-NAND*/
+	{0xCD,0x61,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* FORESEE F35UQA512M 64MB SPI-NAND*/
+	{0xCD,0x60,0x00,0,2048,64,0,0,0x20000,64,512,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* micron-MT29F2G01ABAGDWB-ITG 256MB SPI-NAND*/
+	{0x2C,0x25,0x00,0,2048,128,0,0,0x20000,64,2048,1,2,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+	/* ESMT F50D44G41XB (2X)  512MB SPI-NAND*/
+	{0x2C,0x35,0x00,0,4096,256,0,0,0x40000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{8,512}},
+	{0,}
+}; 
+/*
+*	Chip ID list
+*
+*	Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
+*	options
+*
+*	Pagesize; 0, 256, 512
+*	0	get this information from the extended chip ID
++	256	256 Byte page size
+*	512	512 Byte page size
+*/
+const struct nand_flash_dev nand_flash_ids[] = {
+
+	{"NAND 512MiB 1,8V 8-bit",	0xAC, 2048, 512, 0x20000, 0},  //7520 EVB H9TA4GG2GDMCPR
+	{"SPI-NAND 128MiB 1,8V",	0xE1, 2048, 128, 0x20000, 0},  //7520m GD5F1G
+	{"SPI-NAND 256MiB 1,8V",	0xE2, 2048, 256, 0x20000, 0},  //7520m GD5F2G
+	{"SPI-NAND 512MiB 1,8V",	0xE4, 2048, 512, 0x20000, 0},  //7520m GD5F4G
+	{"SPI-NAND 256MiB 1,8V",	0xF2, 2048, 256, 0x20000, 0},  //7520 FPGA GD5F2G 
+	{"SPI-NAND 256MiB 1,8V",	0xF1, 2048, 256, 0x20000, 0},  //7520 FPGA GD5F2G 
+	{"SPI-NAND 128MiB 1,8V",	0xC1, 2048, 128, 0x20000, 0},   //7520 EVB GD5F1G
+	{"SPI-NAND 128MiB 1,8V",	0x41, 2048, 128, 0x20000, 0},	//GD5F1GQ5RExxG	
+	{"SPI-NAND 256MiB 1,8V",	0xC2, 2048, 256, 0x20000, 0},  //7520 7520m GD5F2G 
+	{"SPI-NAND 128MiB 1,8V",	0x51, 2048, 128, 0x20000, 0},   
+	{"SPI-NAND 256MiB 1,8V",	0x52, 2048, 256, 0x20000, 0},  
+	{"SPI-NAND 512MiB 1,8V",	0xC4, 4096, 512, 0x40000, 0},   //7520 EVB GD5F1G
+	{"NAND 256MiB 1,8V 8-bit",	0xDA, 2048, 256, 0x20000, 0},  //7520 fpga samsung                                                          //!!the last block is not valid
+	{"NAND 256MiB 1,8V 8-bit",	0xAA, 2048, 256, 0x20000, 0},  //7520 EVB MICRON
+	{"SPI-NAND 128MiB 1,8V",	0xBA, 2048, 128, 0x20000, 0},  //winbond W25N01G
+	{"SPI-NAND 256MiB 1,8V",	0xBB, 2048, 256, 0x20000, 0},  //winbond W25N02G
+	{"SPI-NAND 128MiB 1,8V",	0xB2, 2048, 128, 0x20000, 0}, 
+	{"SPI-NAND 128MiB 1,8V",	0x21, 2048, 128, 0x20000, 0},
+	{"SPI-NAND 128MiB 1,8V",	0xA5, 2048, 128, 0x20000, 0},
+	{"SPI-NAND 128MiB 1,8V",	0x11, 2048, 128, 0x20000, 0},	//EMST F50D1G41LB (2M)	
+	{"SPINAND 128MiB 1,8V",	0x61, 2048, 128, 0x20000, 0},
+	{"SPINAND 64MiB 1,8V",	0x60, 2048, 64, 0x20000, 0},
+	{"SPI-NAND 256MiB 1,8V",	0x25, 2048, 256, 0x20000, 0},	//MT29F2G01ABAGDWB
+	{"SPI-NAND 256MiB 1,8V",	0x35, 4096, 512, 0x40000, 0},	//F50D44G41XB (2X)
+	{NULL,}
+};
+
+/*
+*	Manufacturer ID list
+*/
+const struct nand_manufacturers nand_manuf_ids[] = {
+	{NAND_MFR_TOSHIBA, "Toshiba"},
+	{NAND_MFR_SAMSUNG, "Samsung"},
+	{NAND_MFR_FUJITSU, "Fujitsu"},
+	{NAND_MFR_NATIONAL, "National"},
+	{NAND_MFR_RENESAS, "Renesas"},
+	{NAND_MFR_STMICRO, "ST Micro"},
+	{NAND_MFR_HYNIX, "Hynix"},
+	{NAND_MFR_MICRON, "Micron"},
+	{NAND_MFR_AMD, "AMD"},
+	{NAND_MFR_GIGADEVICE, "GigaDevice"}, 
+	{NAND_MFR_WINBOND, "WinBond"}, 
+	{NAND_MFR_PARAGON, "Parogon"}, 
+	{NAND_MFR_HEYANGTEK, "HeYangTek"},
+	{NAND_MFR_ZETTA, "zetta"},
+	{NAND_MFR_DOSILICON, "dosilicon"},
+	{NAND_MFR_FUDANWEI, "FuDanWei"},
+	{NAND_MFR_HOSIN, "hosin"},
+	{NAND_MFR_EMST, "emst"},
+	{NAND_MFR_FORESEE, "foresee"},
+	{0x0, "Unknown"}
+};
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/nand_util.c b/boot/common/src/uboot/drivers/mtd/nand/nand_util.c
new file mode 100644
index 0000000..afa5a03
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_util.c
@@ -0,0 +1,847 @@
+/*
+ * drivers/mtd/nand/nand_util.c
+ *
+ * Copyright (C) 2006 by Weiss-Electronic GmbH.
+ * All rights reserved.
+ *
+ * @author:	Guido Classen <clagix@gmail.com>
+ * @descr:	NAND Flash support
+ * @references: borrowed heavily from Linux mtd-utils code:
+ *		flash_eraseall.c by Arcom Control System Ltd
+ *		nandwrite.c by Steven J. Hill (sjhill@realitydiluted.com)
+ *			       and Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Copyright (C) 2008 Nokia Corporation: drop_ffs() function by
+ * Artem Bityutskiy <dedekind1@gmail.com> from mtd-utils
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version
+ * 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * Copyright 2010 Freescale Semiconductor
+ * The portions of this file whose copyright is held by Freescale and which
+ * are not considered a derived work of GPL v2-only code may be distributed
+ * and/or modified under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ */
+
+#include <common.h>
+#include <command.h>
+#include <watchdog.h>
+#include <malloc.h>
+#include <div64.h>
+
+#include <asm/errno.h>
+#include <linux/mtd/mtd.h>
+#include <nand.h>
+#include <jffs2/jffs2.h>
+
+typedef struct erase_info erase_info_t;
+typedef struct mtd_info	  mtd_info_t;
+
+/* support only for native endian JFFS2 */
+#define cpu_to_je16(x) (x)
+#define cpu_to_je32(x) (x)
+
+/*****************************************************************************/
+static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	return 0;
+}
+
+/**
+ * nand_erase_opts: - erase NAND flash with support for various options
+ *		      (jffs2 formating)
+ *
+ * @param meminfo	NAND device to erase
+ * @param opts		options,  @see struct nand_erase_options
+ * @return		0 in case of success
+ *
+ * This code is ported from flash_eraseall.c from Linux mtd utils by
+ * Arcom Control System Ltd.
+ */
+int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
+{
+	struct jffs2_unknown_node cleanmarker;
+	erase_info_t erase;
+	unsigned long erase_length, erased_length; /* in blocks */
+	int result;
+	int percent_complete = -1;
+	int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
+	const char *mtd_device = meminfo->name;
+	struct mtd_oob_ops oob_opts;
+	struct nand_chip *chip = meminfo->priv;
+
+	if ((opts->offset & (meminfo->writesize - 1)) != 0) {
+		printf("Attempt to erase non page aligned data\n");
+		return -1;
+	}
+
+	memset(&erase, 0, sizeof(erase));
+	memset(&oob_opts, 0, sizeof(oob_opts));
+
+	erase.mtd = meminfo;
+	erase.len  = meminfo->erasesize;
+	erase.addr = opts->offset;
+	erase_length = lldiv(opts->length + meminfo->erasesize - 1,
+			     meminfo->erasesize);
+
+	cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
+	cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+	cleanmarker.totlen = cpu_to_je32(8);
+
+	/* scrub option allows to erase badblock. To prevent internal
+	 * check from erase() method, set block check method to dummy
+	 * and disable bad block table while erasing.
+	 */
+	if (opts->scrub) 
+    {
+		struct nand_chip *priv_nand = meminfo->priv;
+
+		nand_block_bad_old = priv_nand->block_bad;
+		priv_nand->block_bad = nand_block_bad_scrub;
+		/* we don't need the bad block table anymore...
+		 * after scrub, there are no bad blocks left!
+		 */
+		if (priv_nand->bbt) 
+        {
+			kfree(priv_nand->bbt);
+		}
+		priv_nand->bbt = NULL;
+	}
+
+	for (erased_length = 0;
+	     erased_length < erase_length;
+	     erase.addr += meminfo->erasesize) 
+    {
+
+		//WATCHDOG_RESET ();
+
+		if (!opts->scrub) 
+        {
+			int ret = meminfo->block_isbad(meminfo, erase.addr);
+			if (ret > 0) 
+            {
+				if (!opts->quiet)
+					printf("\rSkipping bad block at  "
+					       "0x%08llx                 "
+					       "                         \n",
+					       (u64)erase.addr);
+
+				if (!opts->spread)
+					erased_length++;
+
+				continue;
+
+			} 
+            else if (ret < 0) 
+            {
+				printf("\n%s: MTD get bad block failed: %d\n",
+				       mtd_device,
+				       ret);
+				return -1;
+			}
+		}
+
+		erased_length++;
+
+		result = meminfo->erase(meminfo, &erase);
+		if (result != 0) 
+        {
+			printf("\n%s: MTD Erase failure: %d\n",
+			       mtd_device, result);
+			continue;
+		}
+
+		/* format for JFFS2 ? */
+		if (opts->jffs2 && chip->ecc.layout->oobavail >= 8) 
+        {
+			chip->ops.ooblen = 8;
+			chip->ops.datbuf = NULL;
+			chip->ops.oobbuf = (uint8_t *)&cleanmarker;
+			chip->ops.ooboffs = 0;
+			chip->ops.mode = MTD_OOB_AUTO;
+
+			result = meminfo->write_oob(meminfo,
+			                            erase.addr,
+			                            &chip->ops);
+			if (result != 0) 
+            {
+				printf("\n%s: MTD writeoob failure: %d\n",
+				       mtd_device, result);
+				continue;
+			}
+		}
+
+		if (!opts->quiet) 
+        {
+			unsigned long long n = erased_length * 100ULL;
+			int percent;
+
+			do_div(n, erase_length);
+			percent = (int)n;
+
+			/* output progress message only at whole percent
+			 * steps to reduce the number of messages printed
+			 * on (slow) serial consoles
+			 */
+			if (percent != percent_complete) {
+				percent_complete = percent;
+
+				printf("\rErasing at 0x%llx -- %3d%% complete.",
+				       (u64)erase.addr, percent);
+
+				if (opts->jffs2 && result == 0)
+					printf(" Cleanmarker written at 0x%llx.",
+					       (u64)erase.addr);
+			}
+		}
+	}
+	if (!opts->quiet)
+		printf("\n");
+
+	if (nand_block_bad_old) 
+    {
+		struct nand_chip *priv_nand = meminfo->priv;
+
+		priv_nand->block_bad = nand_block_bad_old;
+		//priv_nand->scan_bbt(meminfo);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_CMD_NAND_LOCK_UNLOCK
+
+/******************************************************************************
+ * Support for locking / unlocking operations of some NAND devices
+ *****************************************************************************/
+
+#define NAND_CMD_LOCK		0x2a
+#define NAND_CMD_LOCK_TIGHT	0x2c
+#define NAND_CMD_UNLOCK1	0x23
+#define NAND_CMD_UNLOCK2	0x24
+#define NAND_CMD_LOCK_STATUS	0x7a
+
+/**
+ * nand_lock: Set all pages of NAND flash chip to the LOCK or LOCK-TIGHT
+ *	      state
+ *
+ * @param mtd		nand mtd instance
+ * @param tight		bring device in lock tight mode
+ *
+ * @return		0 on success, -1 in case of error
+ *
+ * The lock / lock-tight command only applies to the whole chip. To get some
+ * parts of the chip lock and others unlocked use the following sequence:
+ *
+ * - Lock all pages of the chip using nand_lock(mtd, 0) (or the lockpre pin)
+ * - Call nand_unlock() once for each consecutive area to be unlocked
+ * - If desired: Bring the chip to the lock-tight state using nand_lock(mtd, 1)
+ *
+ *   If the device is in lock-tight state software can't change the
+ *   current active lock/unlock state of all pages. nand_lock() / nand_unlock()
+ *   calls will fail. It is only posible to leave lock-tight state by
+ *   an hardware signal (low pulse on _WP pin) or by power down.
+ */
+int nand_lock(struct mtd_info *mtd, int tight)
+{
+	int ret = 0;
+	int status;
+	struct nand_chip *chip = mtd->priv;
+
+	/* select the NAND device */
+	chip->select_chip(mtd, 0);
+
+	chip->cmdfunc(mtd,
+		      (tight ? NAND_CMD_LOCK_TIGHT : NAND_CMD_LOCK),
+		      -1, -1);
+
+	/* call wait ready function */
+	status = chip->waitfunc(mtd, chip);
+
+	/* see if device thinks it succeeded */
+	if (status & 0x01) {
+		ret = -1;
+	}
+
+	/* de-select the NAND device */
+	chip->select_chip(mtd, -1);
+	return ret;
+}
+
+/**
+ * nand_get_lock_status: - query current lock state from one page of NAND
+ *			   flash
+ *
+ * @param mtd		nand mtd instance
+ * @param offset	page address to query (muss be page aligned!)
+ *
+ * @return		-1 in case of error
+ *			>0 lock status:
+ *			  bitfield with the following combinations:
+ *			  NAND_LOCK_STATUS_TIGHT: page in tight state
+ *			  NAND_LOCK_STATUS_LOCK:  page locked
+ *			  NAND_LOCK_STATUS_UNLOCK: page unlocked
+ *
+ */
+int nand_get_lock_status(struct mtd_info *mtd, loff_t offset)
+{
+	int ret = 0;
+	int chipnr;
+	int page;
+	struct nand_chip *chip = mtd->priv;
+
+	/* select the NAND device */
+	chipnr = (int)(offset >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+
+	if ((offset & (mtd->writesize - 1)) != 0) {
+		printf ("nand_get_lock_status: "
+			"Start address must be beginning of "
+			"nand page!\n");
+		ret = -1;
+		goto out;
+	}
+
+	/* check the Lock Status */
+	page = (int)(offset >> chip->page_shift);
+	chip->cmdfunc(mtd, NAND_CMD_LOCK_STATUS, -1, page & chip->pagemask);
+
+	ret = chip->read_byte(mtd) & (NAND_LOCK_STATUS_TIGHT
+					  | NAND_LOCK_STATUS_LOCK
+					  | NAND_LOCK_STATUS_UNLOCK);
+
+ out:
+	/* de-select the NAND device */
+	chip->select_chip(mtd, -1);
+	return ret;
+}
+
+/**
+ * nand_unlock: - Unlock area of NAND pages
+ *		  only one consecutive area can be unlocked at one time!
+ *
+ * @param mtd		nand mtd instance
+ * @param start		start byte address
+ * @param length	number of bytes to unlock (must be a multiple of
+ *			page size nand->writesize)
+ *
+ * @return		0 on success, -1 in case of error
+ */
+int nand_unlock(struct mtd_info *mtd, ulong start, ulong length)
+{
+	int ret = 0;
+	int chipnr;
+	int status;
+	int page;
+	struct nand_chip *chip = mtd->priv;
+	printf ("nand_unlock: start: %08x, length: %d!\n",
+		(int)start, (int)length);
+
+	/* select the NAND device */
+	chipnr = (int)(start >> chip->chip_shift);
+	chip->select_chip(mtd, chipnr);
+
+	/* check the WP bit */
+	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	if (!(chip->read_byte(mtd) & NAND_STATUS_WP)) {
+		printf ("nand_unlock: Device is write protected!\n");
+		ret = -1;
+		goto out;
+	}
+
+	if ((start & (mtd->erasesize - 1)) != 0) {
+		printf ("nand_unlock: Start address must be beginning of "
+			"nand block!\n");
+		ret = -1;
+		goto out;
+	}
+
+	if (length == 0 || (length & (mtd->erasesize - 1)) != 0) {
+		printf ("nand_unlock: Length must be a multiple of nand block "
+			"size %08x!\n", mtd->erasesize);
+		ret = -1;
+		goto out;
+	}
+
+	/*
+	 * Set length so that the last address is set to the
+	 * starting address of the last block
+	 */
+	length -= mtd->erasesize;
+
+	/* submit address of first page to unlock */
+	page = (int)(start >> chip->page_shift);
+	chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask);
+
+	/* submit ADDRESS of LAST page to unlock */
+	page += (int)(length >> chip->page_shift);
+	chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, page & chip->pagemask);
+
+	/* call wait ready function */
+	status = chip->waitfunc(mtd, chip);
+	/* see if device thinks it succeeded */
+	if (status & 0x01) {
+		/* there was an error */
+		ret = -1;
+		goto out;
+	}
+
+ out:
+	/* de-select the NAND device */
+	chip->select_chip(mtd, -1);
+	return ret;
+}
+#endif
+
+/**
+ * check_skip_len
+ *
+ * Check if there are any bad blocks, and whether length including bad
+ * blocks fits into device
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param length image length
+ * @return 0 if the image fits and there are no bad blocks
+ *         1 if the image fits, but there are bad blocks
+ *        -1 if the image does not fit
+ */
+static int check_skip_len(nand_info_t *nand, loff_t offset, size_t length)
+{
+	size_t len_excl_bad = 0;
+	int ret = 0;
+
+	while (len_excl_bad < length) {
+		size_t block_len, block_off;
+		loff_t block_start;
+
+		if (offset >= nand->size)
+			return -1;
+
+		block_start = offset & ~(loff_t)(nand->erasesize - 1);
+		block_off = offset & (nand->erasesize - 1);
+		block_len = nand->erasesize - block_off;
+
+		if (!nand_block_isbad(nand, block_start))
+			len_excl_bad += block_len;
+		else
+			ret = 1;
+
+		offset += block_len;
+	}
+
+	return ret;
+}
+
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+static size_t drop_ffs(const nand_info_t *nand, const u_char *buf,
+			const size_t *len)
+{
+	size_t i, l = *len;
+
+	for (i = l - 1; i >= 0; i--)
+		if (buf[i] != 0xFF)
+			break;
+
+	/* The resulting length must be aligned to the minimum flash I/O size */
+	l = i + 1;
+	l = (l + nand->writesize - 1) / nand->writesize;
+	l *=  nand->writesize;
+
+	/*
+	 * since the input length may be unaligned, prevent access past the end
+	 * of the buffer
+	 */
+	return min(l, *len);
+}
+#endif
+
+/**
+ * nand_write_skip_bad:
+ *
+ * Write image to NAND flash.
+ * Blocks that are marked bad are skipped and the is written to the next
+ * block instead as long as the image is short enough to fit even after
+ * skipping the bad blocks.
+ *
+ * @param nand  	NAND device
+ * @param offset	offset in flash
+ * @param length	buffer length
+ * @param buffer        buffer to read from
+ * @param flags		flags modifying the behaviour of the write to NAND
+ * @return		0 in case of success
+ */
+int nand_write_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
+			u_char *buffer, int flags)
+{
+	int rval = 0, blocksize;
+	size_t left_to_write = *length;
+	u_char *p_buffer = buffer;
+	int need_skip;
+
+#ifdef CONFIG_CMD_NAND_YAFFS
+	if (flags & WITH_YAFFS_OOB) {
+		if (flags & ~WITH_YAFFS_OOB)
+			return -EINVAL;
+
+		int pages;
+		pages = nand->erasesize / nand->writesize;
+		blocksize = (pages * nand->oobsize) + nand->erasesize;
+		if (*length % (nand->writesize + nand->oobsize)) {
+			printf ("Attempt to write incomplete page"
+				" in yaffs mode\n");
+			return -EINVAL;
+		}
+	} else
+#endif
+	{
+		blocksize = nand->erasesize;
+	}
+
+	/*
+	 * nand_write() handles unaligned, partial page writes.
+	 *
+	 * We allow length to be unaligned, for convenience in
+	 * using the $filesize variable.
+	 *
+	 * However, starting at an unaligned offset makes the
+	 * semantics of bad block skipping ambiguous (really,
+	 * you should only start a block skipping access at a
+	 * partition boundary).  So don't try to handle that.
+	 */
+	if ((offset & (nand->writesize - 1)) != 0) {
+		printf ("Attempt to write non page aligned data\n");
+		*length = 0;
+		return -EINVAL;
+	}
+
+	need_skip = check_skip_len(nand, offset, *length);
+	if (need_skip < 0) {
+		printf ("Attempt to write outside the flash area\n");
+		*length = 0;
+		return -EINVAL;
+	}
+
+	if (!need_skip && !(flags & WITH_DROP_FFS)) {
+		rval = nand_write (nand, offset, length, buffer);
+		if (rval == 0)
+			return 0;
+
+		*length = 0;
+		printf ("NAND write to offset %llx failed %d\n",
+			(u64)offset, rval);
+		return rval;
+	}
+
+	while (left_to_write > 0) {
+		size_t block_offset = offset & (nand->erasesize - 1);
+		size_t write_size, truncated_write_size;
+
+		//WATCHDOG_RESET ();
+
+		if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
+			printf ("Skip bad block 0x%08llx\n",
+				(u64)(offset & ~(nand->erasesize - 1)));
+			offset += nand->erasesize;
+			continue;
+		}
+
+		if (left_to_write < (blocksize - block_offset))
+			write_size = left_to_write;
+		else
+			write_size = blocksize - block_offset;
+
+#ifdef CONFIG_CMD_NAND_YAFFS
+		if (flags & WITH_YAFFS_OOB) {
+			int page, pages;
+			size_t pagesize = nand->writesize;
+			size_t pagesize_oob = pagesize + nand->oobsize;
+			struct mtd_oob_ops ops;
+
+			ops.len = pagesize;
+			ops.ooblen = nand->oobsize;
+			ops.mode = MTD_OOB_AUTO;
+			ops.ooboffs = 0;
+
+			pages = write_size / pagesize_oob;
+			for (page = 0; page < pages; page++) {
+				//WATCHDOG_RESET();
+
+				ops.datbuf = p_buffer;
+				ops.oobbuf = ops.datbuf + pagesize;
+
+				rval = nand->write_oob(nand, offset, &ops);
+				if (!rval)
+					break;
+
+				offset += pagesize;
+				p_buffer += pagesize_oob;
+			}
+		}
+		else
+#endif
+		{
+			truncated_write_size = write_size;
+#ifdef CONFIG_CMD_NAND_TRIMFFS
+			if (flags & WITH_DROP_FFS)
+				truncated_write_size = drop_ffs(nand, p_buffer,
+						&write_size);
+#endif
+
+			rval = nand_write(nand, offset, &truncated_write_size,
+					p_buffer);
+			offset += write_size;
+			p_buffer += write_size;
+		}
+
+		if (rval != 0) {
+			printf ("NAND write to offset %llx failed %d\n",
+				(u64)offset, rval);
+			*length -= left_to_write;
+			return rval;
+		}
+
+		left_to_write -= write_size;
+	}
+
+	return 0;
+}
+
+/**
+ * nand_read_skip_bad:
+ *
+ * Read image from NAND flash.
+ * Blocks that are marked bad are skipped and the next block is readen
+ * instead as long as the image is short enough to fit even after skipping the
+ * bad blocks.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param length buffer length, on return holds remaining bytes to read
+ * @param buffer buffer to write to
+ * @return 0 in case of success
+ */
+int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,
+		       u_char *buffer)
+{
+	int rval;
+	size_t left_to_read = *length;
+	u_char *p_buffer = buffer;
+	int need_skip;
+
+	if ((offset & (nand->writesize - 1)) != 0) {
+		printf ("Attempt to read non page aligned data\n");
+		*length = 0;
+		return -EINVAL;
+	}
+
+	need_skip = check_skip_len(nand, offset, *length);
+	if (need_skip < 0) 
+    {
+		printf ("Attempt to read outside the flash area\n");
+		*length = 0;
+		return -EINVAL;
+	}
+
+	if (!need_skip) 
+    {
+		rval = nand_read (nand, offset, length, buffer);
+		if (!rval || rval == -EUCLEAN)
+			return 0;
+
+		*length = 0;
+		printf ("NAND read from offset %llx failed %d\n",(u64)offset, rval);
+		return rval;
+	}
+
+	while (left_to_read > 0) 
+    {
+		size_t block_offset = offset & (nand->erasesize - 1);
+		size_t read_length;
+
+		//WATCHDOG_RESET ();
+
+		if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) 
+        {   
+            #if NAND_BAD_DEBUG
+			printf ("Skipping bad block 0x%08llx\n",
+				(u64)(offset & ~(nand->erasesize - 1)));
+            #endif
+			offset += nand->erasesize;
+			continue;
+		}
+
+		if (left_to_read < (nand->erasesize - block_offset))
+			read_length = left_to_read;
+		else
+			read_length = nand->erasesize - block_offset;
+
+		rval = nand_read (nand, offset, &read_length, p_buffer);
+		if (rval && rval != -EUCLEAN) 
+        {
+			printf ("NAND read from offset %llx failed %d\n",
+				(u64)offset, rval);
+			*length -= left_to_read;
+			return rval;
+		}
+
+		left_to_read -= read_length;
+		offset       += read_length;
+		p_buffer     += read_length;
+	}
+
+	return 0;
+}
+
+int read_write_with_no_ecc_flag = 0;
+
+void read_write_with_no_ecc_flag_set(int flag)
+{
+	read_write_with_no_ecc_flag = flag;
+}
+int read_write_with_no_ecc_flag_get()
+{
+	return read_write_with_no_ecc_flag;
+}
+
+/**
+ * nand_write_skip_bad_no_ecc:  add by zhouqi for write z-load
+ *
+ * Read image from NAND flash.
+ * Blocks that are marked bad are skipped and the next block is readen
+ * instead as long as the image is short enough to fit even after skipping the
+ * bad blocks.
+ *
+ * @param nand NAND device
+ * @param offset offset in flash
+ * @param length buffer length, on return holds remaining bytes to read
+ * @param buffer buffer to write to
+ * @return 0 in case of success
+ */
+int nand_write_page_with_no_ecc(nand_info_t *nand, loff_t offset, u_char *buffer )
+{
+    uint8_t oob[128];
+    
+    memset(&oob, 0xff, 128);
+    if ((offset & (nand->writesize - 1)) != 0) 
+    {
+		printf ("Attempt to write non page aligned data\n");
+
+		return -EINVAL;
+	}
+    
+    struct mtd_oob_ops ops;
+
+    ops.datbuf = buffer;
+    ops.oobbuf = (uint8_t *)&oob;
+    ops.len = nand->writesize;
+	ops.ooblen = nand->oobsize;
+	ops.mode = MTD_OOB_RAW;
+	ops.ooboffs = 0;
+
+    nand->write_oob(nand, offset, &ops);
+    
+	read_write_with_no_ecc_flag_set(1);
+	return 0;
+}
+
+int nand_write_page_with_ecc(nand_info_t *nand, loff_t offset, u_char *buffer )
+{
+    
+    if ((offset & (nand->writesize - 1)) != 0) 
+    {
+		printf ("Attempt to write non page aligned data\n");
+
+		return -EINVAL;
+	}
+    
+    struct mtd_oob_ops ops;
+
+    ops.datbuf = buffer;
+    ops.oobbuf = NULL;
+    ops.len = nand->writesize;
+	ops.ooblen = 0;
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = 0;
+
+    nand->write_oob(nand, offset, &ops);
+
+	return 0;
+}
+
+
+/**
+ * nand_read_page_with_no_ecc:  add by zhouqi for write partition_table.bin
+ *
+ * buffer = nand->writesize + nand->oobsize
+ */
+int nand_read_page_with_no_ecc(nand_info_t *nand, loff_t offset, size_t *length,
+		       u_char *buffer)
+{
+    uint8_t oob[128];
+    
+    if ((offset & (nand->writesize - 1)) != 0) 
+    {
+		printf ("Attempt to write non page aligned data\n");
+
+		return -EINVAL;
+	}
+    
+    struct mtd_oob_ops ops;
+
+    ops.datbuf = buffer;
+    ops.oobbuf = (uint8_t *)&oob;          /* must exist, but oob data will be appended to ops.datbuf */
+    ops.len = nand->writesize;
+	ops.ooblen = nand->oobsize;
+	ops.mode = MTD_OOB_RAW;
+	ops.ooboffs = 0;
+
+    nand->read_oob(nand, offset, &ops);
+    
+	read_write_with_no_ecc_flag_set(1);
+	return 0;
+}
+
+int nand_read_page_with_ecc(nand_info_t *nand, loff_t offset, size_t *length,
+		       u_char *buffer)
+{
+    
+    if ((offset & (nand->writesize - 1)) != 0) 
+    {
+		printf ("Attempt to write non page aligned data\n");
+
+		return -EINVAL;
+	}
+    
+    struct mtd_oob_ops ops;
+
+    ops.datbuf = buffer;
+    ops.oobbuf = NULL;          
+    ops.len = nand->writesize;
+	ops.ooblen = 0;
+	ops.mode = MTD_OOB_PLACE;
+	ops.ooboffs = 0;
+
+    nand->read_oob(nand, offset, &ops);
+
+	return 0;
+}
+
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/spi_nand.c b/boot/common/src/uboot/drivers/mtd/nand/spi_nand.c
new file mode 100755
index 0000000..f5be951
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/spi_nand.c
@@ -0,0 +1,927 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spi_nand.c
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 
+*     Version:
+*     Author: 
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#include <malloc.h> 
+#include <errno.h>
+#include <asm/io.h> 
+#include <linux/mtd/mtd.h>
+#include <asm-generic/ioctl.h>
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/nand.h> 
+#include <linux/mtd/nand.h>
+#include <asm/arch/lsp_crpm.h>
+#include <drvs_gpio.h>
+#include "spi_nand.h"
+#include <boot_mode.h>
+
+int flash_dmabuf_disable_flag = 0; 
+
+extern struct nand_flash_device_para nand_flash_para[];
+extern struct mtd_info nand_info[];
+extern struct nand_chip nand_chip[];
+extern struct nand_flash_device_para *g_nand_dev_info;
+static int spi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			   int page, int sndcmd);
+
+#define mtd_to_spi_nand(m) 		(struct spi_nand_info *)(((struct nand_chip *)(m->priv))->priv)
+
+#define __ECC_CHECK_SUPPORT__
+
+#ifdef __ECC_CHECK_SUPPORT__
+static void spi_nand_enable_ecc(struct mtd_info *mtd, int enable)
+{
+    int ret = 0;
+    uint8_t feature = 0x0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;	
+
+	ret = ctrl->get_feature(REG_FEATURE, &feature);
+	if ( ret != 0 )
+	{
+		printk("[SPI-NAND][spi_fc_get_feature]\n");
+	}
+
+	if(enable)
+		feature |= ECC_EN;
+	else
+		feature &= ~ECC_EN;		
+	ret = ctrl->set_feature(REG_FEATURE, &feature);
+	if ( ret != 0 )
+	{
+		printk("[SPI-NAND][spi_fc_set_feature]\n");
+	}
+}
+#else
+static void spi_nand_enable_ecc(struct mtd_info *mtd, int enable){}
+#endif
+
+
+/*******************************************************************************
+ * Function:    spi_nand_read_byte
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static uint8_t spi_nand_read_byte(struct mtd_info *mtd)
+{
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	uint8_t result = 0xff;
+
+	if (spi_nand->buf.head < spi_nand->buf.tail)
+		result = spi_nand->buf.buf[spi_nand->buf.head++];
+
+	return result;
+}
+
+/*******************************************************************************
+ * Function:    reset_buf
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void reset_buf(struct spi_nand_info *spi_nand)
+{
+	spi_nand->buf.head = spi_nand->buf.tail = 0;
+}
+
+
+/*******************************************************************************
+ * Function:    write_byte_to_buf
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void write_byte_to_buf(struct spi_nand_info *spi_nand, uint8_t byte)
+{
+	BUG_ON(spi_nand->buf.tail >= SPI_NAND_BUF_SIZE/*sizeof(spi_nand->buf.buf)*/);
+	spi_nand->buf.buf[spi_nand->buf.tail++] = byte;
+}
+
+
+/*******************************************************************************
+ * Function:    read_status
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void read_status(struct spi_nand_info *spi_nand)
+{
+	uint32_t status; 
+    status = 0X80;
+    //spi_nand_debug("[SPI-NAND][read_status]\n");
+	write_byte_to_buf(spi_nand, status);
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_read_word
+ * Description: nand_block_badУȡoobÏ¢(nand_chip->oob_poi)ǰ2×Ö½Ú
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static uint16_t spi_nand_read_word(struct mtd_info *mtd)
+{
+    struct nand_chip *chip = mtd->priv;
+	uint16_t result = 0x0;
+
+    result = (uint16_t)(*(chip->oob_poi));
+    result = result << 8;
+    result |= (uint16_t)(*(chip->oob_poi + 1));
+    
+	return result;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_select_chip
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_waitfunc
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_waitfunc(struct mtd_info *mtd, struct nand_chip *chip)
+{
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+    
+	int status = spi_nand->status;
+	spi_nand->status = 0;
+    
+	return status;
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_get_real_page
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_get_real_page(struct mtd_info *mtd, int page)
+{
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int die;
+
+	if(spi_nand->para->die_num == 1)
+		return page;
+
+	die = page/spi_nand->pages_per_die;
+	if(ctrl->switch_die(die))
+		return -1;
+
+	return (page-die*spi_nand->pages_per_die);
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_erase
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_erase(struct mtd_info *mtd, int page)
+{   
+    int ret = 0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int real_page = spi_nand_get_real_page(mtd, page);
+    
+    //spi_nand_debug("\n[SPI-NAND][spi_nand_erase][page] = 0x%0x\n", page);
+
+    ret = ctrl->write_enable();
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_write_enable][ERROR]\n");
+    }  
+
+    ret = ctrl->erase(real_page);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_erase][ERROR]\n");
+    }
+
+    if( ret != 0 )
+    {
+        spi_nand->status = NAND_STATUS_FAIL;
+        printf("[SPI-NAND][spi_nand_erase][NAND_STATUS_FAIL][ret] = %d\n", ret);
+    }
+    else
+    {
+        spi_nand->status = 0;
+        //spi_nand_debug("[SPI-NAND][spi_nand_erase][PASS]\n");
+    }
+    
+    ret = ctrl->write_disable();
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_write_disable][ERROR]\n");
+    }
+
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_cmdfunc
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col,
+			   int page)
+{
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+    
+	switch (cmd) {
+    	case NAND_CMD_PAGEPROG:
+    		break;
+    	case NAND_CMD_STATUS:
+            reset_buf(spi_nand);
+    		read_status(spi_nand);
+    		break;
+    	case NAND_CMD_READID:
+    	case NAND_CMD_PARAM:
+    		reset_buf(spi_nand);
+            
+            write_byte_to_buf(spi_nand, spi_nand->para->manuf_id);
+            write_byte_to_buf(spi_nand, spi_nand->para->device_id);
+                       
+    		break;
+    	case NAND_CMD_READ0:
+    	case NAND_CMD_SEQIN:
+    		spi_nand->page = page;
+    		break;
+    	case NAND_CMD_RESET:
+            ctrl->reset();
+    		break;
+    	case NAND_CMD_READOOB:
+            reset_buf(spi_nand);      //zhouqi
+            spi_nand_read_oob(mtd, mtd->priv, page, 0);
+    		/* TODO: Read OOB data */
+    		break;
+    	default:
+    		debug(": unsupported command"
+    				" received 0x%x\n", cmd);
+    		break;
+	}
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_ecc_calculate
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_ecc_calculate(struct mtd_info *mtd, const uint8_t *data,
+				uint8_t *ecc_code)
+{
+	debug("spi_nand_ecc_calculate called unexpectedly\n");
+	return -1;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_ecc_correct
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_ecc_correct(struct mtd_info *mtd, uint8_t *data,
+				uint8_t *read_ecc, uint8_t *calc_ecc)
+{
+	debug("spi_nand_ecc_correct called unexpectedly\n");
+	return -1;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_ecc_hwctl
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_ecc_hwctl(struct mtd_info *mtd, int mode)
+{
+	debug("spi_nand_ecc_hwctl called unexpectedly\n");
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_check_ecc
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_check_ecc(struct spi_nand_info *spi_nand, uint32_t *ecc)
+{
+    int ret = 0;
+    uint8_t feature = 0x0;
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;	
+
+    ret = ctrl->get_feature(REG_STATUS, &feature);
+    
+    //spi_nand_debug("[SPI-NAND][check_ecc][feature] = 0x%0x\n", feature);
+    
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature]\n");
+    }
+    
+    if( (feature>>ECC_ERR_BIT & 0x3) == 2 )
+    {
+        printf("[SPI-NAND][REG_STATUS][ECC-ERROR]\n");
+        *ecc = 1;
+    }
+    else
+        *ecc = 0;
+    
+    return ret;
+}
+
+static int spi_nand_write(struct mtd_info *mtd, int raw, 
+							const unsigned char *buf, unsigned char *oob_buf) 
+{
+	int ret = 0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int page = spi_nand_get_real_page(mtd, spi_nand->page);
+	uint32_t column_offset = 0;
+	
+	ret = ctrl->write_enable();
+	if ( ret != 0 )
+	{
+		printf("[SPI-NAND][spi_fc_write_enable][ERROR]\n");
+	}
+
+	if(buf)
+    	memcpy((void*)spi_nand->buf.buf, buf, mtd->writesize);				
+	else /* write oob */
+		memset((uint8_t *)spi_nand->buf.buf, 0xFF, mtd->writesize);
+
+	memcpy((void*)(spi_nand->buf.buf + mtd->writesize), oob_buf, mtd->oobsize);
+
+	if((spi_nand->para->planes == 2) && (((page >> 6)%2) != 0))
+	{
+		column_offset |= (0x1 << 12);
+	}
+	
+	ret = ctrl->page_load(column_offset, (uint8_t *)spi_nand->buf.dma_buf, mtd->writesize, oob_buf, mtd->oobsize);	
+	
+	if ( ret != 0 )
+	{
+		printf("[SPI-NAND][spifc_page_load][ERROR]\n");
+	}
+
+	ret = ctrl->program_exec(page);
+	if ( ret != 0 )
+	{
+		spi_nand->status = NAND_STATUS_FAIL;
+		printf("[SPI-NAND][spi_fc_program_exec][NAND_STATUS_FAIL][ret] = %d\n", ret);
+	}
+	else
+	{
+		spi_nand->status = 0;
+	}
+
+	ret = ctrl->write_disable();
+	if ( ret != 0 )
+	{
+		printf("[SPI-NAND][spi_fc_write_disable][ERROR]\n");
+	}
+	
+	return ret;
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_write_page
+ * Description: 
+ *                  
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
+				const uint8_t *buf, struct mtd_oob_ops *ops)
+{
+	spi_nand_write(mtd, 0, buf, chip->oob_poi);
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_write_page_raw
+ * Description: MTD Interface ²»Ê¹ÄÜ ECC, д MAIN + OOB
+ *                  
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+					const uint8_t *buf)
+{
+	spi_nand_enable_ecc(mtd, 0);
+	spi_nand_write(mtd, 1, buf, chip->oob_poi);
+	spi_nand_enable_ecc(mtd, 1);		
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_write_oob
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			    int page)
+{
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+
+    spi_nand->page=page;
+    
+	return spi_nand_write(mtd, 0, NULL, chip->oob_poi);
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_read_oob
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+			   int page, int sndcmd)
+{   
+    int ret = 0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int real_page = spi_nand_get_real_page(mtd, page);
+	uint32_t column_offset = mtd->writesize;
+	
+    ret = ctrl->read_page_to_cache(real_page);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+    }
+
+	if((spi_nand->para->planes == 2) && (((real_page >> 6)%2) != 0))
+	{
+		column_offset |= (0x1 << 12);
+	}
+		
+    ret = ctrl->read_from_cache(column_offset, mtd->oobsize, (uint8_t *)spi_nand->buf.dma_buf);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+    }
+
+    memcpy(chip->oob_poi, (void*)(spi_nand->buf.dma_buf), mtd->oobsize);
+   
+	return 0; 
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_read_page
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+			    uint8_t *buf, int page, struct mtd_oob_ops *ops)
+{
+    int ret = 0;
+    uint32_t ecc = 0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int real_page; 
+	uint32_t column_offset = 0;
+	 
+    if (page != spi_nand->page) 
+    {
+		debug("IN %s: page %d is not"
+				" equal to denali->page %d, investigate!!",
+				__func__, page, spi_nand->page);
+	}
+
+	real_page = spi_nand_get_real_page(mtd, page);
+
+    ret = ctrl->read_page_to_cache(real_page);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+    }
+    
+    ret = spi_nand_check_ecc(spi_nand, &ecc);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_nand_check_ecc]\n");
+    }
+    if( ecc != 0 )
+    {
+        printf("[SPI-NAND][ECC-spifc_read_page_raw][ECC-ERROR][%x]\n",real_page*mtd->writesize);
+		mtd->ecc_stats.failed++;		
+    }
+
+	if((spi_nand->para->planes == 2) && (((real_page >> 6)%2) != 0))
+	{
+		column_offset |= (0x1 << 12);
+	}
+	
+	if(flash_dmabuf_disable_flag== 0)
+	{
+	    ret = ctrl->read_from_cache(column_offset, (mtd->writesize+mtd->oobsize), (uint8_t *)spi_nand->buf.dma_buf);
+	    if( ret != 0 )
+	    {
+	        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+	    }	
+		memcpy(buf, (void*)spi_nand->buf.dma_buf, mtd->writesize);
+		memcpy(chip->oob_poi, (void*)(spi_nand->buf.dma_buf + mtd->writesize), mtd->oobsize);		
+	}
+	else
+	{
+	    ret = ctrl->read_from_cache(column_offset, (mtd->writesize), (uint8_t *)buf);
+	    if( ret != 0 )
+	    {
+	        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+	    }
+	}
+
+    return 0;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_nand_read_page_raw
+ * Description: ²»Ê¹ÄÜECC£¬¶ÁÈ¡ MAIN + OOB
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
+				uint8_t *buf, int page)
+{
+    int ret = 0;
+    uint32_t ecc = 0;
+    struct spi_nand_info *spi_nand = mtd_to_spi_nand(mtd);
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+	int real_page; 
+	uint32_t column_offset = 0;
+	
+    if (page != spi_nand->page) 
+    {
+		debug("IN %s: page %d is not"
+				" equal to denali->page %d, investigate!!",
+				__func__, page, spi_nand->page);
+	}
+
+	spi_nand_enable_ecc(mtd, 0); 
+	
+	real_page = spi_nand_get_real_page(mtd, page);
+	
+    ret = ctrl->read_page_to_cache(real_page);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+    }
+
+	if((spi_nand->para->planes == 2) && (((real_page >> 6)%2) != 0))
+	{
+		column_offset |= (0x1 << 12);
+	}
+
+    ret = ctrl->read_from_cache(column_offset, (mtd->writesize+mtd->oobsize), (uint8_t *)spi_nand->buf.dma_buf);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_read_page_to_cache]\n");
+    }
+
+    memcpy(buf, (void*)spi_nand->buf.dma_buf, mtd->writesize);
+	memcpy(chip->oob_poi, (void*)(spi_nand->buf.dma_buf + mtd->writesize), mtd->oobsize);
+   
+	spi_nand_enable_ecc(mtd, 1); 
+	
+    return 0;
+}
+
+
+#define ECC_4BITS	32
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	4,
+	.len = 4,
+	.veroffs = 20,
+	.maxblocks = 16,
+	.pattern = bbt_pattern,
+};
+
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	4,
+	.len = 4,
+	.veroffs = 20,
+	.maxblocks = 16,
+	.pattern = mirror_pattern,
+};
+
+//static 
+int spi_nand_common_init(struct spi_nand_info *spi_nand)
+{   
+    int ret = SUCCESS;
+    uchar feature = 0x0;
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;	
+
+    feature = 0x0;
+    ret = ctrl->set_feature(REG_PROTECTION, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_feature]\n");
+    }
+
+	ctrl->get_feature(REG_PROTECTION, &feature);
+    
+    printf("[SPI-NAND][A0] = 0x%0x\n", feature);
+
+    /*
+    * enable ecc nand Quad mode
+    */
+
+	feature= 0;
+    feature = (ECC_EN|QE);
+	/*ÊÊÅämicron 4G flash*/
+	if((spi_nand->para->manuf_id == NAND_MFR_MICRON) && (spi_nand->para->device_id == 0x35))
+	{
+        /*½«continute_readλÖÃ0*/
+		feature &= ~(1<<0);
+    }
+    ret = ctrl->set_feature(REG_FEATURE, &feature);
+    if ( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_feature]\n");
+    }
+
+	ctrl->get_feature(REG_FEATURE, &feature);
+    
+    printf("[SPI-NAND][B0] = 0x%0x\n", feature);
+
+	return ret;
+}
+int spi_nand_register(struct spi_nand_info *spi_nand, struct spi_nand_ctrl_info *ctrl)
+{
+	int ret = -1; 
+	struct mtd_info *	mtd;
+	struct nand_chip *	chip;
+
+	mtd  = spi_nand->mtd;
+	chip = spi_nand->nand;
+
+	chip->priv = spi_nand;
+	spi_nand->ctrl = ctrl;
+
+	/* register the driver with the NAND core subsystem */
+	chip->select_chip	= spi_nand_select_chip;
+	chip->cmdfunc		= spi_nand_cmdfunc;
+	chip->read_byte 	= spi_nand_read_byte;
+	chip->read_word 	= spi_nand_read_word;
+	chip->waitfunc		= spi_nand_waitfunc;
+
+	/* scan for NAND devices attached to the controller
+	 * this is the first stage in a two step process to register
+	 * with the nand subsystem */
+	if (nand_scan_ident(mtd, spi_nand->total_used_banks, NULL)) 
+    {
+       	debug("nand ident: cant ident this nand device");
+		return -1;
+	}
+
+	/* MTD supported page sizes vary by kernel. We validate our
+	 * kernel supports the device here.
+	 */
+	if (mtd->writesize > NAND_MAX_PAGESIZE) 
+    {
+		ret = -1;
+		debug("Spectra: device size not supported by this "
+			"version of MTD.");
+		return ret;
+	}
+
+	/* support for multi nand
+	 * MTD known nothing about multi nand,
+	 * so we should tell it the real pagesize
+	 * and anything necessery
+	 */
+	spi_nand->devnum = 1;
+    chip->pagemask = (chip->chipsize >> chip->page_shift) - 1;
+    spi_nand->bbtskipbytes = 0x2;
+
+	/* second stage of the NAND scan
+	 * this stage requires information regarding ECC and
+	 * bad block management. */
+
+	/* Bad block management */
+	chip->bbt_td = &bbt_main_descr;
+	chip->bbt_md = &bbt_mirror_descr;
+
+	/* skip the scan for now until we have OOB read and write support */
+	chip->options |= NAND_USE_FLASH_BBT;//NAND_SKIP_BBTSCAN
+	chip->ecc.mode = NAND_ECC_HW_SYNDROME;
+
+	/* Denali Controller only support 15bit and 8bit ECC in MRST,
+	 * so just let controller do 15bit ECC for MLC and 8bit ECC for
+	 * SLC if possible.
+	 * */
+	chip->ecc.bytes = ECC_4BITS;
+
+	/* These functions are required by the NAND core framework, otherwise,
+	 * the NAND core will assert. However, we don't need them, so we'll stub
+	 * them out. */
+	chip->ecc.size = 512;
+	chip->ecc.calculate = spi_nand_ecc_calculate;
+	chip->ecc.correct = spi_nand_ecc_correct;
+	chip->ecc.hwctl = spi_nand_ecc_hwctl;
+	chip->ecc.read_page = spi_nand_read_page;
+	chip->ecc.read_page_raw = spi_nand_read_page_raw;
+	chip->ecc.write_page = spi_nand_write_page;
+	chip->ecc.write_page_raw = spi_nand_write_page_raw;
+	chip->ecc.read_oob = spi_nand_read_oob;
+	chip->ecc.write_oob = spi_nand_write_oob;
+	chip->erase_cmd = spi_nand_erase;
+
+    /*
+    * remove spi nand chip write protection
+    */
+	spi_nand_common_init(spi_nand);
+	spi_nand_special_init(spi_nand);
+
+	if (nand_scan_tail(mtd)) 
+    {
+		ret = -1;
+	}
+      
+	return 0;
+}
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/spi_nand.h b/boot/common/src/uboot/drivers/mtd/nand/spi_nand.h
new file mode 100644
index 0000000..eefdb5c
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/spi_nand.h
@@ -0,0 +1,167 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spi_nand.h
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 2014.1.15
+*     Version:
+*     Author: zhouqi
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#ifndef __SPI_NAND_H__
+#define __SPI_NAND_H__
+
+#include <linux/mtd/nand.h>
+
+/* SPI NAND CMD */
+#define CMD_WRITE_ENABLE            0x06
+#define CMD_WRITE_DISABLE           0x04
+#define CMD_GET_FEATURE		        0x0F
+#define CMD_SET_FEATURE		        0x1F
+
+#define CMD_READ_PAGE_TO_CACHE	     0x13
+#define CMD_READ_FROM_CACHE          0x03
+//#define CMD_READ_FROM_CACHE_X2       0x3B
+#define CMD_READ_FROM_CACHE_X4       0x6B
+#define CMD_READ_FROM_CACHE_QIO      0xEB
+
+#define CMD_READ_ID		            0x9F
+#define ID_ADDR0                     0x00  /*manufacture id address*/
+#define ID_ADDR1                     0x01  /*device id address*/
+
+#define CMD_PROGRAM_LOAD		    0x02
+#define CMD_PROGRAM_LOAD_X4		    0x32
+
+#define CMD_PROGRAM_EXECUTE		    0x10
+//#define CMD_PROGRAM_LOAD_RANDOM      0x84
+//#define CMD_PROGRAM_LOAD_RANDOM_X4   0xC4
+//#define CMD_PROGRAM_LOAD_RANDOM_QIO  0x72
+
+#define CMD_BLOCK_ERASE             0xD8
+
+#define CMD_RESET                   0xFF
+
+#define CMD_WINBOND_DIE_SWITCH		0xC2
+
+#define SINGLE_MODE 0
+//#define DUAL_MODE   1
+#define RDX4_MODE    2
+#define RDQIO_MODE   3
+#define PLX4_MODE    4
+
+/*read and write mode configuration*/
+//#define RD_MODE  RDX4_MODE
+//#define RD_MODE  RDQIO_MODE
+#define RD_MODE  RDQIO_MODE
+#define WR_MODE  PLX4_MODE
+
+#define	READ_TRANSFER_MODE		0
+#define	WRITE_TRANSFER_MODE		1
+
+#define ADDR_TX_EN      1   /* µØÖ·Âë·¢ËÍʹÄÜ */
+#define ADDR_TX_DIS     0
+#define DATA_TX_EN      1
+#define DATA_TX_DIS     0
+#define DATA_RX_EN      1
+#define DATA_RX_DIS     0
+#define DUMY_TX_EN      1
+#define DUMY_TX_DIS     0
+
+#define ADDR_WIDTH_8    0
+#define ADDR_WIDTH_16   1
+#define ADDR_WIDTH_24   2
+#define ADDR_WIDTH_32   3
+
+struct spiflash_cmd_t
+{
+    uint32_t cmd;
+    uint32_t addr_tx_en;      /* µØÖ·Âë·¢ËÍʹÄÜ */
+    uint32_t addr_width;        /* µØÖ·Âë¿í¶È */
+    uint32_t data_tx_en;      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+    uint32_t data_rx_en;      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+    uint32_t dumy_tx_en;      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+    uint32_t dumy_bytes;      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+    uint32_t dumy_bits;       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+};
+
+
+/* SPI NAND REGISTER */
+#define REG_PROTECTION              0xA0
+#define REG_FEATURE                 0xB0
+#define ECC_EN                      (0x1<<4)
+#define REG_BUF_WINBOND             (0x1<<3)
+#define HSE_EN                      (0x1<<1)
+#define QE                          (0x1<<0)
+#define REG_STATUS                  0xC0
+#define ECC_ERR_BIT                 (0x4)        /* ECC ERR */
+#define ECC_ERR_MASK                (0x3<<4)     /* ECC ERR */
+#define P_FAIL                      (0x1<<3)     /* program fail*/
+#define E_FAIL                      (0x1<<2)     /* erase fail */
+#define WEL                         (0x1<<1)     /* Write Enable Latch */
+#define OIP                         (0x1<<0)     /* Operation In Progress */
+
+#define WRAP_SIZE_MAIN_OOB         (0x0<<12)      /* 2048 +64 = 2112 */
+#define WRAP_SIZE_MAIN             (0x4<<12)      /* 2048 */
+#define WRAP_SIZE_OOB			   (0x8<<12)      /* 64 */
+#define WRAP_SIZE_MINI			   (0xC<<12)      /* 16 */
+
+#define SPI_NAND_BUF_SIZE		(NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE)
+
+struct spi_nand_buf {
+	uint32_t head;
+	uint32_t tail;
+	uint8_t *buf;
+/*	uint8_t buf[SPI_NAND_BUF_SIZE];*/
+	dma_addr_t dma_buf;
+};
+
+struct spi_nand_ctrl_info {
+	int 	(*reset)(void);
+	int 	(*switch_die)(unsigned char die);
+	int 	(*read_id)(uint32_t reg_addr, uint8_t *value, uint8_t len);
+	int 	(*get_feature)(uint32_t reg_addr, uint8_t *value);
+	int 	(*set_feature)(uint32_t reg_addr, uint8_t *value);
+	int 	(*read_page_to_cache)(uint32_t page_addr);
+	int 	(*read_from_cache)(uint32_t column_addr, uint32_t len, uint8_t *buf);
+	int 	(*page_load)(uint32_t column_addr, uint8_t* buf, uint32_t page_len, 
+						uint8_t *oob, uint32_t oob_len);
+	int 	(*program_exec)(uint32_t page_addr);
+	int 	(*write_enable)(void);
+	int 	(*write_disable)(void);
+	int 	(*erase)(uint32_t page_addr);
+};
+
+
+struct spi_nand_info {
+	struct mtd_info *mtd;       
+	struct nand_chip *nand;
+	struct spi_nand_ctrl_info *ctrl;
+	struct nand_flash_device_para *para; 	
+    struct spi_nand_buf buf;
+	int status;
+	uint32_t page;
+	uint32_t devnum;	/* represent how many nands connected */
+    uint32_t total_used_banks;
+    uint32_t pages_per_die;	
+	uint32_t totalblks;
+	uint32_t blksperchip;
+	uint32_t bbtskipbytes;
+};
+
+int spi_nand_register(struct spi_nand_info *spi_nand, struct spi_nand_ctrl_info *ctrl);
+void spi_nand_special_init(struct spi_nand_info *spi_nand);
+void spi_nand_debug_init(void);
+int spi_nand_common_init(struct spi_nand_info *spi_nand);
+inline unsigned int spi_nand_get_trans_mode(int rw_mode);
+
+#endif	/* __SPI_NAND_H__ */
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/spi_nand_debug.c b/boot/common/src/uboot/drivers/mtd/nand/spi_nand_debug.c
new file mode 100644
index 0000000..65d6423
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/spi_nand_debug.c
@@ -0,0 +1,465 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spi_nand_debug.c
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 
+*     Version:
+*     Author: 
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#include <malloc.h> 
+#include <errno.h>
+#include <asm/io.h> 
+#include <linux/mtd/mtd.h>
+#include <asm-generic/ioctl.h>
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/nand.h> 
+#include <linux/mtd/nand.h>
+#include <asm/arch/lsp_crpm.h>
+#include <drvs_gpio.h>
+#include "spi_nand.h"
+
+/* DEBUG */
+struct spi_debug_reg
+{
+    //0x00  VER_REG
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t rev:             16;    
+            uint32_t min:             8;    
+            uint32_t main:            8;                    
+        }BIT;            
+    }VER_REG;
+
+    //0x04  SFC_START
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t busy_start:       1; 
+            uint32_t rev:             31;    
+        }BIT;            
+    }SFC_START;
+
+    //0x08  SFC_EN
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t en:              1; 
+            uint32_t en_back:         1; 
+            uint32_t rev:             30;                    
+        }BIT;            
+    }SFC_EN;
+
+    //0x0C  SFC_CTRL0
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t wr_protect:                 1;    
+            uint32_t spi_mode:                 1;  
+            uint32_t rev:                   1;
+            uint32_t wdog_en:                  1;
+            uint32_t rx_dma_en:                     1;
+            uint32_t tx_dma_en:                1;
+            uint32_t rxfifo_thres:                 1;
+            uint32_t rev1:          3;
+            uint32_t txfifo_thres:               1;
+            uint32_t rev2:          3;
+            uint32_t rxfifo_clr:               1;
+            uint32_t txfifo_clr:               1;
+            uint32_t pause_en:               1;
+            uint32_t pause_clr:               1;
+            uint32_t rev3:               14;
+            
+        }BIT;            
+    }SFC_CTRL0;
+
+    //0x10  SFC_CTRL1
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t write_data_en:      1;    
+            uint32_t read_data_en:   1;  
+            uint32_t dummy_tx_en:  1;
+            uint32_t rev:               1; 
+            uint32_t addr_tx_en:               1;   
+            uint32_t rev1:              27; 
+        }BIT;            
+    }SFC_CTRL1;
+
+    //0x14  SFC_CTRL2 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t trans_mod:               1;    
+            uint32_t rev:                     1;  
+            uint32_t data_multi_en:           1;
+            uint32_t rev1:                    1; 
+            uint32_t addr_multi_en:           1;   
+            uint32_t addr_byte_num:           2; 
+            uint32_t rev2:                    1; 
+            uint32_t dummy_bit_num:           3;
+            uint32_t rev3:                    1; 
+            uint32_t dummy_byte_num:          4;
+            uint32_t rev4:                    16; 
+        }BIT;            
+    }SFC_CTRL2;
+   
+    //0x18  SFC_BYTE_NUM 
+    union
+    {
+        uint32_t VALUE;        
+    }SFC_BYTE_NUM;
+    
+    //0x1C  SFC_ADDR 
+    union
+    {
+        uint32_t VALUE;         
+    }SFC_ADDR;
+
+    //0x20  SFC_INS 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t cmd:                    8;    
+            uint32_t Reserved:               24;   
+        }BIT;            
+    }SFC_INS;
+
+    //0x24  SFC_TIMING 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t cs_desle:                4; 
+            uint32_t rev:                     2; 
+            uint32_t cs_hold:                 3; 
+            uint32_t rev1:                    2; 
+            uint32_t cs_setup:                3;   
+            uint32_t rev2:                    2; 
+            uint32_t rd_delay:                2; 
+            uint32_t rev3:                    14; 
+        }BIT;            
+    }SFC_TIMING;
+
+    //0x28  SFC_INT_EN 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t int_en_cmd_end:                  1;    
+            uint32_t int_en_fmt_err:                  1;  
+            uint32_t int_en_wdog_over:                1;
+            uint32_t rev1:                            1; 
+            uint32_t int_en_rx_over:                  1;   
+            uint32_t int_en_tx_underrun:               1;    
+            uint32_t int_en_rx_byd_thres:              1;  
+            uint32_t int_en_tx_byd_thres:             1;
+            uint32_t rev2:                            24; 
+
+        }BIT;            
+    }SFC_INT_EN;
+
+    //0x2C  SFC_INT_RAW 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t int_raw_cmd_end:                  1;    
+            uint32_t int_raw_fmt_err:                  1;  
+            uint32_t int_raw_wdog_over:                1;
+            uint32_t rev1:                            1; 
+            uint32_t int_raw_rx_over:                  1;   
+            uint32_t int_raw_tx_underrun:               1;    
+            uint32_t int_raw_rx_byd_thres:              1;  
+            uint32_t int_raw_tx_byd_thres:             1;
+            uint32_t rev2:                            24;   
+        }BIT;            
+    }SFC_INT_RAW;
+
+    //0x30  SFC_INT_SW_CLR 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t int_clr_cmd_end:                  1;    
+            uint32_t int_clr_fmt_err:                  1;  
+            uint32_t int_clr_wdog_over:                1;
+            uint32_t rev1:                            1; 
+            uint32_t int_clr_rx_over:                  1;   
+            uint32_t int_clr_tx_underrun:               1;    
+            uint32_t int_clr_rx_byd_thres:              1;  
+            uint32_t int_clr_tx_byd_thres:             1;
+            uint32_t rev2:                            24;   
+        }BIT;            
+    }SFC_INT_SW_CLR;
+
+    //0x34  SFC_SW 
+    union
+    {
+        uint32_t VALUE;
+        struct
+        {
+            uint32_t rev:                             1;    
+            uint32_t fmt_err:                         1;  
+            uint32_t wait_flag:                       1;
+            uint32_t sck_pause_flay:                  1; 
+            uint32_t rx_byd_thres:                    1;   
+            uint32_t tx_byd_thres:                    1;    
+            uint32_t rev2:                            2;  
+            uint32_t rx_fifo_cnt:                     5;
+            uint32_t rev3:                            3; 
+            uint32_t tx_fifo_cnt:                     5;
+            uint32_t rev4:                            11; 
+        }BIT;            
+    }SFC_SW;
+
+    //0x38  SFC_DATA 
+    union
+    {
+        uint32_t VALUE;           
+    }SFC_DATA;
+
+};
+
+#if	SPI_NAND_DEBUG
+struct spi_debug_reg *debug = NULL;
+#define spifc_debug(fmt,args...)	printf (fmt ,##args)
+#define spifc_debugX(level,fmt,args...) if (DEBUG>=level) printf(fmt,##args);
+#else
+#define spifc_debug(fmt,args...)
+#define spifc_debugX(level,fmt,args...)
+#endif	/* DEBUG */
+
+#if	SPI_NAND_DEBUG
+
+void spi_nand_read_oob( uint32_t off )
+{
+    struct spifc_nand_info *spifc = g_spifc;
+    uint8_t buf[64] = {0};
+    uint32_t i = 0;
+
+    spifc->page = off>>11;
+    
+    spifc_read_oob(spifc->mtd, spifc->nand, off>>11, 0);
+
+    memcpy(buf, spifc->nand->oob_poi, 64);  
+
+    for( i=0; i<64; i++ )
+    {   
+        printf("%x ",buf[i]);
+    }
+    printf("\n");
+
+					
+}
+
+void spi_nand_write_page_ops(uint32_t off)
+{
+    struct spifc_nand_info *spifc = g_spifc;
+    uint8_t buf[2112];
+    uint32_t i = 0;
+    uint8_t val = 0;
+    
+
+    for( i=0; i<2112; i++ )
+    {
+        buf[i] = val++;
+    }
+
+    struct mtd_oob_ops *ops = NULL;
+
+    memcpy(spifc->nand->oob_poi, buf, 64);  
+
+    spifc->page = off>>11;
+    
+    spifc_write_page(spifc->mtd, spifc->nand, buf, ops);
+
+					
+}
+
+void spi_nand_write_page(uint32_t off)
+{
+    struct spifc_nand_info *spifc = g_spifc;
+    uint8_t buf[2112];
+    uint32_t i = 0;
+    uint8_t val = 0;
+    
+    struct mtd_oob_ops *ops = NULL;
+    
+    for( i=0; i<2112; i++ )
+    {
+        buf[i] = val++;
+    }
+
+    memset(spifc->nand->oob_poi, 0xff, 64);  
+
+    spifc->page = off>>11;
+    
+    spifc_write_page(spifc->mtd, spifc->nand, buf, ops);
+
+					
+}
+
+void spi_nand_erase(uint32_t off)
+{
+    struct spifc_nand_info *spifc = g_spifc;
+
+    //printf("[SPI-NAND][spi_nand_erase][offset] = 0x%0x\n", off);
+
+    spifc_erase(spifc->mtd, off>>11);
+}
+
+
+void spi_nand_write_page_raw(uint32_t off)
+{
+    struct spifc_nand_info *spifc = g_spifc;
+    uint8_t buf[2112];
+    uint32_t i = 0;
+    uint8_t val = 0;
+    
+
+    for( i=0; i<2112; i++ )
+    {
+        buf[i] = val++;
+    }
+
+    memcpy(spifc->nand->oob_poi, buf, 64);  
+
+    spifc->page = off>>11;
+    
+    spifc_write_page_raw(spifc->mtd, spifc->nand, buf);
+					
+}
+
+
+int spi_nand_get_feture(void)
+{
+    int ret = 0;
+    uint8_t feature = 0xff;
+
+    ret = spi_fc_get_feature(REG_PROTECTION, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature][--ERROR--]\n");
+        goto error_spi_nand1;
+    }
+    printf("[SPI-NAND][PROTECTION] = 0x%0x\n", feature);
+
+    ret = spi_fc_get_feature(REG_FEATURE, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature][--ERROR--]\n");
+        goto error_spi_nand1;
+    }
+    printf("[SPI-NAND][FEATURE] = 0x%0x\n", feature);
+
+    ret = spi_fc_get_feature(REG_STATUS, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature][--ERROR--]\n");
+        goto error_spi_nand1;
+    }
+    printf("[SPI-NAND][STATUS] = 0x%0x\n", feature);
+    printf("\n");
+
+    return 0;    
+error_spi_nand1:
+    return ret;        
+}
+
+int spi_nand_set_ecc(void)
+{
+    int ret = 0;
+    uint8_t feature = 0xff;
+
+    ret = spi_fc_get_feature(REG_FEATURE, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature]\n");
+        goto error_spi_nand2;
+    }
+    feature |= ECC_EN;
+
+    ret = spi_fc_set_feature(REG_FEATURE, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_feature]\n");
+        goto error_spi_nand2;
+    }
+
+    printf("\n");
+
+    return 0; 
+    
+error_spi_nand2:
+    return ret;     
+}
+
+int spi_nand_clear_ecc(void)
+{
+    int ret = 0;
+    uint8_t feature = 0xff;
+
+    ret = spi_fc_get_feature(REG_FEATURE, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_get_feature]\n");
+        goto error_spi_nand3;
+    }
+    feature &= (~ECC_EN);
+
+    ret = spi_fc_set_feature(REG_FEATURE, &feature);
+    if( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_feature]\n");
+        goto error_spi_nand3;
+    }
+
+    printf("\n");
+
+    return 0; 
+    
+error_spi_nand3:
+    return ret;     
+}
+
+#endif
+
+void spi_nand_debug_init(void)
+{
+#if SPI_NAND_DEBUG
+	debug = (struct spi_debug_reg *)SYS_SPI_NAND_BASE;
+#endif
+}
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/spi_nand_devices.c b/boot/common/src/uboot/drivers/mtd/nand/spi_nand_devices.c
new file mode 100755
index 0000000..0eb00ea
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/spi_nand_devices.c
@@ -0,0 +1,330 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spi_nand_devices.c
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 
+*     Version:
+*     Author: 
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#include <malloc.h> 
+#include <errno.h>
+#include <asm/io.h> 
+#include <linux/mtd/mtd.h>
+#include <asm-generic/ioctl.h>
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/nand.h> 
+#include <linux/mtd/nand.h>
+#include <asm/arch/lsp_crpm.h>
+#include <drvs_gpio.h>
+#include "spi_nand.h"
+
+unsigned char  g_maf_id = 0;
+unsigned char  g_dev_id = 0;
+
+static struct nand_ecclayout nand_gd_oob_64 = {
+	.eccbytes = 0,
+	.oobfree = {{4,12},{20,12},{36,12},{52,12}}
+};
+
+static struct nand_ecclayout nand_gd_oob_128 = {
+	.eccbytes = 64,
+	.oobfree = {{4,12},{20,12},{36,12},{52,12}}
+};
+static struct nand_ecclayout nand_gd_oob_256 = {
+	.eccbytes = 128,
+	.oobfree = {{2,2},{16,4},{32,4},{48,4},{64,4},{80,4},{96,4},{112,4}}
+};
+
+static struct nand_ecclayout nand_paragon_oob_128 =
+{
+    .eccbytes = 52,
+	.oobfree = {{4,2}, {19,2}, {34,2}, {49,2}}
+};
+static struct nand_ecclayout nand_heyangtek_oob_128 =
+{
+    .eccbytes = 56,
+	.oobfree = {{2,16}, {32,18}, {64,18}, {96,18}}
+};
+
+ static struct nand_ecclayout nand_winbond_oob_64= {
+	 .eccbytes = 32,
+	 .oobfree = {{4,4}, {20,4}, {36,4}, {52,4}}
+ };
+ static struct nand_ecclayout nand_Toshiba_OOB_64= {
+	 .eccbytes = 64,
+	 .oobfree = {{2,62}}
+ };
+ 
+static struct nand_ecclayout nand_zetta_oob_64= {
+	 .eccbytes = 32,
+	 .oobfree = {{4,4}, {20,4}, {36,4}, {52,4}}
+ };
+
+static struct nand_ecclayout nand_dosilicon_oob_64= {
+	 .eccbytes = 32,
+	 .oobfree = {{4,4}, {20,4}, {36,4}, {52,4}}
+ };
+
+static struct nand_ecclayout nand_dosilicon_oob_128= {
+	 .eccbytes = 64,
+	 .oobfree = {{2,62}}
+ };
+
+static struct nand_ecclayout nand_fudanwei_oob_128= {
+	.eccbytes = 64,
+	.oobfree = {{2,62}}
+ };
+
+static struct nand_ecclayout nand_hosin_oob_64= {
+	 .eccbytes = 32,
+	 .oobfree = {{4,4}, {20,4}, {36,4}, {52,4}}
+ };
+
+static struct nand_ecclayout nand_emst_oob_64= {
+	 .eccbytes = 32,
+	 .oobfree = {{4,4}, {20,4}, {36,4}, {52,4}}
+ };
+
+static struct nand_ecclayout nand_foresee_oob_64= {
+	 .oobfree = {{2,62}}
+ };
+
+static struct nand_ecclayout nand_micron_oob_128= {
+	.eccbytes = 64,
+	.oobfree = {{32,8}, {40,8}, {48,8}, {56,8}}
+ };
+
+ static struct nand_ecclayout nand_esmt_oob_256= {
+	.oobfree = {{64,64}}
+ };
+
+static void	spi_nand_winbond_init(struct spi_nand_info *spi_nand)
+{
+	uint8_t dev_id = spi_nand->para->device_id;	
+	struct nand_chip *chip = spi_nand->nand;
+	struct mtd_info *mtd = spi_nand->mtd;
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+    uint8_t feature;
+	int ret;
+
+	if(mtd->oobsize==64 && mtd->writesize==2048)
+	{
+		chip->ecc.layout =&nand_winbond_oob_64;
+	}
+
+	if(dev_id == NAND_DEVID_WINBOND_2G)
+	{
+		if(ctrl->switch_die(1))
+			BUG();
+		spi_nand_common_init(spi_nand);
+		ctrl->get_feature(REG_FEATURE, &feature);
+		feature |= REG_BUF_WINBOND;
+	    ret = ctrl->set_feature(REG_FEATURE, &feature);
+	    if ( ret != 0 )
+	    {
+	        printf("[SPI-NAND][spi_fc_set_winbond]\n");
+	        BUG();
+	    }	
+
+		if(ctrl->switch_die(0))
+			BUG();
+	}
+
+	spi_nand_common_init(spi_nand);
+	ctrl->get_feature(REG_FEATURE, &feature);
+	feature |= REG_BUF_WINBOND;
+    ret = ctrl->set_feature(REG_FEATURE, &feature);
+    if ( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_winbond]\n");
+        BUG();
+    }	
+	
+}
+
+static void	spi_nand_toshiba_init(struct spi_nand_info *spi_nand)
+{
+	struct nand_chip *chip = spi_nand->nand;
+	struct mtd_info *mtd = spi_nand->mtd;
+	struct spi_nand_ctrl_info *ctrl = spi_nand->ctrl;
+    uint8_t feature;
+	int ret;
+
+	if(mtd->oobsize==64 && mtd->writesize==2048)
+	{
+		chip->ecc.layout =&nand_Toshiba_OOB_64;
+	}
+
+	ctrl->get_feature(REG_FEATURE, &feature);
+	feature |= HSE_EN;
+    ret = ctrl->set_feature(REG_FEATURE, &feature);
+    if ( ret != 0 )
+    {
+        printf("[SPI-NAND][spi_fc_set_toshiba]\n");
+        BUG();
+    }		
+}
+
+inline unsigned int spi_nand_get_trans_mode(int rw_mode)
+{
+	if(WRITE_TRANSFER_MODE == rw_mode)
+	{
+		if(g_maf_id == NAND_MFR_TOSHIBA)
+			return SINGLE_MODE;
+		else
+			return WR_MODE;
+	}		
+	else
+	{
+		if((g_maf_id == NAND_MFR_TOSHIBA) 
+			|| (g_maf_id == NAND_MFR_ZETTA)
+			|| (g_maf_id == NAND_MFR_DOSILICON)
+			|| (g_maf_id == NAND_MFR_PARAGON)
+			|| ((g_maf_id == NAND_MFR_GIGADEVICE) && (g_dev_id == NAND_DEVID_GD5F1GQ5R_1G))
+			|| (g_maf_id == NAND_MFR_HOSIN)
+			||((g_maf_id == NAND_MFR_EMST) && (g_dev_id == NAND_DEVID_EMST_F50D1G41LB_1G))
+			|| (g_maf_id == NAND_MFR_FORESEE)
+			|| (g_maf_id == NAND_MFR_MICRON))
+			return RDX4_MODE;
+		else
+			return RD_MODE;
+	}		
+}
+
+
+void spi_nand_special_init(struct spi_nand_info *spi_nand)
+{
+	uint8_t maf_id = spi_nand->para->manuf_id;
+	uint8_t dev_id = spi_nand->para->device_id;	
+	struct nand_chip *chip = spi_nand->nand;
+	struct mtd_info *mtd = spi_nand->mtd;
+
+	spi_nand->pages_per_die = (spi_nand->para->pages_per_block
+								*spi_nand->para->block_num)
+								/spi_nand->para->die_num;
+	g_maf_id = maf_id;
+	g_dev_id = dev_id;
+
+	switch(maf_id)
+	{
+	case NAND_MFR_HEYANGTEK:
+		if(mtd->oobsize == 128 && mtd->writesize==2048)
+		{
+			chip->bbt_td->offs = BBT_INFO_OOB_OFFSET_HEYANGTEK;
+			chip->bbt_td->veroffs = BBT_INFO_OOB_VER_OFFSET_HEYANGTEK;
+			chip->bbt_md->offs = BBT_INFO_OOB_OFFSET_HEYANGTEK;
+			chip->bbt_md->veroffs = BBT_INFO_OOB_VER_OFFSET_HEYANGTEK;
+			chip->ecc.layout =&nand_heyangtek_oob_128;
+		}
+		break;		
+	case NAND_MFR_PARAGON:
+		if(dev_id == NAND_DEVID_FDANWEI_1G)
+		{
+			if(mtd->oobsize==128 && mtd->writesize==2048)
+			{
+				chip->ecc.layout =&nand_fudanwei_oob_128;
+			}
+		}
+		else
+		{
+			if(mtd->oobsize == 128 && mtd->writesize==2048)
+		    {
+   				chip->bbt_td->offs = BBT_INFO_OOB_OFFSET_PARAGON;
+				chip->bbt_td->veroffs = BBT_INFO_OOB_VER_OFFSET_PARAGON;
+				chip->bbt_md->offs = BBT_INFO_OOB_OFFSET_PARAGON;
+				chip->bbt_md->veroffs = BBT_INFO_OOB_VER_OFFSET_PARAGON;
+				chip->ecc.layout =&nand_paragon_oob_128;
+		    }
+			else
+			{
+				BUG();
+			}				
+		}		
+		break;	
+	case NAND_MFR_GIGADEVICE:
+		if(dev_id == NAND_DEVID_EMST_F50D1G41LB_1G)
+		{
+			if(mtd->oobsize==64 && mtd->writesize==2048)
+			{
+				chip->ecc.layout =&nand_emst_oob_64;
+			}
+		}
+		else
+		{
+			if(mtd->oobsize==64 && mtd->writesize==2048)
+				chip->ecc.layout = &nand_gd_oob_64;		
+			else if(mtd->oobsize==128 && mtd->writesize==2048)
+				chip->ecc.layout = &nand_gd_oob_128;
+			else if(mtd->oobsize==256 && mtd->writesize==4096)
+				chip->ecc.layout = &nand_gd_oob_256;
+			else
+				BUG();
+		}
+		break;
+	case NAND_MFR_WINBOND:
+		spi_nand_winbond_init(spi_nand);
+		break;		
+	case NAND_MFR_TOSHIBA:
+		spi_nand_toshiba_init(spi_nand);
+		break;
+	case NAND_MFR_ZETTA:
+		if(mtd->oobsize==64 && mtd->writesize==2048)
+		{
+			chip->ecc.layout =&nand_zetta_oob_64;
+		}
+		break;
+	case NAND_MFR_DOSILICON:
+		if(mtd->oobsize==64 && mtd->writesize==2048)
+			chip->ecc.layout =&nand_dosilicon_oob_64;
+		else if(mtd->oobsize==128 && mtd->writesize==2048)
+			chip->ecc.layout = &nand_dosilicon_oob_128;
+		else
+			BUG();
+		break;
+	case NAND_MFR_HOSIN:
+		if(mtd->oobsize==64 && mtd->writesize==2048)
+		{
+			chip->ecc.layout =&nand_hosin_oob_64;
+		}
+		break;	
+	case NAND_MFR_FORESEE:
+		if(mtd->oobsize==64 && mtd->writesize==2048)
+		{
+			chip->ecc.layout =&nand_foresee_oob_64;
+		}
+		break;
+	case NAND_MFR_MICRON:
+		if(dev_id == 0x35)
+		{
+            if(mtd->oobsize==256 && mtd->writesize==4096)
+			{
+				chip->ecc.layout =&nand_esmt_oob_256;
+			}
+		}
+		else
+		{
+            if(mtd->oobsize==128 && mtd->writesize==2048)
+			{
+				chip->ecc.layout =&nand_micron_oob_128;
+			}
+		}
+		break;
+	default:
+		break;
+	}
+
+}
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/zftl_ecc.c b/boot/common/src/uboot/drivers/mtd/nand/zftl_ecc.c
new file mode 100644
index 0000000..f05d5d9
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/zftl_ecc.c
@@ -0,0 +1,118 @@
+#include <common.h>
+#include <linux/mtd/zftl_ecc.h>
+
+
+static const unsigned char column_parity_table[] = {
+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+	0xa9, 0xfc, 0xf0, 0xa5, 0xcc, 0x99, 0x95, 0xc0,
+	0xc0, 0x95, 0x99, 0xcc, 0xa5, 0xf0, 0xfc, 0xa9,
+	0x3c, 0x69, 0x65, 0x30, 0x59, 0x0c, 0x00, 0x55,
+	0x55, 0x00, 0x0c, 0x59, 0x30, 0x65, 0x69, 0x3c,
+	0x30, 0x65, 0x69, 0x3c, 0x55, 0x00, 0x0c, 0x59,
+	0x59, 0x0c, 0x00, 0x55, 0x3c, 0x69, 0x65, 0x30,
+	0xa5, 0xf0, 0xfc, 0xa9, 0xc0, 0x95, 0x99, 0xcc,
+	0xcc, 0x99, 0x95, 0xc0, 0xa9, 0xfc, 0xf0, 0xa5,
+	0x0c, 0x59, 0x55, 0x00, 0x69, 0x3c, 0x30, 0x65,
+	0x65, 0x30, 0x3c, 0x69, 0x00, 0x55, 0x59, 0x0c,
+	0x99, 0xcc, 0xc0, 0x95, 0xfc, 0xa9, 0xa5, 0xf0,
+	0xf0, 0xa5, 0xa9, 0xfc, 0x95, 0xc0, 0xcc, 0x99,
+	0x95, 0xc0, 0xcc, 0x99, 0xf0, 0xa5, 0xa9, 0xfc,
+	0xfc, 0xa9, 0xa5, 0xf0, 0x99, 0xcc, 0xc0, 0x95,
+	0x00, 0x55, 0x59, 0x0c, 0x65, 0x30, 0x3c, 0x69,
+	0x69, 0x3c, 0x30, 0x65, 0x0c, 0x59, 0x55, 0x00,
+};
+
+
+void zftl_ecc_calc_other(const unsigned char *data, unsigned n_bytes,
+			  struct zftl_ecc_other *ecc_other)
+{
+	unsigned int i;
+
+	unsigned char col_parity = 0;
+	unsigned line_parity = 0;
+	unsigned line_parity_prime = 0;
+	unsigned char b;
+
+	for (i = 0; i < n_bytes; i++) {
+		b = column_parity_table[*data++];
+		col_parity ^= b;
+
+		if (b & 0x01) {
+			/* odd number of bits in the byte */
+			line_parity ^= i;
+			line_parity_prime ^= ~i;
+		}
+
+	}
+
+	ecc_other->col_parity = (col_parity >> 2) & 0x3f;
+	ecc_other->line_parity = line_parity;
+	ecc_other->line_parity_prime = line_parity_prime;
+}
+
+int zftl_ecc_correct_other(unsigned char *data, unsigned n_bytes,
+			    struct zftl_ecc_other *read_ecc,
+			    const struct zftl_ecc_other *test_ecc)
+{
+	unsigned char delta_col;	/* column parity delta */
+	unsigned delta_line;	/* line parity delta */
+	unsigned delta_line_prime;	/* line parity delta */
+	unsigned bit;
+
+	delta_col = read_ecc->col_parity ^ test_ecc->col_parity;
+	delta_line = read_ecc->line_parity ^ test_ecc->line_parity;
+	delta_line_prime =
+	    read_ecc->line_parity_prime ^ test_ecc->line_parity_prime;
+
+	if ((delta_col | delta_line | delta_line_prime) == 0)
+		return 0;	/* no error */
+
+	if (delta_line == ~delta_line_prime &&
+	    (((delta_col ^ (delta_col >> 1)) & 0x15) == 0x15)) {
+		/* Single bit (recoverable) error in data */
+
+		bit = 0;
+
+		if (delta_col & 0x20)
+			bit |= 0x04;
+		if (delta_col & 0x08)
+			bit |= 0x02;
+		if (delta_col & 0x02)
+			bit |= 0x01;
+
+		if (delta_line >= n_bytes)
+			return -1;
+
+		data[delta_line] ^= (1 << bit);
+
+		return 1;	/* corrected */
+	}
+
+	if ((hweight32(delta_line) +
+	     hweight32(delta_line_prime) +
+	     hweight8(delta_col)) == 1) {
+		/* Reccoverable error in ecc */
+
+		*read_ecc = *test_ecc;
+		return 1;	/* corrected */
+	}
+
+	/* Unrecoverable error */
+
+	return -1;
+}
diff --git a/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.c b/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.c
new file mode 100755
index 0000000..683b2a3
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.c
@@ -0,0 +1,1491 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spifc.c
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 
+*     Version:
+*     Author: 
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#include <malloc.h> 
+#include <errno.h>
+#include <asm/io.h> 
+#include <linux/mtd/mtd.h>
+#include <asm-generic/ioctl.h>
+#include <config.h>
+#include <common.h>
+#include <command.h>
+#include <asm/arch/nand.h> 
+#include <linux/mtd/nand.h>
+#include <asm/arch/lsp_crpm.h>
+#include <drvs_gpio.h>
+#include "zxic_spifc.h"
+#include "spi_nand.h"
+#include <../drivers/dma/zx29_dma.h>
+
+extern struct nand_flash_device_para nand_flash_para[];
+extern struct mtd_info nand_info[];
+extern struct nand_chip nand_chip[];
+extern struct nand_flash_device_para *g_nand_dev_info;
+extern unsigned char  g_maf_id;
+extern unsigned char g_dev_id;
+struct spi_nand_info spifc_nand;
+struct spi_nand_info *g_spifc = &spifc_nand;
+
+#define	TRANS_USE_DMA	1
+
+/* SPI NAND FLASH COMMAND FORMAT TYPE */
+/********************************************************************************
+  [Instruction]                 |--write enable
+                                |--write disable
+                                |--reset
+
+  [Instruction]                 |--addr width is 24 bit
+  [addr]                        |--page read to cache
+                                |--block erase
+                                |--program execute
+
+  [Instruction]                 |--addr width is 8 bit
+  [addr]                        |--set feature
+  [data_tx]                     |--addr width is 16 bit
+                                |--program load(X1,X4)
+                                |--program load random data(X1,X4)
+                                |--program load random data quad IO
+
+  [Instruction]                 |--addr width is 8 bit
+  [addr]                        |--get feature
+  [data_rx]                     |--read id
+  
+  [Instruction]                 |--addr width is 16 bit
+  [addr]                        |--dummy cycel 8/4/2
+  [dummy]                       |--read form cache(X1,X2,X4)
+  [data_rx]                     |--read form cache(dual,quad)IO
+
+********************************************************************************/
+uint32_t spifc_get_reg_base(void)
+{
+	return (uint32_t)SYS_SPI_NAND_BASE;
+}
+
+/*******************************************************************************
+ * Function:    spifc_set_timing
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+void spifc_set_timing(uint32_t rd_delay, uint32_t cs_setup, 
+                                uint32_t cs_hold, uint32_t cs_desel)
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    spi->SFC_TIMING = 0;
+    spi->SFC_TIMING  |= ((rd_delay&0x3) << 16)|((cs_setup&0x7) <<11)|
+                        ((cs_hold&0x7) << 6)|((cs_desel&0xf) << 0);
+}
+
+/*******************************************************************************
+ * Function:    spifc_set_clk
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+void spifc_set_clk(void)
+{
+    uint32_t clk_reg = 0;
+
+	clk_reg = readl(SPIFC_CLKSEL);
+	clk_reg &= ~0x00000070;
+
+	if((g_maf_id == NAND_MFR_EMST) && (g_dev_id == NAND_DEVID_EMST_F50D1G41LB_1G))
+	{
+		clk_reg |= (0x3 << 4);       /*104MHz*/
+	}
+	else
+	{
+		clk_reg |= (0x2 << 4);       /*124.8MHz*/
+	}
+
+	writel(clk_reg, SPIFC_CLKSEL);  
+
+    return;
+}
+
+/*******************************************************************************
+ * Function:    spi_fc_enable
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_fc_enable(void)
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    if( spi->SFC_EN & FC_EN_BACK )
+        return;
+    
+    spi->SFC_EN |= FC_EN;  
+    spi->SFC_CTRL0 |= FC_SCLK_PAUSE_EN;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_disable
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_fc_disable( void )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    if( !(spi->SFC_EN & FC_EN_BACK) )
+        return;
+
+    spi->SFC_EN &= (~FC_EN);    
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_clear_fifo
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+void spi_fc_clear_fifo( void )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    spi->SFC_CTRL0 |= (FC_RXFIFO_THRES | FC_TXFIFO_THRES | 
+                       FC_RXFIFO_CLR | FC_TXFIFO_CLR);
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_config_ctrl
+ * Description: ÇåFIFOÊý¾Ý£¬²¢ÇÒÉèÖÃÊÇ·ñʹÓÃDMA´«Êä
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  
+ ********************************************************************************/
+static void spi_fc_config_ctrl( uint32_t dma )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    /* config DMA */
+    if( dma == FC_DMA_TX )
+        spi->SFC_CTRL0 |= FC_TX_DMA_EN;
+    else if( dma == FC_DMA_RX )
+        spi->SFC_CTRL0 |= FC_RX_DMA_EN;
+	else
+		spi->SFC_CTRL0 &= ~(FC_RX_DMA_EN|FC_TX_DMA_EN);
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_setup_cmd
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_fc_setup_cmd( struct spiflash_cmd_t *cmd, 
+                                                uint32_t addr, uint32_t len )
+{   
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    uint32_t wrap = 0;	
+    uint32_t tmp = 0;
+
+    /* ÃüÁîÂë */  
+    spi->SFC_INS = cmd->cmd;
+
+    /* Êý¾Ý³¤¶È */
+    if( len )
+        spi->SFC_BYTE_NUM = len - 1;
+    else
+        spi->SFC_BYTE_NUM = 0;  
+
+    switch( len )
+    {
+        case 2048:
+            wrap = WRAP_SIZE_MAIN;
+            break;
+        case 2112:
+            wrap = WRAP_SIZE_MAIN_OOB;
+            break;
+        case 64:
+            wrap = WRAP_SIZE_OOB;
+            break;
+        default:
+            wrap = 0;
+            break;
+    }
+
+    /* µØÖ·Âë */
+    switch( spi->SFC_INS )
+    {
+        case CMD_READ_FROM_CACHE:
+        //  case CMD_READ_FROM_CACHE_X2:
+        case CMD_READ_FROM_CACHE_X4:
+        case CMD_READ_FROM_CACHE_QIO:
+        case CMD_PROGRAM_LOAD:
+        case CMD_PROGRAM_LOAD_X4:
+            // case CMD_PROGRAM_LOAD_RANDOM:
+            //  case CMD_PROGRAM_LOAD_RANDOM_X4:
+            //case CMD_PROGRAM_LOAD_RANDOM_QIO:
+            addr |= wrap;
+            break;
+            
+        default:
+            addr = addr;
+            break;
+    }
+    spi->SFC_ADDR = addr;  
+
+    /* µØÖ·Âë¡¢¿ÕÖÜÆÚ¡¢¶Á/д ʹÄÜÉèÖà */
+    spi->SFC_CTRL1 = 0;
+    spi->SFC_CTRL1 = ((cmd->addr_tx_en << FC_ADDR_TX_EN) |  
+                      (cmd->dumy_tx_en << FC_DUMMY_TX_EN) | 
+                      (cmd->data_rx_en << FC_READ_DAT_EN) | 
+                      (cmd->data_tx_en << FC_WRITE_DAT_EN)); 
+                    
+
+    /* ¿ÕÖÜÆÚÊý¡¢µØÖ·¿í¶È(1£¬2£¬3£¬4×Ö½Ú)¡¢µØÖ·/Êý¾ÝÏß¶È¡¢´«Êäģʽ */
+    tmp = spi->SFC_CTRL2;
+    tmp &= 0x1f;
+    tmp |= ((cmd->dumy_bytes << FC_DUMMY_BYTE_NUM) |
+            (cmd->dumy_bits << FC_DUMMY_BIT_NUM) |
+            (cmd->addr_width << FC_ADDR_BYTE_NUM));
+    spi->SFC_CTRL2 = tmp;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_clear_int
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+ void spi_fc_clear_int( void )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    if(spi->SFC_INT_RAW & 0x2)
+        printf("Spi-Nand Int Format error!\n");
+
+    spi->SFC_INT_SW_CLR = 0xFF; //clear int ?
+    
+}
+
+/*******************************************************************************
+ * Function:    spi_nand_wait_cmd_end
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spi_fc_wait_cmd_end( void )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    uint32_t int_status = 0;
+    
+    while( spi->SFC_START & FC_BUSY ); 
+
+    while(!(spi->SFC_INT_RAW & FC_INT_RAW_CMD_END));
+    
+    int_status = spi->SFC_INT_RAW;
+    spi->SFC_INT_SW_CLR = int_status;           /* ÇåÖÐ¶Ï */   
+
+    if( int_status & FC_INT_RAW_TX_UNDERRUN )
+    {   
+        printf("    Spi-fc Nand Failed: TX_UNDERRUN\n");
+        return -EIO;
+    }
+
+    if( int_status & FC_INT_RAW_RX_OVERRUN )
+    {   
+        printf("    Spi-fc Nand Failed: RX_UNDERRUN\n");
+        return -EIO;
+    }
+
+    if( int_status & FC_INT_RAW_WDOG_OVERRUN )
+    {   
+        printf("    Spi-fc Nand Failed: WDOG_OVERRUN\n");
+        return -EIO;
+    }
+
+    if( int_status & FC_INT_RAW_FMT_ERR )
+    {   
+        printf("    Spi-fc Nand Failed: FMT_ERR\n");
+        return -EIO;
+    }
+
+    if( int_status & FC_INT_RAW_CMD_END )
+    {   
+        return SUCCESS;
+    }
+    else
+    {
+        return -EIO;
+    }  
+    
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_read_fifo_one_byte
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  only for:
+                [Instruction]                 |--addr width is 8 bit
+                [addr]                        |--get feature
+                [data_rx]   
+ ********************************************************************************/
+static int spi_fc_read_fifo_one_byte( uint8_t *value )
+{
+    uint32_t sw = 0;
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+
+    sw = spi->SFC_SW;
+    
+    if( ((sw >> FC_RX_FIFO_CNT) & FC_RX_FIFO_CNT_MASK) != 1 )
+    {
+        printf("[SPI-NAND][-ERROR-][spi_fc_read_fifo_one_byte][SFC_SW] = 0x%0x\n", sw );
+        return -EIO;
+    }
+
+    *value = (uint8_t)spi->SFC_DATA;   
+    
+    return SUCCESS;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_read_fifo
+ * Description:´Ófifo¶ÁÊý¾Ý£¬Ö±µ½¶Áµ½ÆÚÍûµÄ¸öÊý 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static uint32_t spi_fc_read_fifo( uint32_t len, uint8_t *buf )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    uint32_t *p = (uint32_t *)buf;
+    uint32_t cnt = 0;
+   
+    while(cnt < ((len+3)>>2))  
+    {
+        if(spi->SFC_SW & (FC_RX_FIFO_CNT_MASK<<FC_RX_FIFO_CNT))//rx fifo not empty
+        {           
+            p[cnt++]= spi->SFC_DATA;
+        }
+    }
+
+
+    return (cnt<<2);
+
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_write_fifo_one_byte
+ * Description:
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+uint32_t spi_fc_write_fifo_one_byte(uint32_t len, uint8_t* value)
+{
+    uint32_t sw = 0;
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+
+    sw = spi->SFC_SW;
+    
+    if( ((sw >> FC_TX_FIFO_CNT) & FC_TX_FIFO_CNT_MASK) != 0x10 )/* ²»Îª¿ÕÔò³ö´í */
+    {
+        printf("[SPI-NAND][-ERROR-][spi_fc_write_fifo_one_byte][SFC_SW] = 0x%0x\n", sw );
+        return -EIO;
+    }
+
+    spi->SFC_DATA = (uint32_t)(*value);   
+    
+    return SUCCESS;
+}
+
+
+/*******************************************************************************
+ * Function:    spi_fc_write_fifo
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static uint32_t spi_fc_write_fifo(uint8_t* buf, uint32_t buf_len, uint8_t* oob, uint32_t oob_len)
+{
+    uint32_t *main_area = (uint32_t *)buf;
+    uint32_t *spear_area = (uint32_t *)oob;
+    uint32_t cnt = 0;
+    volatile struct spifc_reg_t* spi = (struct spifc_reg_t*)spifc_get_reg_base();
+
+    while(cnt < (buf_len>>2))  
+    {
+        if(spi->SFC_SW & (FC_TX_FIFO_CNT_MASK<<FC_TX_FIFO_CNT))//tx fifo not full
+        {           
+            spi->SFC_DATA = main_area[cnt++];
+        }
+    }
+
+    cnt = 0;
+    if( oob != NULL )
+    {
+		while(cnt < (oob_len>>2))  			
+        {
+            if(spi->SFC_SW & (FC_TX_FIFO_CNT_MASK<<FC_TX_FIFO_CNT))//tx fifo not full
+            {           
+                spi->SFC_DATA = spear_area[cnt++];
+            }
+        }
+    }
+
+    return (cnt<<2);
+}
+/*******************************************************************************
+ * Function:    spi_fc_start
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_fc_start( void )
+{
+    volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    spi->SFC_START |= FC_START;
+}
+
+/*******************************************************************************
+ * Function:    spifc_get_feature
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:   [Instruction]                 |--addr width is 8 bit
+             [addr]                        |--get feature
+             [data_rx]                     |--read id
+            
+ ********************************************************************************/
+ static int spifc_get_feature( uint32_t reg_addr, uint8_t *value)
+ {   
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    
+    struct spiflash_cmd_t cmd = {   CMD_GET_FEATURE,        /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_8,     /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    1,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, reg_addr, 1);        /* ÃüÁîÅäÖà */      
+        spi_fc_start();                             /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();                /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        ret = spi_fc_read_fifo_one_byte(value);          /* ¶ÁÈ¡Êý¾Ý */
+        if( ret != SUCCESS ) 
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+        
+        break;        
+    } while( --retries != 0 );
+     
+    return ret;
+}
+
+ 
+/*******************************************************************************
+ * Function:    spifc_set_feature
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:   [Instruction]                 |--addr width is 8 bit
+             [addr]                        |--set feature
+             [data_tx]              
+            
+ ********************************************************************************/
+ static int spifc_set_feature( uint32_t reg_addr, uint8_t *value)
+{   
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    
+    struct spiflash_cmd_t cmd = {   CMD_SET_FEATURE,        /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_8,     /* µØÖ·Â볤¶È */
+                                    1,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, reg_addr, 1);        /* ÃüÁîÅäÖà */    
+        spi_fc_write_fifo_one_byte(1, value);
+        spi_fc_start();                             /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();                /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+        break;        
+    } while( --retries != 0 );
+     
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spifc_reset
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spifc_reset( void )
+{   
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint8_t status = 0;
+    struct spiflash_cmd_t cmd = {   CMD_RESET,              /* Ö¸ÁîÂë */
+                                    0,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    0,                      /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_setup_cmd(&cmd, 0, 0);       /* ÃüÁîÅäÖà */
+        spi_fc_start();                     /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();        /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )    
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+		/* µÈ´ýRESETÖ´ÐÐÍê³É */ //sunzhaoxing must wait
+		do
+		{
+			spifc_get_feature(REG_STATUS, &status); 
+		}
+		while ( status & OIP); 
+
+        break;
+    } while( --retries != 0 );
+     
+    return ret;
+}
+
+static int spifc_switch_die(unsigned char die)
+{
+	int ret = SUCCESS;
+	uint8_t value = die;
+    uint8_t status = 0;
+	uint32_t retries = SPI_NAND_RETRIE;
+		
+	struct spiflash_cmd_t cmd = {	CMD_WINBOND_DIE_SWITCH, /* ??¨¢??? */
+									0,						/* ¦Ì??¡¤??¡¤¡é?¨ª¨º1?¨¹ */
+									0,						 /* ¦Ì??¡¤??3¡è?¨¨ */
+									1,						/* ¡¤¡é?¨ª¨ºy?Y¨º1?¨¹---D¡ä */
+									0,						/* ?¨®¨º?¨ºy?Y¨º1?¨¹---?¨¢ */
+									0,						/* ???D¦Ì¨¨¡äy?¨¹?¨²¨º1?¨¹ */
+									0,						/* ???D¦Ì¨¨¡äy?¨¹?¨² x8 */
+									0						/* ???D¦Ì¨¨¡äy?¨¹?¨² x1 */
+								};
+
+	
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);       
+		spi_fc_setup_cmd(&cmd, 0, 1);		/* ?¨¹¨¢????? */	  
+        spi_fc_write_fifo_one_byte(1, &value);
+        spi_fc_start();                             /* ?a¨º?¡ä?¨º? */
+        ret = spi_fc_wait_cmd_end();                /* ¦Ì¨¨¡äy?¨¹¨¢??¨¢¨º? */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ¡ä?¨º?¨º¡ì¡ã¨¹¡ê??¨¢¨º?¡À?¡ä??-?¡¤2¡é??¡ä? */
+        }
+        break;        
+    } while( --retries != 0 );
+
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spifc_write_enable
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spifc_write_enable( void )
+{      
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+	uint8_t status = 0;
+    struct spiflash_cmd_t cmd = {   CMD_WRITE_ENABLE,       /* Ö¸ÁîÂë */
+                                    0,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    0,                      /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+	while(1)
+	{
+		do
+		{
+			spi_fc_clear_fifo();
+			spi_fc_clear_int();
+			spi_fc_config_ctrl(FC_DMA_NONE);
+			spi_fc_setup_cmd(&cmd, 0x0, 0); 		 /* ÃüÁîÅäÖà */
+			spi_fc_start(); 						 /* ¿ªÊ¼´«Êä */
+			ret = spi_fc_wait_cmd_end(); 			/* µÈ´ýÃüÁî½áÊø */
+			if ( ret != SUCCESS )
+			{
+				continue;	/* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+			}
+	
+			break;
+		}
+		while ( --retries != 0 );
+	
+		/* µÈ´ýÉÏ´ÎÃüÁîÖ´ÐÐÍê³É */
+		do
+		{
+			spifc_get_feature(REG_STATUS, &status); 
+		}
+		while ( status & OIP); 
+	
+		if(status&WEL)//sunzhaoxing
+			break;
+	
+		retries = SPI_NAND_RETRIE;
+			
+	}
+     
+    return ret;
+}
+
+
+/*******************************************************************************
+ * Function:    spifc_write_disable
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static int spifc_write_disable( void )
+{  
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    struct spiflash_cmd_t cmd = {   CMD_WRITE_DISABLE,      /* Ö¸ÁîÂë */
+                                    0,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    0,                      /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, 0x0, 0);          /* ÃüÁîÅäÖà */      
+        spi_fc_start();                          /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();             /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+       break;        
+    } while( --retries != 0 );
+     
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spi_fc_setup_tansmod
+ * Description:
+ * Parameters:
+ *   Input:
+ *
+ *   Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+static void spi_fc_setup_tansmod(uint32_t mod)
+{
+    uint32_t tmpReg;
+    volatile struct spifc_reg_t* spi  = (struct spifc_reg_t*)spifc_get_reg_base();
+
+    tmpReg = spi->SFC_CTRL2;
+    tmpReg &= 0xffffffe0;
+
+    switch (mod)
+    {
+        case RDQIO_MODE:
+            tmpReg |= FC_ADDR_MULTI_LINE_EN |
+                      FC_DAT_MULTI_LINE_EN |
+                      FC_TRANS_MOD;
+            break;
+
+        case PLX4_MODE:
+        case RDX4_MODE:
+            tmpReg |= FC_DAT_MULTI_LINE_EN | FC_TRANS_MOD;
+            break;
+
+        /* case DUAL_MODE:
+             tmpReg |=FC_DAT_MULTI_LINE_EN;
+          break; */
+
+        default:   /*signle mode*/
+            break;
+    }
+
+    spi->SFC_CTRL2 = tmpReg;
+
+}
+
+/*******************************************************************************
+ * Function:    spifc_read_id
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]                 |--addr width is 8 bit
+            [addr]                        |--get feature
+            [data_rx]                     |--read id
+ ********************************************************************************/
+
+static int spifc_read_id(uint32_t reg_addr, uint8_t *value, uint8_t len)
+{   
+    int ret = 0;
+    uint32_t retries = SPI_NAND_RETRIE;
+    
+    struct spiflash_cmd_t cmd = {   CMD_READ_ID,            /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_8,     /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    1,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, reg_addr, len);        /* ÃüÁîÅäÖà */      
+        spi_fc_start();                             /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();                /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            //spi_fc_disable();
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        ret = spi_fc_read_fifo(len,value);     /* ¶ÁÈ¡Êý¾Ý */
+        if( ret != SUCCESS ) 
+        {
+            //spi_fc_disable();
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        break;        
+    } while( --retries != 0 );
+    
+    return 0;
+}
+
+
+/*******************************************************************************
+ * Function:    spifc_erase
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]       |--addr width is 24 bit(page/block addr!!!!)
+            [addr]            
+                                |--block erase
+                              
+ ********************************************************************************/
+static int spifc_erase(uint32_t page_addr)
+{   
+    //volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint8_t status = 0;
+    
+    struct spiflash_cmd_t cmd = {   CMD_BLOCK_ERASE,        /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_24,    /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, page_addr, 0);   /* ÃüÁîÅäÖà */      
+        spi_fc_start();                          /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();             /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+        do
+        {
+            ret = spifc_get_feature(REG_STATUS, &status);
+        }while( status & OIP);
+            if( status & E_FAIL )
+            {   
+                printf("[SPI-NAND][ERASE][status] = 0x%0x\n", status);
+                return NAND_STATUS_FAIL;
+            }            
+       
+        break;        
+    }
+     while( --retries != 0 );
+     
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spifc_read_page_to_cache
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]       |--addr width is 24 bit(page/block addr!!!!)
+            [addr]              |--page read
+                                |--block erase
+                                |--program execute
+ ********************************************************************************/
+static int spifc_read_page_to_cache(uint32_t page_addr)
+{   
+    //volatile struct spifc_reg_t* spi	= (struct spifc_reg_t*)spifc_get_reg_base();
+    
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint8_t status = 0;
+    
+    struct spiflash_cmd_t cmd = {   CMD_READ_PAGE_TO_CACHE, /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_24,    /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, page_addr, 0);   /* ÃüÁîÅäÖà */      
+        spi_fc_start();                          /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();             /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+        do
+        {
+            spifc_get_feature(REG_STATUS, &status);
+        }while( status & OIP);
+
+        break;        
+    }
+     while( --retries != 0 );
+     
+    return ret;
+}
+
+#if TRANS_USE_DMA
+/*
+ * config dma 
+ *
+ * dir  -- FC_DMA_RX/FC_DMA_TX
+ */ 
+static void spifc_config_dma(uint8_t *buf, uint32_t len, int dir)
+{
+	dma_channel_def chan_para;
+	dma_peripheral_id channel_id = 0;
+    volatile struct spifc_reg_t* spi = (struct spifc_reg_t*)spifc_get_reg_base();
+
+	if(dir == FC_DMA_RX)
+	{	
+		chan_para.src_addr = &(spi->SFC_DATA);
+		chan_para.dest_addr = (unsigned int)buf;
+		channel_id	= DMA_CH_SPIFC_RX;	
+		chan_para.dma_control.tran_mode = TRAN_PERI_TO_MEM;
+		
+	}
+	else if(dir == FC_DMA_TX)
+	{	
+		chan_para.src_addr = (unsigned int)buf;
+		chan_para.dest_addr = &(spi->SFC_DATA);
+		channel_id	= DMA_CH_SPIFC_TX;	
+		chan_para.dma_control.tran_mode = TRAN_MEM_TO_PERI;
+	}
+	else
+		BUG();
+		
+	chan_para.count = len;
+	chan_para.dma_control.irq_mode  		= DMA_ALL_IRQ_DISABLE;
+	chan_para.dma_control.src_burst_size	= DMA_BURST_SIZE_32BIT;
+	chan_para.dma_control.src_burst_len		= DMA_BURST_LEN_8;
+	chan_para.dma_control.dest_burst_size 	= DMA_BURST_SIZE_32BIT;
+	chan_para.dma_control.dest_burst_len	= DMA_BURST_LEN_8;
+	
+	dma_config(channel_id, &chan_para);	
+	
+	dma_start(channel_id, NULL, NULL);
+}
+
+static int spifc_wait_dma_done(int dir)
+{
+	dma_peripheral_id channel_id = 0;
+
+	if(dir == FC_DMA_RX)
+		channel_id = DMA_CH_SPIFC_RX;
+	else if(dir == FC_DMA_TX)
+		channel_id = DMA_CH_SPIFC_TX;
+	else
+		BUG();
+
+	while(1)
+    {
+        if(dma_get_status(channel_id) == DMA_TRANSFER_DONE)
+            break;
+    }
+
+	dma_disable_channel(channel_id);
+	
+	return 0;
+}
+#endif
+/*******************************************************************************
+ * Function:    spifc_read_from_cache
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]                 |--addr width is 16 bit
+            [addr]                        |--dummy cycel 8/4/2
+            [dummy]                       |--read form cache(X1,X2,X4)
+            [data_rx]                     |--read form cache(dual,quad)IO
+ ********************************************************************************/
+static int spifc_read_from_cache(uint32_t column_addr, uint32_t len, uint8_t *buf)
+{   
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint32_t len_tmp = 0;
+    uint32_t trans_mode = spi_nand_get_trans_mode(READ_TRANSFER_MODE);
+    struct spiflash_cmd_t cmd;
+
+    spi_fc_setup_tansmod(trans_mode);
+
+    switch (trans_mode)
+    {
+        case RDQIO_MODE:
+            cmd.cmd = CMD_READ_FROM_CACHE_QIO;       /* Ö¸ÁîÂë */
+            cmd.dumy_bytes = 0;                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+            if(g_maf_id == 0xEF)
+			{
+				cmd.dumy_bits = 4;                      /* ¿ÕÏеȴýÖÜÆÚ x1 */
+			}
+			else
+			{
+				cmd.dumy_bits = 2;						 /* ¿ÕÏеȴýÖÜÆÚ x1 */
+			}
+            break;
+        case RDX4_MODE: 
+            cmd.cmd = CMD_READ_FROM_CACHE_X4;        /* Ö¸ÁîÂë */
+            cmd.dumy_bytes = 1;                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+            cmd.dumy_bits = 0;                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+            break;
+        default:
+            cmd.cmd = CMD_READ_FROM_CACHE;           /* Ö¸ÁîÂë */
+            cmd.dumy_bytes = 1;                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+            cmd.dumy_bits = 0;                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+            break;
+    }
+	
+    cmd.addr_tx_en = 1;                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+    cmd.addr_width = FC_ADDR_BYTE_NUM_16;    /* µØÖ·Â볤¶È */
+    cmd.data_tx_en = 0;                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+    cmd.data_rx_en = 1;                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+    cmd.dumy_tx_en = 1;                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_setup_cmd(&cmd, column_addr, len);       /* ÃüÁîÅäÖà */      
+
+#if TRANS_USE_DMA
+		spifc_config_dma(buf, len, FC_DMA_RX);		
+		spi_fc_config_ctrl(FC_DMA_RX);
+		spi_fc_start(); 								/* ¿ªÊ¼´«Êä */		
+#else
+		spi_fc_config_ctrl(FC_DMA_NONE);        
+		spi_fc_start(); 								/* ¿ªÊ¼´«Êä */		
+
+		len_tmp = spi_fc_read_fifo(len, buf);			/* ¶ÁÈ¡Êý¾Ý */
+		if( len_tmp != len ) 
+		{
+			ret = -2;
+			continue;	/* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+		}
+#endif
+
+        ret = spi_fc_wait_cmd_end();                    /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+#if TRANS_USE_DMA
+		spifc_wait_dma_done(FC_DMA_RX);
+		spi_fc_config_ctrl(FC_DMA_NONE); 
+#endif
+        break;        
+    } while( --retries != 0 );
+
+    spi_fc_setup_tansmod(SINGLE_MODE);
+	
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spifc_page_load
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]                 
+            [addr]                        
+            [data_tx]                     |--addr width is 16 bit
+                                          |--program load(X1,X4)
+ ********************************************************************************/
+static int spifc_page_load(uint32_t column_addr, uint8_t* buf, uint32_t page_len, 
+						uint8_t *oob, uint32_t oob_len)
+{   
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint32_t trans_mode = spi_nand_get_trans_mode(WRITE_TRANSFER_MODE);
+    struct spiflash_cmd_t cmd;
+
+    switch (trans_mode)
+    {
+        case PLX4_MODE:
+             cmd.cmd = CMD_PROGRAM_LOAD_X4;  /* Ö¸ÁîÂë */
+           break;
+        default:
+             cmd.cmd = CMD_PROGRAM_LOAD;
+            break;
+    }
+	
+    cmd.addr_tx_en = 1;                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+    cmd.addr_width = FC_ADDR_BYTE_NUM_16;    /* µØÖ·Â볤¶È */
+    cmd.data_tx_en = 1;                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+    cmd.data_rx_en = 0;                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+    cmd.dumy_tx_en = 0;                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+    cmd.dumy_bytes = 0;                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+    cmd.dumy_bits = 0;                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                              
+    do
+    {
+		spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_setup_cmd(&cmd, column_addr, page_len+oob_len);       /* ÃüÁîÅäÖà */ 
+#if TRANS_USE_DMA
+		if(oob)
+			spifc_config_dma(buf, page_len+oob_len, FC_DMA_TX);
+		else
+			spifc_config_dma(buf, page_len, FC_DMA_TX);
+		spi_fc_config_ctrl(FC_DMA_TX);
+		spi_fc_setup_tansmod(trans_mode);		
+		spi_fc_start(); 								/* ¿ªÊ¼´«Êä */		
+#else
+		spi_fc_config_ctrl(FC_DMA_NONE);      
+		spi_fc_setup_tansmod(trans_mode);
+        spi_fc_start();                                 /* ¿ªÊ¼´«Êä */
+        spi_fc_write_fifo(buf, page_len, oob, oob_len);               /*±£Ö¤´«Íê*/
+#endif		
+        ret = spi_fc_wait_cmd_end();                    /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+#if TRANS_USE_DMA
+		spifc_wait_dma_done(FC_DMA_TX);
+		spi_fc_config_ctrl(FC_DMA_NONE); 
+#endif
+	
+        break;        
+    } while( --retries != 0 );
+     
+    spi_fc_setup_tansmod(SINGLE_MODE);
+    return ret;
+}
+
+
+/*******************************************************************************
+ * Function:    spifc_program_exec
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:  [Instruction]       |--addr width is 24 bit(page/block addr!!!!)
+            [addr]              |--program execute
+                                                               
+ ********************************************************************************/
+static int spifc_program_exec(uint32_t page_addr)
+{    
+    int ret = SUCCESS;
+    uint32_t retries = SPI_NAND_RETRIE;
+    uint8_t status = 0;
+    
+    struct spiflash_cmd_t cmd = {   CMD_PROGRAM_EXECUTE,    /* Ö¸ÁîÂë */
+                                    1,                      /* µØÖ·Âë·¢ËÍʹÄÜ */
+                                    FC_ADDR_BYTE_NUM_24,    /* µØÖ·Â볤¶È */
+                                    0,                      /* ·¢ËÍÊý¾ÝʹÄÜ---д */
+                                    0,                      /* ½ÓÊÕÊý¾ÝʹÄÜ---¶Á */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚʹÄÜ */
+                                    0,                      /* ¿ÕÏеȴýÖÜÆÚ x8 */
+                                    0                       /* ¿ÕÏеȴýÖÜÆÚ x1 */
+                                };
+    do
+    {
+        spi_fc_clear_fifo();
+        spi_fc_clear_int();
+        spi_fc_config_ctrl(FC_DMA_NONE);        
+        spi_fc_setup_cmd(&cmd, page_addr, 0);       /* ÃüÁîÅäÖà */      
+        spi_fc_start();                             /* ¿ªÊ¼´«Êä */
+        ret = spi_fc_wait_cmd_end();                /* µÈ´ýÃüÁî½áÊø */
+        if( ret != SUCCESS )
+        {
+            continue;   /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ­»·²¢ÖØ´« */
+        }
+
+        /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+        do
+        {
+            spifc_get_feature(REG_STATUS, &status);
+        }while( status & OIP);
+		
+        if( status & P_FAIL )
+        {
+            return NAND_STATUS_FAIL;
+        }   
+         break;  
+    }
+     while( --retries != 0 );
+     
+    return ret;
+}
+
+/*******************************************************************************
+ * Function:    spifc_hw_init
+ * Description: 
+ * Parameters:
+ *	 Input:
+ *
+ *	 Output:
+ *
+ * Returns:
+ *
+ *
+ * Others:
+ ********************************************************************************/
+int winbond_dev_id2 = 0;
+
+static void spifc_hw_init(struct spi_nand_info *spi_nand)
+{
+	uint8_t id[4];
+    struct nand_flash_device_para * table = nand_flash_para;
+    struct nand_flash_timing *time = NULL;
+
+    zDrvGpio_SetFunc(GPIO98, GPIO98_SPIFC_DATA3);
+	zDrvGpio_SetFunc(GPIO97, GPIO97_SPIFC_DATA2);
+	zDrvGpio_SetFunc(GPIO96, GPIO96_SPIFC_DATA1);
+	zDrvGpio_SetFunc(GPIO95, GPIO95_SPIFC_DATA0);
+	zDrvGpio_SetFunc(GPIO93,GPIO93_SPIFC_CS);
+	zDrvGpio_SetFunc(GPIO94,GPIO94_SPIFC_CLK);
+
+	spi_fc_enable();
+
+    spifc_read_id(0x0,id, 3);
+	spi_fc_disable();
+	winbond_dev_id2 = id[2];
+    printf("\n[SPI-NAND]: maf_id = 0x%0x\n", id[0]);
+    printf("[SPI-NAND]: dev_id = 0x%0x\n", id[1]);
+	printf("[SPI-NAND]: dev_id = 0x%0x\n", id[2]);
+    
+    for (; table->manuf_id != 0; table++)
+    {
+        if( table->manuf_id == 0 )
+        {
+            printf("Can not find the nand chip id...\n");
+            BUG();
+        }
+        if ((id[0] == table->manuf_id) && (id[1] == table->device_id)&&(table->res_id == 0) )
+        {
+            break;
+        }		
+    }
+
+	spi_nand->para = table;
+
+    g_nand_dev_info = table;	/* maybe remove later */
+    
+    time = &(table->nand_timeing);
+    spifc_set_clk();
+    spi_fc_enable();
+    spifc_set_timing(time->Twhr, time->Trr1, time->Tadl, time->Trr2 );
+}
+
+static struct spi_nand_ctrl_info spifc_ops = 
+{
+	.reset		 		= spifc_reset,
+	.switch_die			= spifc_switch_die,
+	.read_id	 		= spifc_read_id,
+	.get_feature 		= spifc_get_feature,
+	.set_feature 		= spifc_set_feature,
+	.read_page_to_cache	= spifc_read_page_to_cache,
+	.read_from_cache	= spifc_read_from_cache,
+	.page_load 			= spifc_page_load,
+	.program_exec		= spifc_program_exec,
+	.write_enable		= spifc_write_enable,
+	.write_disable		= spifc_write_disable,
+	.erase 				= spifc_erase,
+};
+
+int board_nand_init_spifc(struct nand_chip *nand)
+{
+	int ret = -1; 
+	struct spi_nand_info *spifc = g_spifc;
+    
+    spifc->mtd = (struct mtd_info *)&nand_info;
+    spifc->nand = (struct nand_chip *)&nand_chip;
+    spifc->buf.dma_buf = (dma_addr_t)CONFIG_NAND_DMA_BUF_ADDR;
+    spifc->buf.buf = (uint8_t *)spifc->buf.dma_buf;
+
+	spifc_hw_init(spifc);
+	ret = spi_nand_register(spifc, &spifc_ops);
+	if(ret)
+	{
+       	debug("board_nand_init_spifc:(err=%d)", ret);
+		return ret;	
+	}
+
+	spi_nand_debug_init();
+
+	return 0;
+}
+
diff --git a/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.h b/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.h
new file mode 100644
index 0000000..c95c827
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.h
@@ -0,0 +1,140 @@
+/*********************************************************************
+ Copyright 2014 by  ZTE Corporation.
+*
+* FileName::    spi_nand.h
+* File Mark:
+* Description:  
+* Others:
+* Version:  
+* Author:  
+* Date:   
+
+* History 1:
+*     Date: 2014.1.15
+*     Version:
+*     Author: zhouqi
+*     Modification:
+* History 2:
+**********************************************************************/
+
+#ifndef __ZXIC_SPIFC_H__
+#define __ZXIC_SPIFC_H__
+
+#define SYS_SPI_NAND_BASE           0x01407000
+#define SPI_NAND_RETRIE             3
+#define SUCCESS                     0
+
+struct spifc_reg_t
+{
+    uint32_t     VER_REG;                        //0x00
+    uint32_t     SFC_START;                      //0x04
+    uint32_t     SFC_EN;                         //0x08
+	uint32_t	 SFC_CTRL0;						//0x0c 
+	uint32_t	 SFC_CTRL1;						//0x10
+	uint32_t	 SFC_CTRL2;						//0x14 
+	uint32_t	 SFC_BYTE_NUM;					//0x18
+	uint32_t	 SFC_ADDR;						//0x1c
+	uint32_t	 SFC_INS;						//0x20
+	uint32_t	 SFC_TIMING;					//0x24
+	uint32_t	 SFC_INT_EN;					//0x28
+    uint32_t     SFC_INT_RAW;                    //0x2c
+    uint32_t     SFC_INT_SW_CLR;                 //0x30
+    uint32_t     SFC_SW;                         //0x34
+    uint32_t     SFC_DATA;                       //0x38
+};
+
+/*spifc start 0x4*/
+#define     FC_START      (1<<0)
+#define     FC_BUSY       (1<<0)
+
+/*spifc enable 0x8*/
+#define     FC_EN_BACK          (1<<1)
+#define     FC_EN               (1<<0)
+
+/*spifc main ctr0 0xc*/
+#define     FC_SCLK_PAUSE_CLR_ALLOW     (1<<17)
+#define     FC_SCLK_PAUSE_EN            (1<<16)
+#define     FC_TXFIFO_CLR               (1<<15)
+#define     FC_RXFIFO_CLR               (1<<14)
+#define     FC_TXFIFO_THRES             (1<<10)
+#define     FC_RXFIFO_THRES             (1<<6)
+#define     FC_TX_DMA_EN                (1<<5)
+#define     FC_RX_DMA_EN                (1<<4)
+#define     FC_WDOG_EN                  (1<<3)
+#define     FC_SPI_MODE                 (1<<1)
+#define     FC_WR_PROTECT               (1<<0)
+
+/*spifc ctrl1 0x10  in the condition : SFC_EN = 1 SFC_BUSY = 0*/
+#define     FC_ADDR_TX_EN           (4)
+#define     FC_DUMMY_TX_EN          (2)
+#define     FC_READ_DAT_EN          (1)
+#define     FC_WRITE_DAT_EN         (0)
+
+/*spifc ctrl2 0x14*/
+#define     FC_DUMMY_BYTE_NUM           (12)  /* [12:15} */
+#define     FC_DUMMY_BIT_NUM            (8)   /* [8:10] */
+#define     FC_ADDR_BYTE_NUM            (5)   /* [5:6] */
+#define         FC_ADDR_BYTE_NUM_8             (0) 
+#define         FC_ADDR_BYTE_NUM_16            (1) 
+#define         FC_ADDR_BYTE_NUM_24            (2)  
+#define         FC_ADDR_BYTE_NUM_32            (3)   
+#define     FC_ADDR_MULTI_LINE_EN       (1<<4)
+#define     FC_DAT_MULTI_LINE_EN        (1<<2)
+#define     FC_TRANS_MOD                (1<<0)
+
+/*spifc timing 0x24*/
+#define     FC_READ_DELAY           (1<<16)   /* [17:16} */
+#define     FC_T_CS_SETUP           (1<<11)   /* [11:13} */
+#define     FC_T_CS_HOLD            (1<<6)    /* [8:6} */
+#define     FC_T_CS_DESEL           (1<<0)    /* [0:3} */
+
+
+/*spifc int enable 0x28*/
+#define     FC_INT_EN_TX_BYD_THES           (1<<7)
+#define     FC_INT_EN_RX_BYD_THES           (1<<6)
+#define     FC_INT_EN_TX_UNDERRUN           (1<<5)
+#define     FC_INT_EN_RX_OVERRUN            (1<<4)
+#define     FC_INT_EN_WDOG_OVERRUN          (1<<2)
+#define     FC_INT_EN_FMT_ERR               (1<<1)
+#define     FC_INT_EN_CMD_END               (1<<0)
+
+/*spifc raw interrupt 0x2c*/
+#define     FC_INT_RAW_TX_BYD_THES           (1<<7)
+#define     FC_INT_RAW_RX_BYD_THES           (1<<6)
+#define     FC_INT_RAW_TX_UNDERRUN           (1<<5)
+#define     FC_INT_RAW_RX_OVERRUN            (1<<4)
+#define     FC_INT_RAW_WDOG_OVERRUN          (1<<2)
+#define     FC_INT_RAW_FMT_ERR               (1<<1)
+#define     FC_INT_RAW_CMD_END               (1<<0)
+#define     FC_INT_RAW_ERR_MASK              (FC_INT_RAW_TX_UNDERRUN|\
+                                              FC_INT_RAW_RX_OVERRUN|\
+                                              FC_INT_RAW_WDOG_OVERRUN|\
+                                              FC_INT_RAW_FMT_ERR)
+
+/*spifc int startus and clr 0x30*/
+#define     FC_INT_CLR_TX_BYD_THES           (1<<7)
+#define     FC_INT_CLR_RX_BYD_THES           (1<<6)
+#define     FC_INT_CLR_TX_UNDERRUN           (1<<5)
+#define     FC_INT_CLR_RX_OVERRUN            (1<<4)
+#define     FC_INT_CLR_WDOG_OVERRUN          (1<<2)
+#define     FC_INT_CLR_FMT_ERR               (1<<1)
+#define     FC_INT_CLR_CMD_END               (1<<0)
+
+/*spifc sw 0x34*/
+#define     FC_TX_FIFO_CNT              (16)         /* [16:20} */
+#define     FC_TX_FIFO_CNT_MASK         (0x1F)      /* [8:12} */
+#define     FC_RX_FIFO_CNT              (8)             /* [8:12} */
+#define     FC_RX_FIFO_CNT_MASK         (0x1F)      /* [8:12} */
+#define     FC_TX_BYD_THRES             (1<<5)  
+#define     FC_RX_BYD_THRES             (1<<4)  
+#define     FC_SCLK_PAUSE_FLAG          (1<<3)  
+#define     FC_WAIT_FLAG                (1<<2) 
+#define     FC_FORMAT_ERR               (1<<1)  
+
+
+#define     FC_DMA_NONE           		0
+#define     FC_DMA_TX             		1
+#define     FC_DMA_RX             		2
+
+#endif	/* __ZXIC_SPIFC_H__ */
+