[U-Boot] [PATCH v3 4/6] i.MX31: Add i.MX31 NAND Flash Controller driver.

Magnus Lilja lilja.magnus at gmail.com
Fri Aug 29 10:36:20 CEST 2008


Imported from Freescale's Linux NFC driver from the i.MX31 BSP
release 5 (Linux 2.6.22.5) and the i.MX31 PDK BSP (Linux 2.6.24).

The code has been changed to conform (better) with the coding style
in Linux/U-boot. Sections not used by U-boot have been removed.

The driver has been tested on i.MX31 Litekit (small page NAND)
and i.MX31 PDK (large page NAND). Both boards have 8 bit wide
NAND devices.
16 bit NAND devices have not been tested and probably requires a
minor code change.

Signed-off-by: Magnus Lilja <lilja.magnus at gmail.com>
---

Issues regarding non-standard OOB/Bad block table, verify_buf and
memcpy_32() and some other of Scott Wood's comments haven't been 
implemented yet.

And yes, the controller requires 16 bit accesses but I made a 
memcpy_32() instead of memcpy_16() but the plan is to implement
Scott's proposal to memcpy_32() to a software buffer and then
memcpy() from there.

 drivers/mtd/nand/Makefile             |    1 +
 drivers/mtd/nand/mx31_nand.c          | 1136 +++++++++++++++++++++++++++++++++
 include/asm-arm/arch-mx31/mx31-regs.h |   96 +++
 3 files changed, 1233 insertions(+), 0 deletions(-)

diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index 1923310..729b8c2 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -37,6 +37,7 @@ endif
 
 COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
 COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
+COBJS-$(CONFIG_MX31_NAND) += mx31_nand.o
 endif
 
 COBJS	:= $(COBJS-y)
