nand: mxc: Switch NAND SPL to generic SPL

This also fixes support for mx31pdk and tx25, which had been broken by commit
e05e5de7fae5bec79617e113916dac6631251156.

Signed-off-by: Benoît Thébaudeau <benoit.thebaudeau@advansee.com>
Acked-by: Scott Wood <scottwood@freescale.com>
Tested-by: Fabio Estevam <fabio.estevam@freescale.com>
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index c77c0c4..bcb7161 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -82,6 +82,7 @@
 else  # minimal SPL drivers
 
 COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o
+COBJS-$(CONFIG_NAND_MXC) += mxc_nand_spl.o
 
 endif # drivers
 endif # nand
diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c
index 29ceab3..507bbc2 100644
--- a/drivers/mtd/nand/mxc_nand.c
+++ b/drivers/mtd/nand/mxc_nand.c
@@ -26,7 +26,7 @@
 	defined(CONFIG_MX51) || defined(CONFIG_MX53)
 #include <asm/arch/imx-regs.h>
 #endif
-#include <fsl_nfc.h>
+#include "mxc_nand.h"
 
 #define DRIVER_NAME "mxc_nand"
 
@@ -36,9 +36,9 @@
 	struct mtd_info			mtd;
 	struct nand_chip		*nand;
 
-	struct fsl_nfc_regs __iomem	*regs;
+	struct mxc_nand_regs __iomem	*regs;
 #ifdef MXC_NFC_V3_2
-	struct fsl_nfc_ip_regs __iomem	*ip_regs;
+	struct mxc_nand_ip_regs __iomem	*ip_regs;
 #endif
 	int				spare_only;
 	int				status_request;
@@ -1213,10 +1213,10 @@
 	this->read_buf = mxc_nand_read_buf;
 	this->verify_buf = mxc_nand_verify_buf;
 
