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 <[EMAIL PROTECTED]>
---
 cpu/arm1136/mx31/Makefile |    4 +-
 cpu/arm1136/mx31/mxc_nd.c | 1172 +++++++++++++++++++++++++++++++++++++++++++++
 cpu/arm1136/mx31/mxc_nd.h |  106 ++++
 3 files changed, 1281 insertions(+), 1 deletions(-)

diff --git a/cpu/arm1136/mx31/Makefile b/cpu/arm1136/mx31/Makefile
index b648ffd..bd654ee 100644
--- a/cpu/arm1136/mx31/Makefile
+++ b/cpu/arm1136/mx31/Makefile
@@ -25,8 +25,10 @@ include $(TOPDIR)/config.mk
 
 LIB    = $(obj)lib$(SOC).a
 
-COBJS  = interrupts.o serial.o generic.o
+COBJS-y        = interrupts.o serial.o generic.o 
+COBJS-$(CONFIG_MX31_NAND) += mxc_nd.o
 
+COBJS  := $(COBJS-y)
 SRCS   := $(SOBJS:.o=.S) $(COBJS:.o=.c)
 OBJS   := $(addprefix $(obj),$(SOBJS) $(COBJS))
 
diff --git a/cpu/arm1136/mx31/mxc_nd.c b/cpu/arm1136/mx31/mxc_nd.c
new file mode 100644
index 0000000..d281104
--- /dev/null
+++ b/cpu/arm1136/mx31/mxc_nd.c
@@ -0,0 +1,1172 @@
+/*
+ * (C) Copyright 2008 Magnus Lilja <[EMAIL PROTECTED]>
+ *
+ * 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 "mxc_nd.h"
+
+/* The bool type is used locally in this file, added for U-boot. */
+typedef enum {false = 0, true = 1 } bool;
+
+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 {
+       bool bSpareOnly;
+       bool bStatusRequest;
+       u16 colAddr;
+};
+
+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       maxRetries     number of retry attempts (separated by 1 us)
+ * @param       param          parameter for debug
+ * @param       useirq         True if IRQ should be used rather than polling
+ */
+static void wait_op_done(int maxRetries, u16 param, bool useirq)
+{
+       while (maxRetries-- > 0) {
+               if (NFC_CONFIG2 & NFC_INT) {
+                       NFC_CONFIG2 &= ~NFC_INT;
+                       break;
+               }
+               udelay(1);
+       }
+       if (maxRetries <= 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  True if IRQ should be used rather than polling
+ */
+static void send_cmd(u16 cmd, bool 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  True if this is the last address cycle for command
+ */
+static void send_addr(u16 addr, bool 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       bSpareOnly    set true if only the spare area is transferred
+ */
+static void send_prog_page(u8 buf_id, bool bSpareOnly)
+{
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "send_prog_page (%d)\n", bSpareOnly);
+
+       /* NANDFC buffer 0 is used for page read/write */
+
+       NFC_BUF_ADDR = buf_id;
+
+       /* Configure spare or page+spare access */
+       if (!is2k_Pagesize) {
+               if (bSpareOnly)
+                       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, bSpareOnly, true);
+}
+
+/**
+ * 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  bSpareOnly          set to true if only spare area needs correction
+ */
+static void mxc_nd_correct_error(u8 buf_id, u16 eccpos, bool bSpareOnly)
+{
+       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 (!bSpareOnly)
+               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 true if only spare area needs correction
+ */
+static void mxc_nd_correct_ecc(u8 buf_id, bool spare)
+{
+#ifdef CONFIG_MTD_NAND_MXC_ECC_CORRECTION_OPTION2
+       /* To maintain single bit error in previous page */
+       static int lastErrMain, lastErrSpare;
+#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 (lastErrMain && !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, false);
+               } else
+                       /* Set if single bit error in current page */
+                       lastErrMain = 1;
+       } else
+               /* Reset if no single bit error in current page */
+               lastErrMain = 0;
+
+       /* Check for Error in Sparearea */
+       if ((ecc_status & 0x3) == SPARE_SINGLEBIT_ERROR) {
+               /* Check for error in previous page */
+               if (lastErrSpare) {
+                       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, true);
+               } else
+                       /* Set if single bit error in current page */
+                       lastErrSpare = 1;
+       } else
+               /* Reset if no single bit error in current page */
+               lastErrSpare = 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, false);
+                       }
+                       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, true);
+                       }
+
+               } 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       bSpareOnly     set true if only the spare area is
+ * transferred
+ */
+static void send_read_page(u8 buf_id, bool bSpareOnly)
+{
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "send_read_page (%d)\n", bSpareOnly);
+
+       /* NANDFC buffer 0 is used for page read/write */
+       NFC_BUF_ADDR = buf_id;
+
+       /* Configure spare or page+spare access */
+       if (!is2k_Pagesize) {
+               if (bSpareOnly)
+                       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, bSpareOnly, true);
+
+       /* 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, bSpareOnly);
+}
+
+/**
+ * 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, true);
+
+       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, true);
+
+       /* 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 retVal = 0;
+       u16 col, rdWord;
+       volatile u16 *mainBuf = MAIN_AREA0;
+       volatile u16 *spareBuf = SPARE_AREA0;
+
+       /* Check for status request */
+       if (g_nandfc_info.bStatusRequest)
+               return get_dev_status() & 0xFF;
+
+       /* Get column for 16-bit access */
+       col = g_nandfc_info.colAddr >> 1;
+
+       /* If we are accessing the spare region */
+       if (g_nandfc_info.bSpareOnly)
+               rdWord = spareBuf[col];
+       else
+               rdWord = mainBuf[col];
+
+       /* Pick upper/lower byte of word from RAM buffer */
+       if (g_nandfc_info.colAddr & 0x1)
+               retVal = (rdWord >> 8) & 0xFF;
+       else
+               retVal = rdWord & 0xFF;
+
+       /* Update saved column address */
+       g_nandfc_info.colAddr++;
+
+       return retVal;
+}
+
+/**
+  * 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 rdWord, retVal;
+       volatile u16 *p;
+
+       MTDDEBUG(MTD_DEBUG_LEVEL3,
+                "mxc_nand_read_word(col = %d)\n", g_nandfc_info.colAddr);
+
+       col = g_nandfc_info.colAddr;
+       /* Adjust saved column address */
+       if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+               col += mtd->writesize;
+
+       if (col < mtd->writesize)
+               p = (MAIN_AREA0) + (col >> 1);
+       else
+               p = (SPARE_AREA0) + ((col - mtd->writesize) >> 1);
+
+       if (col & 1) {
+               rdWord = *p;
+               retVal = (rdWord >> 8) & 0xff;
+               rdWord = *(p + 1);
+               retVal |= (rdWord << 8) & 0xff00;
+
+       } else
+               retVal = *p;
+
+       /* Update saved column address */
+       g_nandfc_info.colAddr = col + 2;
+
+       return retVal;
+}
+
+/**
+ * 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.colAddr, len);
+
+       col = g_nandfc_info.colAddr;
+
+       /* Adjust saved column address */
+       if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+               col += mtd->writesize;
+
+       n = mtd->writesize + mtd->oobsize - col;
+       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.colAddr = 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.colAddr, len);
+
+       col = g_nandfc_info.colAddr;
+       /* Adjust saved column address */
+       if (col < mtd->writesize && g_nandfc_info.bSpareOnly)
+               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.colAddr = 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)
+{
+       bool useirq = false;
+
+       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.bStatusRequest = false;
+
+       /*
+        * Command pre-processing step
+        */
+       switch (command) {
+
+       case NAND_CMD_STATUS:
+               g_nandfc_info.colAddr = 0;
+               g_nandfc_info.bStatusRequest = true;
+               break;
+
+       case NAND_CMD_READ0:
+               g_nandfc_info.colAddr = column;
+               g_nandfc_info.bSpareOnly = false;
+               useirq = false;
+               break;
+
+       case NAND_CMD_READOOB:
+               g_nandfc_info.colAddr = column;
+               g_nandfc_info.bSpareOnly = true;
+               useirq = false;
+               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.colAddr = column - mtd->writesize;
+                       g_nandfc_info.bSpareOnly = true;
+                       /* Set program pointer to spare region */
+                       if (!is2k_Pagesize)
+                               send_cmd(NAND_CMD_READOOB, false);
+               } else {
+                       g_nandfc_info.bSpareOnly = false;
+                       g_nandfc_info.colAddr = column;
+                       /* Set program pointer to page start */
+                       if (!is2k_Pagesize)
+                               send_cmd(NAND_CMD_READ0, false);
+               }
+               useirq = false;
+               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.bSpareOnly);
+
+               if (is2k_Pagesize) {
+                       /* data in 4 areas datas */
+                       send_prog_page(1, g_nandfc_info.bSpareOnly);
+                       send_prog_page(2, g_nandfc_info.bSpareOnly);
+                       send_prog_page(3, g_nandfc_info.bSpareOnly);
+               }
+
+               break;
+
+       case NAND_CMD_ERASE1:
+               useirq = false;
+               break;
+       }
+
+       /*
+        * Write out the command to the device.
+        */
+       send_cmd(command, useirq);
+
+       /*
+        * 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, false);
+       }
+
+       /*
+        * Write out page address, if necessary
+        */
+       if (page_addr != -1) {
+               /* paddr_0 - p_addr_7 */
+               send_addr((page_addr & 0xff), false);
+
+               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, false);
+                               send_addr((page_addr >> 16) & 0xff, true);
+                       } else
+                               /* paddr_8 - paddr_15 */
+                               send_addr((page_addr >> 8) & 0xff, true);
+               } else {
+                       /* One more address cycle for higher
+                        * density devices */
+
+                       if (mtd->size >= 0x4000000) {
+                               /* paddr_8 - paddr_15 */
+                               send_addr((page_addr >> 8) & 0xff, false);
+                               send_addr((page_addr >> 16) & 0xff, true);
+                       } else
+                               /* paddr_8 - paddr_15 */
+                               send_addr((page_addr >> 8) & 0xff, true);
+               }
+       }
+
+       /*
+        * 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, true);
+                       /* read for each AREA */
+                       send_read_page(0, g_nandfc_info.bSpareOnly);
+                       send_read_page(1, g_nandfc_info.bSpareOnly);
+                       send_read_page(2, g_nandfc_info.bSpareOnly);
+                       send_read_page(3, g_nandfc_info.bSpareOnly);
+               } else
+                       send_read_page(0, g_nandfc_info.bSpareOnly);
+               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);
+}
+
+#ifdef CONFIG_MXC_NAND_LOW_LEVEL_ERASE
+static void mxc_low_erase(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+       unsigned int page_addr, addr;
+       u_char status;
+
+       MTDDEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : mxc_low_erase:Erasing NAND\n");
+       for (addr = 0; addr < this->chipsize; addr += mtd->erasesize) {
+               page_addr = addr / mtd->writesize;
+               mxc_nand_command(mtd, NAND_CMD_ERASE1, -1, page_addr);
+               mxc_nand_command(mtd, NAND_CMD_ERASE2, -1, -1);
+               mxc_nand_command(mtd, NAND_CMD_STATUS, -1, -1);
+               status = mxc_nand_read_byte(mtd);
+               if (status & NAND_STATUS_FAIL) {
+                       printk(KERN_ERR
+                              "ERASE FAILED(block = %d,status = 0x%x)\n",
+                              addr / mtd->erasesize, status);
+               }
+       }
+
+}
+#endif
+
+int board_nand_init(struct nand_chip *nand)
+{
+       struct nand_chip *this;
+       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));
+
+       memset((char *)&g_nandfc_info, 0, sizeof(g_nandfc_info));
+
+       this = nand;
+       mtd = &mxc_nand_data->mtd;
+       mtd->priv = this;
+       this->priv = mxc_nand_data;
+
+       /* 50 us command delay time */
+       this->chip_delay = 5;
+
+       this->dev_ready = mxc_nand_dev_ready;
+       this->cmdfunc = mxc_nand_command;
+       this->select_chip = mxc_nand_select_chip;
+       this->read_byte = mxc_nand_read_byte;
+       this->read_word = mxc_nand_read_word;
+       this->write_buf = mxc_nand_write_buf;
+       this->read_buf = mxc_nand_read_buf;
+       this->verify_buf = mxc_nand_verify_buf;
+       this->scan_bbt = mxc_nand_scan_bbt;
+
+       NFC_CONFIG1 |= NFC_INT_MSK;
+
+       if (hardware_ecc) {
+               this->ecc.calculate = mxc_nand_calculate_ecc;
+               this->ecc.hwctl = mxc_nand_enable_hwecc;
+               this->ecc.correct = mxc_nand_correct_data;
+               this->ecc.mode = NAND_ECC_HW;
+               this->ecc.layout = &nand_hw_eccoob_8;
+               this->ecc.size = 512;
+               this->ecc.bytes = 3;
+               NFC_CONFIG1 |= NFC_ECC_EN;
+       } else
+               this->ecc.mode = NAND_ECC_SOFT;
+
+       /* Reset NAND */
+       this->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 */
+       this->options |= 0;
+
+       if ((NFMS >> NFMS_BIT) & 1) {
+               is2k_Pagesize = 1;
+               this->ecc.layout = &nand_hw_eccoob_2k;
+       }
+       else
+               is2k_Pagesize = 0;
+
+       return 0;
+}
+
diff --git a/cpu/arm1136/mx31/mxc_nd.h b/cpu/arm1136/mx31/mxc_nd.h
new file mode 100644
index 0000000..14aeaa3
--- /dev/null
+++ b/cpu/arm1136/mx31/mxc_nd.h
@@ -0,0 +1,106 @@
+/*
+ * (C) Copyright 2008 Magnus Lilja <[EMAIL PROTECTED]>
+ *
+ * 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
+ */
+
+#ifndef __MXC_ND_H__
+#define __MXC_ND_H__
+
+#include <asm-arm/arch/mx31-regs.h>
+
+/*
+ * 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)
+
+#endif                         /* MXCND_H */
+
-- 
1.5.2.4


-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
U-Boot-Users mailing list
U-Boot-Users@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/u-boot-users

Reply via email to