diff --git a/drivers/mtd/nand/mx31_nand.c b/drivers/mtd/nand/mx31_nand.c
new file mode 100644
index 0000000..ebcd567
--- /dev/null
+++ b/drivers/mtd/nand/mx31_nand.c
@@ -0,0 +1,1136 @@
+/*
+ * (C) Copyright 2008 Magnus Lilja <lilja.magnus at gmail.com>
+ *
+ * Based on Freescale's Linux MXC NAND driver.
+ *
+ * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+#include <common.h>
+#include <nand.h>
+#include <asm-arm/arch/mx31-regs.h>
+
+struct mxc_mtd_s {
+	struct mtd_info mtd;
+	struct nand_chip nand;
+	struct device *dev;
+};
+
+static struct mxc_mtd_s *mxc_nand_data;
+
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY   2000
+
+/*
+ * Macros to get byte and bit positions of ECC
+ */
+#define COLPOS(x) ((x) >> 4)
+#define BITPOS(x) ((x) & 0xf)
+
+/* Define single bit Error positions in Main & Spare area */
+#define MAIN_SINGLEBIT_ERROR 0x4
+#define SPARE_SINGLEBIT_ERROR 0x1
+
+struct nand_info {
+	int spare_only;
+	int status_request;
+	u16 col_addr;
+};
+
+static struct nand_info g_nandfc_info;
+
+#ifdef CONFIG_MTD_NAND_MXC_SWECC
+static int hardware_ecc;
+#else
+static int hardware_ecc = 1;
+#endif
+
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+static int ecc_disabled;
+#endif
+
+static int is2k_pagesize;
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_8 = {
+	.eccbytes = 5,
+	.eccpos = {6, 7, 8, 9, 10},
+	.oobfree = {
+		{.offset = 0,
+		 .length = 5},
+		{.offset = 11,
+		 .length = 5}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_16 = {
+	.eccbytes = 5,
+	.eccpos = {6, 7, 8, 9, 10},
+	.oobfree = {
+		{.offset = 0,
+		 .length = 6},
+		{.offset = 12,
+		 .length = 4}}
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+	.eccbytes = 20,
+	.eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26,
+		   38, 39, 40, 41, 42, 54, 55, 56, 57, 58},
+	.oobfree = {
+		{.offset = 0,
+		 .length = 5},
+		{.offset = 11,
+		 .length = 10},
+		{.offset = 27,
+		 .length = 10},
+		{.offset = 43,
+		 .length = 10},
+		{.offset = 59,
+		 .length = 5}}
+};
+
+/* 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
+};
+
+/* 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 = 0,
+	.len = 4,
+	.veroffs = 4,
+	.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 = 0,
+	.len = 4,
+	.veroffs = 4,
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
+/**
+ * memcpy variant that copies 32 bit words. This is needed since the
+ * NFC only allows 32 bit accesses. Added for U-boot.
+ */
+static void *memcpy_32(void *dest, const void *src, size_t n)
+{
+	u32 *dst_32 = (u32 *)dest;
+	const u32 *src_32 = (u32 *)src;
+
+	while (n > 0) {
+		*dst_32++ = *src_32++;
+		n -= 4;
+	}
+
+	return dest;
+}
+
+/**
+ * This function polls the NANDFC to wait for the basic operation to
+ * complete by checking the INT bit of config2 register.
+ *
+ * @param       max_retries    number of retry attempts (separated by 1 us)
+ * @param       param          parameter for debug
+ * @param       useirq         1 if IRQ should be used rather than polling
+ */
+static void wait_op_done(int max_retries, u16 param, int useirq)
+{
+	while (max_retries-- > 0) {
+		if (NFC_CONFIG2 & NFC_INT) {
+			NFC_CONFIG2 &= ~NFC_INT;
+			break;
+		}
+		udelay(1);
+	}
+	if (max_retries <= 0)
+		MTDDEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n",
+			 __FUNCTION__, param);
+}
+
+/**
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @param       cmd     command for NAND Flash
+ * @param       useirq  1 if IRQ should be used rather than polling
+ */
+static void send_cmd(u16 cmd, int useirq)
+{
+	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(0x%x, %d)\n", cmd, useirq);
+
+	NFC_FLASH_CMD = (u16) cmd;
+	NFC_CONFIG2 = NFC_CMD;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, cmd, useirq);
+}
+
+/**
+ * This function sends an address (or partial address) to the
+ * NAND device.  The address is used to select the source/destination for
+ * a NAND command.
+ *
+ * @param       addr    address to be written to NFC.
+ * @param       islast  1 if this is the last address cycle for command
+ */
+static void send_addr(u16 addr, int islast)
+{
+	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(0x%x %d)\n", addr, islast);
+
+	NFC_FLASH_ADDR = addr;
+	NFC_CONFIG2 = NFC_ADDR;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, addr, islast);
+}
+
+/**
+ * This function requests the NANDFC to initate the transfer
+ * of data currently in the NANDFC RAM buffer to the NAND device.
+ *
+ * @param	buf_id	      Specify Internal RAM Buffer number (0-3)
+ * @param       spare_only    set to 1 if only the spare area is transferred
+ */
+static void send_prog_page(u8 buf_id, int spare_only)
+{
+	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", spare_only);
+
+	/* NANDFC buffer 0 is used for page read/write */
+
+	NFC_BUF_ADDR = buf_id;
+
+	/* Configure spare or page+spare access */
+	if (!is2k_pagesize) {
+		if (spare_only)
+			NFC_CONFIG1 |= NFC_SP_EN;
+		else
+			NFC_CONFIG1 &= ~NFC_SP_EN;
+	}
+	NFC_CONFIG2 = NFC_INPUT;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, spare_only, 1);
+}
+
+/**
+ * This function will correct the single bit ECC error
+ *
+ * @param  buf_id	Specify Internal RAM Buffer number (0-3)
+ * @param  eccpos 	Ecc byte and bit position
+ * @param  spare_only  	set to 1 if only spare area needs correction
+ */
+static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, int spare_only)
+{
+	u16 col;
+	u8 pos;
+	volatile u16 *buf;
+
+	/* Get col & bit position of error
+	   these macros works for both 8 & 16 bits */
+	col = COLPOS(eccpos);	/* Get half-word position */
+	pos = BITPOS(eccpos);	/* Get bit position */
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nd_correct_error (col=%d pos=%d)\n", col, pos);
+
+	/* Set the pointer for main / spare area */
+	if (!spare_only)
+		buf = (volatile u16 *)(MAIN_AREA0 + col + (256 * buf_id));
+	else
+		buf = (volatile u16 *)(SPARE_AREA0 + col + (8 * buf_id));
+
+	/* Fix the data */
+	*buf ^= 1 << pos;
+}
+
+/**
+ * This function will maintains state of single bit Error
+ * in Main & spare  area
+ *
+ * @param buf_id	Specify Internal RAM Buffer number (0-3)
+ * @param spare  	set to 1 if only spare area needs correction
+ */
+static void mxc_nd_correct_ecc(u8 buf_id, int spare)
+{
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+	/* To maintain single bit error in previous page */
+	static int last_err_main, last_err_spare;
+#endif
+	u16 value, ecc_status;
+
+	/* Read the ECC result */
+	ecc_status = NFC_ECC_STATUS_RESULT;
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nd_correct_ecc (Ecc status=%x)\n", ecc_status);
+
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+	/* Check for Error in Mainarea */
+	if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+		/* Check for error in previous page */
+		if (last_err_main && !spare) {
+			value = NFC_RSLTMAIN_AREA;
+			/* Correct single bit error in Mainarea
+			   NFC will not correct the error in
+			   current page */
+			mxc_nd_correct_error(buf_id, value, 0);
+		} else
+			/* Set if single bit error in current page */
+			last_err_main = 1;
+	} else
+		/* Reset if no single bit error in current page */
+		last_err_main = 0;
+
+	/* Check for Error in Sparearea */
+	if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+		/* Check for error in previous page */
+		if (last_err_space) {
+			value = NFC_RSLTSPARE_AREA;
+			/* Correct single bit error in Mainarea
+			   NFC will not correct the error in
+			   current page */
+			mxc_nd_correct_error(buf_id, value, 1);
+		} else
+			/* Set if single bit error in current page */
+			last_err_spare = 1;
+	} else
+		/* Reset if no single bit error in current page */
+		last_err_spare = 0;
+#else
+	if (((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR)
+	    || ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR)) {
+		if (ecc_disabled) {
+			if ((ecc_status & 0xC) == MAIN_SINGLEBIT_ERROR) {
+				value = NFC_RSLTMAIN_AREA;
+				/* Correct single bit error in Mainarea
+				   NFC will not correct the error in
+				   current page */
+				mxc_nd_correct_error(buf_id, value, 0);
+			}
+			if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+				value = NFC_RSLTSPARE_AREA;
+				/* Correct single bit error in Mainarea
+				   NFC will not correct the error in
+				   current page */
+				mxc_nd_correct_error(buf_id, value, 1);
+			}
+
+		} else {
+			/* Disable ECC  */
+			NFC_CONFIG1 &= ~NFC_ECC_EN;
+			ecc_disabled = 1;
+		}
+	} else if (ecc_status == 0) {
+		if (ecc_disabled) {
+			/* Enable ECC */
+			NFC_CONFIG1 |= NFC_ECC_EN;
+			ecc_disabled = 0;
+		}
+	} /* else 2-bit Error. Do nothing */
+#endif /* CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2 */
+}
+
+/**
+ * This function requests the NANDFC to initated the transfer
+ * of data from the NAND device into in the NANDFC ram buffer.
+ *
+ * @param  	buf_id		Specify Internal RAM Buffer number (0-3)
+ * @param       spare_only    	set 1 if only the spare area is
+ * transferred
+ */
+static void send_read_page(u8 buf_id, int spare_only)
+{
+	MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", spare_only);
+
+	/* NANDFC buffer 0 is used for page read/write */
+	NFC_BUF_ADDR = buf_id;
+
+	/* Configure spare or page+spare access */
+	if (!is2k_pagesize) {
+		if (spare_only)
+			NFC_CONFIG1 |= NFC_SP_EN;
+		else
+			NFC_CONFIG1 &= ~NFC_SP_EN;
+	}
+
+	NFC_CONFIG2 = NFC_OUTPUT;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, spare_only, 1);
+
+	/* If there are single bit errors in
+	   two consecutive page reads then
+	   the error is not  corrected by the
+	   NFC for the second page.
+	   Correct single bit error in driver */
+
+	mxc_nd_correct_ecc(buf_id, spare_only);
+}
+
+/**
+ * This function requests the NANDFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+	struct nand_chip *this = &mxc_nand_data->nand;
+
+	/* NANDFC buffer 0 is used for device ID output */
+	NFC_BUF_ADDR = 0x0;
+
+	/* Read ID into main buffer */
+	NFC_CONFIG1 &= ~NFC_SP_EN;
+	NFC_CONFIG2 = NFC_ID;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, 0, 1);
+
+	if (this->options & NAND_BUSWIDTH_16) {
+		volatile u16 *mainbuf = MAIN_AREA0;
+
+		/*
+		 * Pack the every-other-byte result for 16-bit ID reads
+		 * into every-byte as the generic code expects and various
+		 * chips implement.
+		 */
+
+		mainbuf[0] = (mainbuf[0] & 0xff) | ((mainbuf[1] & 0xff) << 8);
+		mainbuf[1] = (mainbuf[2] & 0xff) | ((mainbuf[3] & 0xff) << 8);
+		mainbuf[2] = (mainbuf[4] & 0xff) | ((mainbuf[5] & 0xff) << 8);
+	}
+}
+
+/**
+ * This function requests the NANDFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return  device status
+ */
+static u16 get_dev_status(void)
+{
+	volatile u16 *mainbuf = MAIN_AREA1;
+	u32 store;
+	u16 ret;
+	/* Issue status request to NAND device */
+
+	/* store the main area1 first word, later do recovery */
+	store = *((u32 *) mainbuf);
+	/*
+	 * NANDFC buffer 1 is used for device status to prevent
+	 * corruption of read/write buffer on status requests.
+	 */
+	NFC_BUF_ADDR = 1;
+
+	/* Read status into main buffer */
+	NFC_CONFIG1 &= ~NFC_SP_EN;
+	NFC_CONFIG2 = NFC_STATUS;
+
+	/* Wait for operation to complete */
+	wait_op_done(TROP_US_DELAY, 0, 1);
+
+	/* Status is placed in first word of main buffer */
+	/* get status, then recovery area 1 data */
+	ret = mainbuf[0];
+	*((u32 *) mainbuf) = store;
+
+	return ret;
+}
+
+/**
+ * This functions is used by upper layer to checks if device is ready
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ *
+ * @return  0 if device is busy else 1
+ */
+static int mxc_nand_dev_ready(struct mtd_info *mtd)
+{
+	/*
+	 * NFC handles R/B internally.Therefore,this function
+	 * always returns status as ready.
+	 */
+	return 1;
+}
+
+static void mxc_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	/*
+	 * If HW ECC is enabled, we turn it on during init.  There is
+	 * no need to enable again here.
+	 */
+}
+
+static int mxc_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+				 u_char *read_ecc, u_char *calc_ecc)
+{
+	/*
+	 * 1-Bit errors are automatically corrected in HW.  No need for
+	 * additional correction.  2-Bit errors cannot be corrected by
+	 * HW ECC, so we need to return failure
+	 */
+	u16 ecc_status = NFC_ECC_STATUS_RESULT;
+
+	if (((ecc_status & 0x3) == 2) || ((ecc_status >> 2) == 2)) {
+		MTDDEBUG(MTD_DEBUG_LEVEL0,
+			 "MXC_NAND: HWECC uncorrectable 2-bit ECC error\n");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int mxc_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+				  u_char *ecc_code)
+{
+	/*
+	 * Just return success.  HW ECC does not read/write the NFC spare
+	 * buffer.  Only the FLASH spare area contains the calcuated ECC.
+	 */
+	return 0;
+}
+
+/**
+ * This function reads byte from the NAND Flash
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ *
+ * @return    data read from the NAND Flash
+ */
+static u_char mxc_nand_read_byte(struct mtd_info *mtd)
+{
+	u_char ret_val = 0;
+	u16 col, rd_word;
+	volatile u16 *mainbuf = MAIN_AREA0;
+	volatile u16 *sparebuf = SPARE_AREA0;
+
+	/* Check for status request */
+	if (g_nandfc_info.status_request)
+		return get_dev_status() & 0xFF;
+
+	/* Get column for 16-bit access */
+	col = g_nandfc_info.col_addr >> 1;
+
+	/* If we are accessing the spare region */
+	if (g_nandfc_info.spare_only)
+		rd_word = sparebuf[col];
+	else
+		rd_word = mainbuf[col];
+
+	/* Pick upper/lower byte of word from RAM buffer */
+	if (g_nandfc_info.col_addr & 0x1)
+		ret_val = (rd_word >> 8) & 0xFF;
+	else
+		ret_val = rd_word & 0xFF;
+
+	/* Update saved column address */
+	g_nandfc_info.col_addr++;
+
+	return ret_val;
+}
+
+/**
+  * This function reads word from the NAND Flash
+  *
+  * @param       mtd     MTD structure for the NAND Flash
+  *
+  * @return    data read from the NAND Flash
+  */
+static u16 mxc_nand_read_word(struct mtd_info *mtd)
+{
+	u16 col;
+	u16 rd_word, ret_val;
+	volatile u16 *p;
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nand_read_word(col = %d)\n", g_nandfc_info.col_addr);
+
+	col = g_nandfc_info.col_addr;
+	/* Adjust saved column address */
+	if (col < mtd->writesize && g_nandfc_info.spare_only)
+		col += mtd->writesize;
+
+	if (col < mtd->writesize)
+		p = (MAIN_AREA0) + (col >> 1);
+	else
+		p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1);
+
+	if (col & 1) {
+		rd_word = *p;
+		ret_val = (rd_word >> 8) & 0xff;
+		rd_word = *(p + 1);
+		ret_val |= (rd_word << 8) & 0xff00;
+
+	} else
+		ret_val = *p;
+
+	/* Update saved column address */
+	g_nandfc_info.col_addr = col + 2;
+
+	return ret_val;
+}
+
+/**
+ * This function writes data of length \b len to buffer \b buf. The data
+ * to be written on NAND Flash is first copied to RAMbuffer. After the
+ * Data Input Operation by the NFC, the data is written to NAND Flash.
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ * @param       buf     data to be written to NAND Flash
+ * @param       len     number of bytes to be written
+ */
+static void mxc_nand_write_buf(struct mtd_info *mtd,
+			       const u_char *buf, int len)
+{
+	int n;
+	int col;
+	int i = 0;
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nand_write_buf(col = %d, len = %d)\n",
+		 g_nandfc_info.col_addr, len);
+
+	col = g_nandfc_info.col_addr;
+
+	/* Adjust saved column address */
+	if (col < mtd->writesize && g_nandfc_info.spare_only)
+		col += mtd->writesize;
+
+	n = mtd->writesize + mtd->oobsize - col;
+	if (len > mtd->writesize + mtd->oobsize - col)
+		MTDDEBUG(MTD_DEBUG_LEVEL1, "Error: too much data.\n");
+
+	n = min(len, n);
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "%s:%d: col = %d, n = %d\n", __FUNCTION__, __LINE__, col, n);
+
+	while (n) {
+		volatile u32 *p;
+		if (col < mtd->writesize)
+			p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+		else
+			p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+					     mtd->writesize + (col & ~3));
+
+		MTDDEBUG(MTD_DEBUG_LEVEL3, "%s:%d: p = %p\n",
+			 __FUNCTION__, __LINE__, p);
+
+		if (((col | (int)&buf[i]) & 3) || n < 16) {
+			u32 data = 0;
+
+			if (col & 3 || n < 4)
+				data = *p;
+
+			switch (col & 3) {
+			case 0:
+				if (n) {
+					data = (data & 0xffffff00) |
+					    (buf[i++] << 0);
+					n--;
+					col++;
+				}
+			case 1:
+				if (n) {
+					data = (data & 0xffff00ff) |
+					    (buf[i++] << 8);
+					n--;
+					col++;
+				}
+			case 2:
+				if (n) {
+					data = (data & 0xff00ffff) |
+					    (buf[i++] << 16);
+					n--;
+					col++;
+				}
+			case 3:
+				if (n) {
+					data = (data & 0x00ffffff) |
+					    (buf[i++] << 24);
+					n--;
+					col++;
+				}
+			}
+
+			*p = data;
+		} else {
+			int m = mtd->writesize - col;
+
+			if (col >= mtd->writesize)
+				m += mtd->oobsize;
+
+			m = min(n, m) & ~3;
+
+			MTDDEBUG(MTD_DEBUG_LEVEL3,
+				 "%s:%d: n = %d, m = %d, i = %d, col = %d\n",
+				 __FUNCTION__, __LINE__, n, m, i, col);
+
+			memcpy_32((void *)(p), &buf[i], m);
+			col += m;
+			i += m;
+			n -= m;
+		}
+	}
+	/* Update saved column address */
+	g_nandfc_info.col_addr = col;
+}
+
+/**
+ * This function id is used to read the data buffer from the NAND Flash. To
+ * read the data from NAND Flash first the data output cycle is initiated by
+ * the NFC, which copies the data to RAMbuffer. This data of length \b len is
+ * then copied to buffer \b buf.
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ * @param       buf     data to be read from NAND Flash
+ * @param       len     number of bytes to be read
+ */
+static void mxc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int n;
+	int col;
+	int i = 0;
+
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nand_read_buf(col = %d, len = %d)\n",
+		 g_nandfc_info.col_addr, len);
+
+	col = g_nandfc_info.col_addr;
+	/* Adjust saved column address */
+	if (col < mtd->writesize && g_nandfc_info.spare_only)
+		col += mtd->writesize;
+
+	n = mtd->writesize + mtd->oobsize - col;
+	n = min(len, n);
+
+	while (n) {
+		volatile u32 *p;
+
+		if (col < mtd->writesize)
+			p = (volatile u32 *)((ulong) (MAIN_AREA0) + (col & ~3));
+		else
+			p = (volatile u32 *)((ulong) (SPARE_AREA0) -
+					     mtd->writesize + (col & ~3));
+
+		if (((col | (int)&buf[i]) & 3) || n < 16) {
+			u32 data;
+
+			data = *p;
+			switch (col & 3) {
+			case 0:
+				if (n) {
+					buf[i++] = (u8) (data);
+					n--;
+					col++;
+				}
+			case 1:
+				if (n) {
+					buf[i++] = (u8) (data >> 8);
+					n--;
+					col++;
+				}
+			case 2:
+				if (n) {
+					buf[i++] = (u8) (data >> 16);
+					n--;
+					col++;
+				}
+			case 3:
+				if (n) {
+					buf[i++] = (u8) (data >> 24);
+					n--;
+					col++;
+				}
+			}
+		} else {
+			int m = mtd->writesize - col;
+
+			if (col >= mtd->writesize)
+				m += mtd->oobsize;
+
+			m = min(n, m) & ~3;
+			memcpy_32(&buf[i], (void *)(p), m);
+			col += m;
+			i += m;
+			n -= m;
+		}
+	}
+	/* Update saved column address */
+	g_nandfc_info.col_addr = col;
+}
+
+/**
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ * @param       buf     data to be verified
+ * @param       len     length of the data to be verified
+ *
+ * @return      -EFAULT if error else 0
+ */
+static int
+mxc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	return -1; /* Was -EFAULT */
+}
+
+/**
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip.
+ *
+ * @param       mtd     MTD structure for the NAND Flash
+ * @param       chip    val indicating select or deselect
+ */
+static void mxc_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+#ifdef CONFIG_MTD_NAND_MXC_FORCE_CE
+	if (chip > 0) {
+		MTDDEBUG(MTD_DEBUG_LEVEL0,
+			 "ERROR:  Illegal chip select (chip = %d)\n", chip);
+		return;
+	}
+
+	if (chip == -1) {
+		NFC_CONFIG1 &= ~NFC_CE;
+		return;
+	}
+
+	NFC_CONFIG1 |= NFC_CE;
+#endif
+}
+
+/**
+ * This function is used by the upper layer to write command to NAND Flash
+ * for different operations to be carried out on NAND Flash
+ *
+ * @param       mtd             MTD structure for the NAND Flash
+ * @param       command         command for NAND Flash
+ * @param       column          column offset for the page read
+ * @param       page_addr       page to be read from NAND Flash
+ */
+static void mxc_nand_command(struct mtd_info *mtd, unsigned command,
+			     int column, int page_addr)
+{
+	MTDDEBUG(MTD_DEBUG_LEVEL3,
+		 "mxc_nand_command (cmd = 0x%x, col = 0x%x, page = 0x%x)\n",
+		 command, column, page_addr);
+
+	/*
+	 * Reset command state information
+	 */
+	g_nandfc_info.status_request = 0;
+
+	/*
+	 * Command pre-processing step
+	 */
+	switch (command) {
+
+	case NAND_CMD_STATUS:
+		g_nandfc_info.col_addr = 0;
+		g_nandfc_info.status_request = 1;
+		break;
+
+	case NAND_CMD_READ0:
+		g_nandfc_info.col_addr = column;
+		g_nandfc_info.spare_only = 0;
+		break;
+
+	case NAND_CMD_READOOB:
+		g_nandfc_info.col_addr = column;
+		g_nandfc_info.spare_only = 1;
+		if (is2k_pagesize)
+			command = NAND_CMD_READ0; /* only READ0 is valid */
+		break;
+
+	case NAND_CMD_SEQIN:
+		if (column >= mtd->writesize) {
+			if (is2k_pagesize) {
+				/*
+				 * FIXME: before send SEQIN command for
+				 * write OOB, we must read one page out.
+				 * For K9F1GXX has no READ1 command to set
+				 * current HW pointer to spare area, we must
+				 * write the whole page including OOB
+				 * together.
+				 */
+				/* call itself to read a page */
+				mxc_nand_command(mtd, NAND_CMD_READ0, 0,
+						 page_addr);
+			}
+			g_nandfc_info.col_addr = column - mtd->writesize;
+			g_nandfc_info.spare_only = 1;
+			/* Set program pointer to spare region */
+			if (!is2k_pagesize)
+				send_cmd(NAND_CMD_READOOB, 0);
+		} else {
+			g_nandfc_info.spare_only = 0;
+			g_nandfc_info.col_addr = column;
+			/* Set program pointer to page start */
+			if (!is2k_pagesize)
+				send_cmd(NAND_CMD_READ0, 0);
+		}
+		break;
+
+	case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+		if (ecc_disabled) {
+			/* Enable Ecc for page writes */
+			NFC_CONFIG1 |= NFC_ECC_EN;
+		}
+#endif
+		send_prog_page(0, g_nandfc_info.spare_only);
+
+		if (is2k_pagesize) {
+			/* data in 4 areas datas */
+			send_prog_page(1, g_nandfc_info.spare_only);
+			send_prog_page(2, g_nandfc_info.spare_only);
+			send_prog_page(3, g_nandfc_info.spare_only);
+		}
+
+		break;
+
+	case NAND_CMD_ERASE1:
+		break;
+	}
+
+	/*
+	 * Write out the command to the device.
+	 */
+	send_cmd(command, 0);
+
+	/*
+	 * Write out column address, if necessary
+	 */
+	if (column != -1) {
+		/*
+		 * MXC NANDFC can only perform full page+spare or
+		 * spare-only read/write.  When the upper layers
+		 * layers perform a read/write buf operation,
+		 * we will used the saved column adress to index into
+		 * the full page.
+		 */
+		send_addr(0, page_addr == -1);
+		if (is2k_pagesize)
+			/* another col addr cycle for 2k page */
+			send_addr(0, 0);
+	}
+
+	/*
+	 * Write out page address, if necessary
+	 */
+	if (page_addr != -1) {
+		/* paddr_0 - p_addr_7 */
+		send_addr((page_addr & 0xff), 0);
+
+		if (is2k_pagesize) {
+			/* One more address cycle for higher
+			 * density devices */
+
+			if (mtd->size >= 0x10000000) {
+				/* paddr_8 - paddr_15 */
+				send_addr((page_addr >> 8) & 0xff, 0);
+				send_addr((page_addr >> 16) & 0xff, 1);
+			} else
+				/* paddr_8 - paddr_15 */
+				send_addr((page_addr >> 8) & 0xff, 1);
+		} else {
+			/* One more address cycle for higher
+			 * density devices */
+
+			if (mtd->size >= 0x4000000) {
+				/* paddr_8 - paddr_15 */
+				send_addr((page_addr >> 8) & 0xff, 0);
+				send_addr((page_addr >> 16) & 0xff, 1);
+			} else
+				/* paddr_8 - paddr_15 */
+				send_addr((page_addr >> 8) & 0xff, 1);
+		}
+	}
+
+	/*
+	 * Command post-processing step
+	 */
+	switch (command) {
+
+	case NAND_CMD_RESET:
+		break;
+
+	case NAND_CMD_READOOB:
+	case NAND_CMD_READ0:
+		if (is2k_pagesize) {
+			/* send read confirm command */
+			send_cmd(NAND_CMD_READSTART, 1);
+			/* read for each AREA */
+			send_read_page(0, g_nandfc_info.spare_only);
+			send_read_page(1, g_nandfc_info.spare_only);
+			send_read_page(2, g_nandfc_info.spare_only);
+			send_read_page(3, g_nandfc_info.spare_only);
+		} else
+			send_read_page(0, g_nandfc_info.spare_only);
+		break;
+
+	case NAND_CMD_READID:
+		send_read_id();
+		break;
+
+	case NAND_CMD_PAGEPROG:
+#ifndef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+		if (ecc_disabled) {
+			/* Disable Ecc after page writes */
+			NFC_CONFIG1 &= ~NFC_ECC_EN;
+		}
+#endif
+		break;
+
+	case NAND_CMD_STATUS:
+		break;
+
+	case NAND_CMD_ERASE2:
+		break;
+	}
+}
+
+static int mxc_nand_scan_bbt(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Config before scanning */
+	/* Do not rely on NFMS_BIT, set/clear NFMS bit based
+	 * on mtd->writesize */
+	if (mtd->writesize == 2048) {
+		NFMS |= 1 << NFMS_BIT;
+		is2k_pagesize = 1;
+	} else {
+		if ((NFMS >> NFMS_BIT) & 0x1) {
+			/* This case has happened on some SoCs */
+			printk(KERN_INFO
+			       "NFMS Bit set for 512B Page, resetting it."
+			       " [RCSR: 0x%08x]\n",
+			       NFMS);
+			NFMS &= ~(1 << NFMS_BIT);
+		}
+		is2k_pagesize = 0;
+	}
+
+	if (is2k_pagesize)
+		this->ecc.layout = &nand_hw_eccoob_2k;
+
+	/* use flash based bbt */
+	this->bbt_td = &bbt_main_descr;
+	this->bbt_md = &bbt_mirror_descr;
+
+	/* update flash based bbt */
+	this->options |= NAND_USE_FLASH_BBT;
+
+	if (!this->badblock_pattern) {
+		if (mtd->writesize == 2048)
+			this->badblock_pattern = &smallpage_memorybased;
+		else
+			this->badblock_pattern = (mtd->writesize > 512) ?
+			    &largepage_memorybased : &smallpage_memorybased;
+	}
+	/* Build bad block table */
+	return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+int board_nand_init(struct nand_chip *nand)
+{
+	struct mtd_info *mtd;
+
+	mxc_nand_data = malloc(sizeof(struct mxc_mtd_s));
+	if (!mxc_nand_data) {
+		printf("mxc_nd: No memory from malloc!\n");
+		return -1;
+	}
+	memset(mxc_nand_data, 0, sizeof(struct mxc_mtd_s));
+
+	mtd = &mxc_nand_data->mtd;
+	mtd->priv = nand;
+	nand->priv = mxc_nand_data;
+
+	nand->chip_delay = 0;
+
+	nand->dev_ready = mxc_nand_dev_ready;
+	nand->cmdfunc = mxc_nand_command;
+	nand->select_chip = mxc_nand_select_chip;
+	nand->read_byte = mxc_nand_read_byte;
+	nand->read_word = mxc_nand_read_word;
+	nand->write_buf = mxc_nand_write_buf;
+	nand->read_buf = mxc_nand_read_buf;
+	nand->verify_buf = mxc_nand_verify_buf;
+	nand->scan_bbt = mxc_nand_scan_bbt;
+
+	NFC_CONFIG1 |= NFC_INT_MSK;
+
+	if (hardware_ecc) {
+		nand->ecc.calculate = mxc_nand_calculate_ecc;
+		nand->ecc.hwctl = mxc_nand_enable_hwecc;
+		nand->ecc.correct = mxc_nand_correct_data;
+		nand->ecc.mode = NAND_ECC_HW;
+		nand->ecc.layout = &nand_hw_eccoob_8;
+		nand->ecc.size = 512;
+		nand->ecc.bytes = 3;
+		NFC_CONFIG1 |= NFC_ECC_EN;
+	} else
+		nand->ecc.mode = NAND_ECC_SOFT;
+
+	/* Reset NAND */
+	nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Unlock the internal RAM buffer */
+	NFC_CONFIG = 0x2;
+
+	/* Block to be unlocked */
+	NFC_UNLOCKSTART_BLKADDR = 0x0;
+	NFC_UNLOCKEND_BLKADDR = 0x4000;
+
+	/* Unlock Block Command for given address range */
+	NFC_WRPROT = 0x4;
+
+	/* Only 8 bit bus support for now */
+	nand->options |= 0;
+
+	if ((NFMS >> NFMS_BIT) & 1) {
+		is2k_pagesize = 1;
+		nand->ecc.layout = &nand_hw_eccoob_2k;
+	} else
+		is2k_pagesize = 0;
+
+	return 0;
+}
diff --git a/include/asm-arm/arch-mx31/mx31-regs.h b/include/asm-arm/arch-mx31/mx31-regs.h
index b04a718..78825f5 100644
--- a/include/asm-arm/arch-mx31/mx31-regs.h
+++ b/include/asm-arm/arch-mx31/mx31-regs.h
@@ -168,4 +168,100 @@
 #define CS5_BASE	0xB6000000
 #define PCMCIA_MEM_BASE	0xC0000000
 
+/*
+ * NAND controller
+ */
+#define NFC_BASE_ADDR	0xB8000000
+
+/*
+ * Addresses for NFC registers
+ */
+#define NFC_BUF_SIZE            (*((volatile u16 *)(NFC_BASE_ADDR + 0xE00)))
+#define NFC_BUF_ADDR            (*((volatile u16 *)(NFC_BASE_ADDR + 0xE04)))
+#define NFC_FLASH_ADDR          (*((volatile u16 *)(NFC_BASE_ADDR + 0xE06)))
+#define NFC_FLASH_CMD           (*((volatile u16 *)(NFC_BASE_ADDR + 0xE08)))
+#define NFC_CONFIG              (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0A)))
+#define NFC_ECC_STATUS_RESULT   (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0C)))
+#define NFC_RSLTMAIN_AREA       (*((volatile u16 *)(NFC_BASE_ADDR + 0xE0E)))
+#define NFC_RSLTSPARE_AREA      (*((volatile u16 *)(NFC_BASE_ADDR + 0xE10)))
+#define NFC_WRPROT              (*((volatile u16 *)(NFC_BASE_ADDR + 0xE12)))
+#define NFC_UNLOCKSTART_BLKADDR (*((volatile u16 *)(NFC_BASE_ADDR + 0xE14)))
+#define NFC_UNLOCKEND_BLKADDR   (*((volatile u16 *)(NFC_BASE_ADDR + 0xE16)))
+#define NFC_NF_WRPRST           (*((volatile u16 *)(NFC_BASE_ADDR + 0xE18)))
+#define NFC_CONFIG1             (*((volatile u16 *)(NFC_BASE_ADDR + 0xE1A)))
+#define NFC_CONFIG2             (*((volatile u16 *)(NFC_BASE_ADDR + 0xE1C)))
+
+/*
+ * Addresses for NFC RAM BUFFER Main area 0
+ */
+#define MAIN_AREA0        (volatile u16 *)(NFC_BASE_ADDR + 0x000)
+#define MAIN_AREA1        (volatile u16 *)(NFC_BASE_ADDR + 0x200)
+#define MAIN_AREA2        (volatile u16 *)(NFC_BASE_ADDR + 0x400)
+#define MAIN_AREA3        (volatile u16 *)(NFC_BASE_ADDR + 0x600)
+
+/*
+ * Addresses for NFC SPARE BUFFER Spare area 0
+ */
+#define SPARE_AREA0       (volatile u16 *)(NFC_BASE_ADDR + 0x800)
+#define SPARE_AREA1       (volatile u16 *)(NFC_BASE_ADDR + 0x810)
+#define SPARE_AREA2       (volatile u16 *)(NFC_BASE_ADDR + 0x820)
+#define SPARE_AREA3       (volatile u16 *)(NFC_BASE_ADDR + 0x830)
+
+/*
+ * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command
+ * operation
+ */
+#define NFC_CMD            0x1
+
+/*
+ * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address
+ * operation
+ */
+#define NFC_ADDR           0x2
+
+/*
+ * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input
+ * operation
+ */
+#define NFC_INPUT          0x4
+
+/*
+ * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data
+ * Output operation
+ */
+#define NFC_OUTPUT         0x8
+
+/*
+ * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID
+ * operation
+ */
+#define NFC_ID             0x10
+
+/*
+ * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read
+ * Status operation
+ */
+#define NFC_STATUS         0x20
+
+/*
+ * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status
+ * operation
+ */
+#define NFC_INT            0x8000
+
+#define NFC_SP_EN           (1 << 2)
+#define NFC_ECC_EN          (1 << 3)
+#define NFC_INT_MSK         (1 << 4)
+#define NFC_BIG             (1 << 5)
+#define NFC_RST             (1 << 6)
+#define NFC_CE              (1 << 7)
+#define NFC_ONE_CYCLE       (1 << 8)
+
+/*
+ * NFMS bit in RCSR register for pagesize of nandflash
+ */
+#define NFMS		(*((volatile u32 *)CCM_RCSR))
+#define NFMS_BIT	30
+
 #endif /* __ASM_ARCH_MX31_REGS_H */
+
-- 
1.5.2.4



More information about the U-Boot mailing list