-	host->regs = (struct fsl_nfc_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
+	host->regs = (struct mxc_nand_regs __iomem *)CONFIG_MXC_NAND_REGS_BASE;
 #ifdef MXC_NFC_V3_2
 	host->ip_regs =
-		(struct fsl_nfc_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE;
+		(struct mxc_nand_ip_regs __iomem *)CONFIG_MXC_NAND_IP_REGS_BASE;
 #endif
 	host->clk_act = 1;
 
diff --git a/drivers/mtd/nand/mxc_nand.h b/drivers/mtd/nand/mxc_nand.h
new file mode 100644
index 0000000..308ff8d
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nand.h
@@ -0,0 +1,225 @@
+/*
+ * (c) 2009 Magnus Lilja <lilja.magnus@gmail.com>
+ *
+ * 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
+ */
+
+#ifndef __MXC_NAND_H
+#define __MXC_NAND_H
+
+/*
+ * Register map and bit definitions for the Freescale NAND Flash Controller
+ * present in various i.MX devices.
+ *
+ * MX31 and MX27 have version 1, which has:
+ *	4 512-byte main buffers and
+ *	4 16-byte spare buffers
+ *	to support up to 2K byte pagesize nand.
+ *	Reading or writing a 2K page requires 4 FDI/FDO cycles.
+ *
+ * MX25 and MX35 have version 2.1, and MX51 and MX53 have version 3.2, which
+ * have:
+ *	8 512-byte main buffers and
+ *	8 64-byte spare buffers
+ *	to support up to 4K byte pagesize nand.
+ *	Reading or writing a 2K or 4K page requires only 1 FDI/FDO cycle.
+ *	Also some of registers are moved and/or changed meaning as seen below.
+ */
+#if defined(CONFIG_MX27) || defined(CONFIG_MX31)
+#define MXC_NFC_V1
+#define is_mxc_nfc_1()		1
+#define is_mxc_nfc_21()		0
+#define is_mxc_nfc_32()		0
+#elif defined(CONFIG_MX25) || defined(CONFIG_MX35)
+#define MXC_NFC_V2_1
+#define is_mxc_nfc_1()		0
+#define is_mxc_nfc_21()		1
+#define is_mxc_nfc_32()		0
+#elif defined(CONFIG_MX51) || defined(CONFIG_MX53)
+#define MXC_NFC_V3
+#define MXC_NFC_V3_2
+#define is_mxc_nfc_1()		0
+#define is_mxc_nfc_21()		0
+#define is_mxc_nfc_32()		1
+#else
+#error "MXC NFC implementation not supported"
+#endif
+#define is_mxc_nfc_3()		is_mxc_nfc_32()
+
+#if defined(MXC_NFC_V1)
+#define NAND_MXC_NR_BUFS		4
+#define NAND_MXC_SPARE_BUF_SIZE		16
+#define NAND_MXC_REG_OFFSET		0xe00
+#define NAND_MXC_2K_MULTI_CYCLE
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+#define NAND_MXC_NR_BUFS		8
+#define NAND_MXC_SPARE_BUF_SIZE		64
+#define NAND_MXC_REG_OFFSET		0x1e00
+#endif
+
+struct mxc_nand_regs {
+	u8 main_area[NAND_MXC_NR_BUFS][0x200];
+	u8 spare_area[NAND_MXC_NR_BUFS][NAND_MXC_SPARE_BUF_SIZE];
+	/*
+	 * reserved size is offset of nfc registers
+	 * minus total main and spare sizes
+	 */
+	u8 reserved1[NAND_MXC_REG_OFFSET
+		- NAND_MXC_NR_BUFS * (512 + NAND_MXC_SPARE_BUF_SIZE)];
+#if defined(MXC_NFC_V1)
+	u16 buf_size;
+	u16 reserved2;
+	u16 buf_addr;
+	u16 flash_addr;
+	u16 flash_cmd;
+	u16 config;
+	u16 ecc_status_result;
+	u16 rsltmain_area;
+	u16 rsltspare_area;
+	u16 wrprot;
+	u16 unlockstart_blkaddr;
+	u16 unlockend_blkaddr;
+	u16 nf_wrprst;
+	u16 config1;
+	u16 config2;
+#elif defined(MXC_NFC_V2_1)
+	u16 reserved2[2];
+	u16 buf_addr;
+	u16 flash_addr;
+	u16 flash_cmd;
+	u16 config;
+	u32 ecc_status_result;
+	u16 spare_area_size;
+	u16 wrprot;
+	u16 reserved3[2];
+	u16 nf_wrprst;
+	u16 config1;
+	u16 config2;
+	u16 reserved4;
+	u16 unlockstart_blkaddr;
+	u16 unlockend_blkaddr;
+	u16 unlockstart_blkaddr1;
+	u16 unlockend_blkaddr1;
+	u16 unlockstart_blkaddr2;
+	u16 unlockend_blkaddr2;
+	u16 unlockstart_blkaddr3;
+	u16 unlockend_blkaddr3;
+#elif defined(MXC_NFC_V3_2)
+	u32 flash_cmd;
+	u32 flash_addr[12];
+	u32 config1;
+	u32 ecc_status_result;
+	u32 status_sum;
+	u32 launch;
+#endif
+};
+
+#ifdef MXC_NFC_V3_2
+struct mxc_nand_ip_regs {
+	u32 wrprot;
+	u32 wrprot_unlock_blkaddr[8];
+	u32 config2;
+	u32 config3;
+	u32 ipc;
+	u32 err_addr;
+	u32 delay_line;
+};
+#endif
+
+/* Set FCMD to 1, rest to 0 for Command operation */
+#define NFC_CMD				0x1
+
+/* Set FADD to 1, rest to 0 for Address operation */
+#define NFC_ADDR			0x2
+
+/* Set FDI to 1, rest to 0 for Input operation */
+#define NFC_INPUT			0x4
+
+/* Set FDO to 001, rest to 0 for Data Output operation */
+#define NFC_OUTPUT			0x8
+
+/* Set FDO to 010, rest to 0 for Read ID operation */
+#define NFC_ID				0x10
+
+/* Set FDO to 100, rest to 0 for Read Status operation */
+#define NFC_STATUS			0x20
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define NFC_CONFIG1_SP_EN		(1 << 2)
+#define NFC_CONFIG1_RST			(1 << 6)
+#define NFC_CONFIG1_CE			(1 << 7)
+#elif defined(MXC_NFC_V3_2)
+#define NFC_CONFIG1_SP_EN		(1 << 0)
+#define NFC_CONFIG1_CE			(1 << 1)
+#define NFC_CONFIG1_RST			(1 << 2)
+#endif
+#define NFC_V1_V2_CONFIG1_ECC_EN	(1 << 3)
+#define NFC_V1_V2_CONFIG1_INT_MSK	(1 << 4)
+#define NFC_V1_V2_CONFIG1_BIG		(1 << 5)
+#define NFC_V2_CONFIG1_ECC_MODE_4	(1 << 0)
+#define NFC_V2_CONFIG1_ONE_CYCLE	(1 << 8)
+#define NFC_V2_CONFIG1_FP_INT		(1 << 11)
+#define NFC_V3_CONFIG1_RBA_MASK		(0x7 << 4)
+#define NFC_V3_CONFIG1_RBA(x)		(((x) & 0x7) << 4)
+
+#define NFC_V1_V2_CONFIG2_INT		(1 << 15)
+#define NFC_V3_CONFIG2_PS_MASK		(0x3 << 0)
+#define NFC_V3_CONFIG2_PS_512		(0 << 0)
+#define NFC_V3_CONFIG2_PS_2048		(1 << 0)
+#define NFC_V3_CONFIG2_PS_4096		(2 << 0)
+#define NFC_V3_CONFIG2_ONE_CYCLE	(1 << 2)
+#define NFC_V3_CONFIG2_ECC_EN		(1 << 3)
+#define NFC_V3_CONFIG2_2CMD_PHASES	(1 << 4)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH0	(1 << 5)
+#define NFC_V3_CONFIG2_ECC_MODE_8	(1 << 6)
+#define NFC_V3_CONFIG2_PPB_MASK		(0x3 << 7)
+#define NFC_V3_CONFIG2_PPB(x)		(((x) & 0x3) << 7)
+#define NFC_V3_CONFIG2_EDC_MASK		(0x7 << 9)
+#define NFC_V3_CONFIG2_EDC(x)		(((x) & 0x7) << 9)
+#define NFC_V3_CONFIG2_NUM_ADDR_PH1(x)	(((x) & 0x3) << 12)
+#define NFC_V3_CONFIG2_INT_MSK		(1 << 15)
+#define NFC_V3_CONFIG2_SPAS_MASK	(0xff << 16)
+#define NFC_V3_CONFIG2_SPAS(x)		(((x) & 0xff) << 16)
+#define NFC_V3_CONFIG2_ST_CMD_MASK	(0xff << 24)
+#define NFC_V3_CONFIG2_ST_CMD(x)	(((x) & 0xff) << 24)
+
+#define NFC_V3_CONFIG3_ADD_OP(x)	(((x) & 0x3) << 0)
+#define NFC_V3_CONFIG3_FW8		(1 << 3)
+#define NFC_V3_CONFIG3_SBB(x)		(((x) & 0x7) << 8)
+#define NFC_V3_CONFIG3_NUM_OF_DEVS(x)	(((x) & 0x7) << 12)
+#define NFC_V3_CONFIG3_RBB_MODE		(1 << 15)
+#define NFC_V3_CONFIG3_NO_SDMA		(1 << 20)
+
+#define NFC_V3_WRPROT_UNLOCK		(1 << 2)
+#define NFC_V3_WRPROT_BLS_UNLOCK	(2 << 6)
+
+#define NFC_V3_IPC_CREQ			(1 << 0)
+#define NFC_V3_IPC_INT			(1 << 31)
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+#define operation	config2
+#define readnfc		readw
+#define writenfc	writew
+#elif defined(MXC_NFC_V3_2)
+#define operation	launch
+#define readnfc		readl
+#define writenfc	writel
+#endif
+
+#endif /* __MXC_NAND_H */
diff --git a/drivers/mtd/nand/mxc_nand_spl.c b/drivers/mtd/nand/mxc_nand_spl.c
new file mode 100644
index 0000000..09f23c3
--- /dev/null
+++ b/drivers/mtd/nand/mxc_nand_spl.c
@@ -0,0 +1,366 @@
+/*
+ * (C) Copyright 2009
+ * Magnus Lilja <lilja.magnus@gmail.com>
+ *
+ * (C) Copyright 2008
+ * Maxim Artamonov, <scn1874 at yandex.ru>
+ *
+ * (C) Copyright 2006-2008
+ * Stefan Roese, DENX Software Engineering, sr at denx.de.
+ *
+ * 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 <common.h>
+#include <nand.h>
+#include <asm/arch/imx-regs.h>
+#include <asm/io.h>
+#include "mxc_nand.h"
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR;
+#elif defined(MXC_NFC_V3_2)
+static struct mxc_nand_regs *const nfc = (void *)NFC_BASE_ADDR_AXI;
+static struct mxc_nand_ip_regs *const nfc_ip = (void *)NFC_BASE_ADDR;
+#endif
+
+static void nfc_wait_ready(void)
+{
+	uint32_t tmp;
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+	while (!(readnfc(&nfc->config2) & NFC_V1_V2_CONFIG2_INT))
+		;
+
+	/* Reset interrupt flag */
+	tmp = readnfc(&nfc->config2);
+	tmp &= ~NFC_V1_V2_CONFIG2_INT;
+	writenfc(tmp, &nfc->config2);
+#elif defined(MXC_NFC_V3_2)
+	while (!(readnfc(&nfc_ip->ipc) & NFC_V3_IPC_INT))
+		;
+
+	/* Reset interrupt flag */
+	tmp = readnfc(&nfc_ip->ipc);
+	tmp &= ~NFC_V3_IPC_INT;
+	writenfc(tmp, &nfc_ip->ipc);
+#endif
+}
+
+static void nfc_nand_init(void)
+{
+#if defined(MXC_NFC_V3_2)
+	int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+	int tmp;
+
+	tmp = (readnfc(&nfc_ip->config2) & ~(NFC_V3_CONFIG2_SPAS_MASK |
+			NFC_V3_CONFIG2_EDC_MASK | NFC_V3_CONFIG2_PS_MASK)) |
+		NFC_V3_CONFIG2_SPAS(CONFIG_SYS_NAND_OOBSIZE / 2) |
+		NFC_V3_CONFIG2_INT_MSK | NFC_V3_CONFIG2_ECC_EN |
+		NFC_V3_CONFIG2_ONE_CYCLE;
+	if (CONFIG_SYS_NAND_PAGE_SIZE == 4096)
+		tmp |= NFC_V3_CONFIG2_PS_4096;
+	else if (CONFIG_SYS_NAND_PAGE_SIZE == 2048)
+		tmp |= NFC_V3_CONFIG2_PS_2048;
+	else if (CONFIG_SYS_NAND_PAGE_SIZE == 512)
+		tmp |= NFC_V3_CONFIG2_PS_512;
+	/*
+	 * if spare size is larger that 16 bytes per 512 byte hunk
+	 * then use 8 symbol correction instead of 4
+	 */
+	if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+		tmp |= NFC_V3_CONFIG2_ECC_MODE_8;
+	else
+		tmp &= ~NFC_V3_CONFIG2_ECC_MODE_8;
+	writenfc(tmp, &nfc_ip->config2);
+
+	tmp = NFC_V3_CONFIG3_NUM_OF_DEVS(0) |
+			NFC_V3_CONFIG3_NO_SDMA |
+			NFC_V3_CONFIG3_RBB_MODE |
+			NFC_V3_CONFIG3_SBB(6) | /* Reset default */
+			NFC_V3_CONFIG3_ADD_OP(0);
+#ifndef CONFIG_SYS_NAND_BUSWIDTH_16
+	tmp |= NFC_V3_CONFIG3_FW8;
+#endif
+	writenfc(tmp, &nfc_ip->config3);
+
+	writenfc(0, &nfc_ip->delay_line);
+#elif defined(MXC_NFC_V2_1)
+	int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+	int config1;
+
+	writenfc(CONFIG_SYS_NAND_OOBSIZE / 2, &nfc->spare_area_size);
+
+	/* unlocking RAM Buff */
+	writenfc(0x2, &nfc->config);
+
+	/* hardware ECC checking and correct */
+	config1 = readnfc(&nfc->config1) | NFC_V1_V2_CONFIG1_ECC_EN |
+			NFC_V1_V2_CONFIG1_INT_MSK | NFC_V2_CONFIG1_ONE_CYCLE |
+			NFC_V2_CONFIG1_FP_INT;
+	/*
+	 * if spare size is larger that 16 bytes per 512 byte hunk
+	 * then use 8 symbol correction instead of 4
+	 */
+	if (CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16)
+		config1 &= ~NFC_V2_CONFIG1_ECC_MODE_4;
+	else
+		config1 |= NFC_V2_CONFIG1_ECC_MODE_4;
+	writenfc(config1, &nfc->config1);
+#elif defined(MXC_NFC_V1)
+	/* unlocking RAM Buff */
+	writenfc(0x2, &nfc->config);
+
+	/* hardware ECC checking and correct */
+	writenfc(NFC_V1_V2_CONFIG1_ECC_EN | NFC_V1_V2_CONFIG1_INT_MSK,
+			&nfc->config1);
+#endif
+}
+
+static void nfc_nand_command(unsigned short command)
+{
+	writenfc(command, &nfc->flash_cmd);
+	writenfc(NFC_CMD, &nfc->operation);
+	nfc_wait_ready();
+}
+
+static void nfc_nand_address(unsigned short address)
+{
+	writenfc(address, &nfc->flash_addr);
+	writenfc(NFC_ADDR, &nfc->operation);
+	nfc_wait_ready();
+}
+
+static void nfc_nand_page_address(unsigned int page_address)
+{
+	unsigned int page_count;
+
+	nfc_nand_address(0x00);
+
+	/* code only for large page flash */
+	if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+		nfc_nand_address(0x00);
+
+	page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE;
+
+	if (page_address <= page_count) {
+		page_count--; /* transform 0x01000000 to 0x00ffffff */
+		do {
+			nfc_nand_address(page_address & 0xff);
+			page_address = page_address >> 8;
+			page_count = page_count >> 8;
+		} while (page_count);
+	}
+
+	nfc_nand_address(0x00);
+}
+
+static void nfc_nand_data_output(void)
+{
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+	int i;
+#endif
+
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+	writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+	int config1 = readnfc(&nfc->config1);
+	config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+	writenfc(config1, &nfc->config1);
+#endif
+	writenfc(NFC_OUTPUT, &nfc->operation);
+	nfc_wait_ready();
+#ifdef NAND_MXC_2K_MULTI_CYCLE
+	/*
+	 * This NAND controller requires multiple input commands
+	 * for pages larger than 512 bytes.
+	 */
+	for (i = 1; i < CONFIG_SYS_NAND_PAGE_SIZE / 512; i++) {
+		writenfc(i, &nfc->buf_addr);
+		writenfc(NFC_OUTPUT, &nfc->operation);
+		nfc_wait_ready();
+	}
+#endif
+}
+
+static int nfc_nand_check_ecc(void)
+{
+#if defined(MXC_NFC_V1)
+	u16 ecc_status = readw(&nfc->ecc_status_result);
+	return (ecc_status & 0x3) == 2 || (ecc_status >> 2) == 2;
+#elif defined(MXC_NFC_V2_1) || defined(MXC_NFC_V3_2)
+	u32 ecc_status = readl(&nfc->ecc_status_result);
+	int ecc_per_page = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+	int err_limit = CONFIG_SYS_NAND_OOBSIZE / ecc_per_page > 16 ? 8 : 4;
+	int subpages = CONFIG_SYS_NAND_PAGE_SIZE / 512;
+
+	do {
+		if ((ecc_status & 0xf) > err_limit)
+			return 1;
+		ecc_status >>= 4;
+	} while (--subpages);
+
+	return 0;
+#endif
+}
+
+static void nfc_nand_read_page(unsigned int page_address)
+{
+	/* read in first 0 buffer */
+#if defined(MXC_NFC_V1) || defined(MXC_NFC_V2_1)
+	writenfc(0, &nfc->buf_addr);
+#elif defined(MXC_NFC_V3_2)
+	int config1 = readnfc(&nfc->config1);
+	config1 &= ~NFC_V3_CONFIG1_RBA_MASK;
+	writenfc(config1, &nfc->config1);
+#endif
+	nfc_nand_command(NAND_CMD_READ0);
+	nfc_nand_page_address(page_address);
+
+	if (CONFIG_SYS_NAND_PAGE_SIZE > 512)
+		nfc_nand_command(NAND_CMD_READSTART);
+
+	nfc_nand_data_output(); /* fill the main buffer 0 */
+}
+
+static int nfc_read_page(unsigned int page_address, unsigned char *buf)
+{
+	int i;
+	u32 *src;
+	u32 *dst;
+
+	nfc_nand_read_page(page_address);
+
+	if (nfc_nand_check_ecc())
+		return -1;
+
+	src = (u32 *)&nfc->main_area[0][0];
+	dst = (u32 *)buf;
+
+	/* main copy loop from NAND-buffer to SDRAM memory */
+	for (i = 0; i < CONFIG_SYS_NAND_PAGE_SIZE / 4; i++) {
+		writel(readl(src), dst);
+		src++;
+		dst++;
+	}
+
+	return 0;
+}
+
+static int is_badblock(int pagenumber)
+{
+	int page = pagenumber;
+	u32 badblock;
+	u32 *src;
+
+	/* Check the first two pages for bad block markers */
+	for (page = pagenumber; page < pagenumber + 2; page++) {
+		nfc_nand_read_page(page);
+
+		src = (u32 *)&nfc->spare_area[0][0];
+
+		/*
+		 * IMPORTANT NOTE: The nand flash controller uses a non-
+		 * standard layout for large page devices. This can
+		 * affect the position of the bad block marker.
+		 */
+		/* Get the bad block marker */
+		badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]);
+		badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4);
+		badblock &= 0xff;
+
+		/* bad block marker verify */
+		if (badblock != 0xff)
+			return 1; /* potential bad block */
+	}
+
+	return 0;
+}
+
+static int nand_load(unsigned int from, unsigned int size, unsigned char *buf)
+{
+	int i;
+	unsigned int page;
+	unsigned int maxpages = CONFIG_SYS_NAND_SIZE /
+				CONFIG_SYS_NAND_PAGE_SIZE;
+
+	nfc_nand_init();
+
+	/* Convert to page number */
+	page = from / CONFIG_SYS_NAND_PAGE_SIZE;
+	i = 0;
+
+	while (i < size / CONFIG_SYS_NAND_PAGE_SIZE) {
+		if (nfc_read_page(page, buf) < 0)
+			return -1;
+
+		page++;
+		i++;
+		buf = buf + CONFIG_SYS_NAND_PAGE_SIZE;
+
+		/*
+		 * Check if we have crossed a block boundary, and if so
+		 * check for bad block.
+		 */
+		if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) {
+			/*
+			 * Yes, new block. See if this block is good. If not,
+			 * loop until we find a good block.
+			 */
+			while (is_badblock(page)) {
+				page = page + CONFIG_SYS_NAND_PAGE_COUNT;
+				/* Check i we've reached the end of flash. */
+				if (page >= maxpages)
+					return -1;
+			}
+		}
+	}
+
+	return 0;
+}
+
+/*
+ * The main entry for NAND booting. It's necessary that SDRAM is already
+ * configured and available since this code loads the main U-Boot image
+ * from NAND into SDRAM and starts it from there.
+ */
+void nand_boot(void)
+{
+	__attribute__((noreturn)) void (*uboot)(void);
+
+	/*
+	 * CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must
+	 * be aligned to full pages
+	 */
+	if (!nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE,
+		       (uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) {
+		/* Copy from NAND successful, start U-boot */
+		uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START;
+		uboot();
+	} else {
+		/* Unrecoverable error when copying from NAND */
+		hang();
+	}
+}
+
+/*
+ * Called in case of an exception.
+ */
+void hang(void)
+{
+	/* Loop forever */
+	while (1) ;
+}