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

Magnus Lilja
2008-08-29T08:36:20Z

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@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@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