[Feature][T106]ZXW P56U09 code
Only Configure: Yes
Affected branch: master
Affected module: unknow
Is it affected on both ZXIC and MTK: only ZXIC
Self-test: No
Doc Update: No
Change-Id: I3cbd8b420271eb20c2b40ebe5c78f83059cd42f3
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..5c471ca
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand.c
@@ -0,0 +1,207 @@
+/*
+ * (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 <secure_verify.h>
+#include <boot_mode.h>
+#include <asm/arch/efuse.h>
+
+#ifndef CONFIG_SYS_NAND_BASE_LIST
+#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE }
+#endif
+#define reg32(addr) (*(volatile unsigned long *)(addr))
+
+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;
+
+unsigned char g_nor_flag = 0;
+int one_flag = 0;
+/*******************************************************************************
+ * 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;
+
+ if(one_flag == 0){
+ /*¶ÁȡоƬid*/
+ if((reg32(0x0121b040)>>8) == ZX297520V3ECOGG_GW_NYC_NOR_2G_DDR
+ ||(reg32(0x0121b040)>>8) == ZX297520V3ECOSC_GW_NYC_NOR_2G_DDR
+ ||(reg32(0x0121b040)>>8) == ZX297520V3ECOSCC_GW_NYB_4G_DDR
+ ||(reg32(0x0121b040)>>8) == ZX297520V3ECOGG_GW_NYB_4G_DDR)
+ {
+ g_nor_flag = 1;
+ efuse_get_data();
+ //printf("chip id is 0x%x.\n",reg32(0x0121b040));
+ //printf("g_nor_flag is %d.\n",g_nor_flag);
+ }
+ }
+
+ 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();
+
+ if(one_flag == 0){
+
+ jffs2_lzma_init();
+ one_flag = 1;
+ }
+
+}
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..6d54c1e
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/nand_ids.c
@@ -0,0 +1,169 @@
+/*
+ * 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}},
+ /*GD5F4GQ6REY2G 512MB SPI-NAND*/
+ {0xC8,0x45,0x00,0,2048,128,0,0,0x20000,64,4096,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+ /*XTX XT26Q04D-B 512M SPI-NAND*/
+ {0x0B,0x53,0x00,0,4096,256,0,0,0x40000,64,2048,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+ /*UNIM UM19A0LISW 128M SPI-NAND*/
+ {0xB0,0x15,0x00,0,2048,64,0,0,0x20000,64,1024,1,1,0,{2,1,1,9,0,0,0,0,0,0},{0,0}},
+ {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 512MiB 1,8V", 0x35, 4096, 512, 0x40000, 0}, //F50D44G41XB (2X)
+ {"SPI-NAND 512MiB 1,8V", 0x45, 2048, 512, 0x20000, 0}, //GD5F4GQ6REY2G
+ {"SPI-NAND 512MiB 1,8V", 0x53, 4096, 512, 0x40000, 0}, //XT26Q04D-B
+ {"SPI-NAND 128MiB 1,8V", 0x15, 2048, 128, 0x20000, 0}, //UM19A0LISW
+ {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"},
+ {NAND_MFR_XTX, "xtx"},
+ {NAND_MFR_UNIM, "unim"},
+ {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..26da064
--- /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 = 12,
+ .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 = 12,
+ .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..4062428
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/spi_nand_devices.c
@@ -0,0 +1,353 @@
+/*********************************************************************
+ 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 struct nand_ecclayout nand_xtx_oob_256= {
+ .eccbytes = 192,
+ .oobfree = {{2,62}}
+ };
+
+ static struct nand_ecclayout nand_unim_oob_64= {
+ .oobfree = {{2,62}}
+ };
+
+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_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_XTX)
+ || (g_maf_id == NAND_MFR_UNIM)
+ || (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;
+ case NAND_MFR_XTX:
+ if(mtd->oobsize==256 && mtd->writesize==4096)
+ {
+ chip->ecc.layout =&nand_xtx_oob_256;
+ }
+ break;
+ case NAND_MFR_UNIM:
+ if(mtd->oobsize==64 && mtd->writesize==2048)
+ {
+ chip->ecc.layout =&nand_unim_oob_64;
+ }
+ 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..7ba60b1
--- /dev/null
+++ b/boot/common/src/uboot/drivers/mtd/nand/zxic_spifc.c
@@ -0,0 +1,1619 @@
+/*********************************************************************
+ 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;
+extern g_nor_flag;
+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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ spi_fc_setup_cmd(&cmd, 0, 0); /* ÃüÁîÅäÖà */
+ spi_fc_start(); /* ¿ªÊ¼´«Êä */
+ ret = spi_fc_wait_cmd_end(); /* µÈ´ýÃüÁî½áÊø */
+ if( ret != SUCCESS )
+ continue; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ udelay(5);
+ }
+
+ /* µÈ´ý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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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¡é??¡ä? */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+ 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)
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+
+ 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 );
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ udelay(5);
+ }
+
+ /* µÈ´ýÉÏ´ÎÃüÁîÖ´ÐÐÍê³É */
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ udelay(5);
+ }
+
+ /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ udelay(5);
+ }
+ /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ }
+
+
+ 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
+ {
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_LOW);
+ }
+ 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; /* ´«Êäʧ°Ü£¬½áÊø±¾´ÎÑ»·²¢ÖØ´« */
+ }
+
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetOutputValue(GPIO86,GPIO_HIGH);
+ udelay(5);
+ }
+
+ /* µÈ´ýÃüÁîÖ´ÐÐÍê³É */
+ 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);
+ if(g_nor_flag == 1)
+ {
+ zDrvGpio_SetFunc(GPIO93,GPIO93_GPIO93);
+ zDrvGpio_SetOutputValue(GPIO93, GPIO_HIGH);
+ zDrvGpio_SetFunc(GPIO86,GPIO86_GPIO86);
+ zDrvGpio_SetDirection(GPIO86, GPIO_OUT);
+ }
+ 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__ */
+