ADS5121 rev4 / MPC5121e rev2 only

Only tested with:
    2K page size
    8 bit device width

This controller treats 2K pages as 4 512 byte pages
and the hw ecc is over the combined 512 byte main
area and the first 7 bytes of the spare area.

The hw ecc is stored in the last 9 bytes of the
spare area.

This all means the the spare area can not be written
separately from the main.  This means unmodified JFFS2
will not work.

Signed-off-by: John Rigby <[EMAIL PROTECTED]>
---
 board/ads5121/ads5121.c             |    1 +
 drivers/mtd/nand/Makefile           |    1 +
 drivers/mtd/nand/mpc5121rev2_nand.c | 1122 +++++++++++++++++++++++++++++++++++
 include/configs/ads5121.h           |   27 +
 4 files changed, 1151 insertions(+), 0 deletions(-)
 create mode 100644 drivers/mtd/nand/mpc5121rev2_nand.c

diff --git a/board/ads5121/ads5121.c b/board/ads5121/ads5121.c
index 0610928..3329f61 100644
--- a/board/ads5121/ads5121.c
+++ b/board/ads5121/ads5121.c
@@ -34,6 +34,7 @@
 /* Clocks in use */
 #define SCCR1_CLOCKS_EN        (CLOCK_SCCR1_CFG_EN |                           
\
                         CLOCK_SCCR1_LPC_EN |                           \
+                        CLOCK_SCCR1_NFC_EN |                           \
                         CLOCK_SCCR1_PSC_EN(CONFIG_PSC_CONSOLE) |       \
                         CLOCK_SCCR1_PSCFIFO_EN |                       \
                         CLOCK_SCCR1_DDR_EN |                           \
diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile
index b0abe6e..7addda6 100644
--- a/drivers/mtd/nand/Makefile
+++ b/drivers/mtd/nand/Makefile
@@ -38,6 +38,7 @@ endif
 COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o
 COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o
 COBJS-$(CONFIG_NAND_S3C64XX) += s3c64xx.o
+COBJS-$(CONFIG_NAND_MPC5121) += mpc5121rev2_nand.o
 endif
 
 COBJS  := $(COBJS-y)
diff --git a/drivers/mtd/nand/mpc5121rev2_nand.c 
b/drivers/mtd/nand/mpc5121rev2_nand.c
new file mode 100644
index 0000000..88555ab
--- /dev/null
+++ b/drivers/mtd/nand/mpc5121rev2_nand.c
@@ -0,0 +1,1122 @@
+/*
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * Based on drivers/mtd/nand/mpc5121_nand.c
+ * which was forked from drivers/mtd/nand/mxc_nd.c
+ */
+
+/*
+ * 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 <malloc.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+
+#include <asm/io.h>
+#include <nand.h>
+
+static struct mpc5121_nand_private {
+       struct mtd_info mtd;
+       char spare_only;
+       char status_req;
+       u16 col_addr;
+       int sparesize;
+       int width;
+       int chipsel;
+} *priv;
+
+#define IS_2K_PAGE_NAND                (mtd->writesize == 2048)
+#define IS_4K_PAGE_NAND                (mtd->writesize == 4096)
+#define IS_LARGE_PAGE_NAND     (mtd->writesize > 512)
+
+#define NFC_REG_BASE ((void *)CONFIG_SYS_NAND_BASE)
+/*
+ * MPC5121 Rev2 NFC registers Definition
+ */
+#define NFC_BUF_ADDR                   (NFC_REG_BASE + 0x1E04)
+#define NFC_FLASH_ADDR                 (NFC_REG_BASE + 0x1E06)
+#define NFC_FLASH_CMD                  (NFC_REG_BASE + 0x1E08)
+#define NFC_CONFIG                     (NFC_REG_BASE + 0x1E0A)
+#define NFC_ECC_STATUS1                        (NFC_REG_BASE + 0x1E0C)
+#define NFC_ECC_STATUS2                        (NFC_REG_BASE + 0x1E0E)
+#define NFC_SPAS                       (NFC_REG_BASE + 0x1E10)
+#define NFC_WRPROT                     (NFC_REG_BASE + 0x1E12)
+#define NFC_NF_WRPRST                  (NFC_REG_BASE + 0x1E18)
+#define NFC_CONFIG1                    (NFC_REG_BASE + 0x1E1A)
+#define NFC_CONFIG2                    (NFC_REG_BASE + 0x1E1C)
+#define NFC_UNLOCKSTART_BLKADDR0       (NFC_REG_BASE + 0x1E20)
+#define NFC_UNLOCKEND_BLKADDR0         (NFC_REG_BASE + 0x1E22)
+#define NFC_UNLOCKSTART_BLKADDR1       (NFC_REG_BASE + 0x1E24)
+#define NFC_UNLOCKEND_BLKADDR1         (NFC_REG_BASE + 0x1E26)
+#define NFC_UNLOCKSTART_BLKADDR2       (NFC_REG_BASE + 0x1E28)
+#define NFC_UNLOCKEND_BLKADDR2         (NFC_REG_BASE + 0x1E2A)
+#define NFC_UNLOCKSTART_BLKADDR3       (NFC_REG_BASE + 0x1E2C)
+#define NFC_UNLOCKEND_BLKADDR3         (NFC_REG_BASE + 0x1E2E)
+
+/*!
+ * Addresses for NFC MAIN RAM BUFFER areas
+ */
+#define MAIN_AREA(n)                   (NFC_REG_BASE + (n)*0x200)
+
+/*!
+ * Addresses for NFC SPARE BUFFER areas
+ */
+#define SPARE_LEN                      0x40
+#define SPARE_AREA(n)                  (NFC_REG_BASE + 0x1000 + (n)*SPARE_LEN)
+
+#define NFC_CMD                                0x1
+#define NFC_ADDR                       0x2
+#define NFC_INPUT                      0x4
+#define NFC_OUTPUT                     0x8
+#define NFC_ID                         0x10
+#define NFC_STATUS                     0x20
+
+/* Bit Definitions */
+#define NFC_INT                                (1 << 15)
+#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)
+#define NFC_BLS_LOCKED                 0
+#define NFC_BLS_LOCKED_DEFAULT         1
+#define NFC_BLS_UNLOCKED               2
+#define NFC_WPC_LOCK_TIGHT             1
+#define NFC_WPC_LOCK                   (1 << 1)
+#define NFC_WPC_UNLOCK                 (1 << 2)
+#define NFC_FLASH_ADDR_SHIFT           0
+#define NFC_UNLOCK_END_ADDR_SHIFT      0
+
+#define NFC_ECC_MODE_4                 1
+/*
+ * Define delays in microsec for NAND device operations
+ */
+#define TROP_US_DELAY                  2000
+
+
+#ifdef CONFIG_MTD_NAND_MPC5121_SWECC
+static int hardware_ecc;
+#else
+static int hardware_ecc = 1;
+#endif
+
+/*
+ * OOB placement block for use with hardware ecc generation
+ */
+static struct nand_ecclayout nand_hw_eccoob_512 = {
+       .eccbytes = 9,
+       .eccpos = {
+               7, 8, 9, 10, 11, 12, 13, 14, 15,
+       },
+       .oobavail = 5,
+       .oobfree = {
+               {0, 5}
+       },
+};
+
+static struct nand_ecclayout nand_hw_eccoob_2k = {
+       .eccbytes = 36,
+       .eccpos = {
+               /* 9 bytes of ecc for each 512 bytes of data */
+               7, 8, 9, 10, 11, 12, 13, 14, 15,
+               23, 24, 25, 26, 27, 28, 29, 30, 31,
+               39, 40, 41, 42, 43, 44, 45, 46, 47,
+               55, 56, 57, 58, 59, 60, 61, 62, 63,
+       },
+       .oobavail = 26,
+       .oobfree = {
+               {0, 5},
+               {16, 7},
+               {32, 7},
+               {48, 7},
+       },
+};
+
+static struct nand_ecclayout nand_hw_eccoob_4k = {
+       .eccbytes = 64, /* actually 72 but only room for 64 */
+       .eccpos = {
+               /* 9 bytes of ecc for each 512 bytes of data */
+               7, 8, 9, 10, 11, 12, 13, 14, 15,
+               23, 24, 25, 26, 27, 28, 29, 30, 31,
+               39, 40, 41, 42, 43, 44, 45, 46, 47,
+               55, 56, 57, 58, 59, 60, 61, 62, 63,
+               71, 72, 73, 74, 75, 76, 77, 78, 79,
+               87, 88, 89, 90, 91, 92, 93, 94, 95,
+               103, 104, 105, 106, 107, 108, 109, 110, 111,
+               119, /* 120, 121, 122, 123, 124, 125, 126, 127, */
+       },
+       .oobavail = 54,
+       .oobfree = {
+               {0, 5},
+               {16, 7},
+               {32, 7},
+               {48, 7},
+               {64, 7},
+               {80, 7},
+               {96, 7},
+               {112, 7},
+       },
+};
+
+static struct nand_ecclayout nand_hw_eccoob_4k_218_spare = {
+       .eccbytes = 64, /* actually 144 but only room for 64 */
+       .eccpos = {
+               /* 18 bytes of ecc for each 512 bytes of data */
+               7, 8, 9, 10, 11, 12, 13, 14, 15,
+                   16, 17, 18, 19, 20, 21, 22, 23, 24,
+               33, 34, 35, 36, 37, 38, 39, 40, 41,
+                   42, 43, 44, 45, 46, 47, 48, 49, 50,
+               59, 60, 61, 62, 63, 64, 65, 66, 67,
+                   68, 69, 70, 71, 72, 73, 74, 75, 76,
+               85, 86, 87, 88, 89, 90, 91, 92, 93,
+                   94, /* 95, 96, 97, 98, 99, 100, 101, 102,
+               111, 112, 113, 114, 115, 116, 117, 118, 119,
+                   120, 121, 122, 123, 124, 125, 126, 127, 128,
+               137, 138, 139, 140, 141, 142, 143, 144, 145,
+                   146, 147, 148, 149, 150, 151, 152, 153, 154,
+               163, 164, 165, 166, 167, 168, 169, 170, 171,
+                   172, 173, 174, 175, 176, 177, 178, 179, 180,
+               189, 190, 191, 192, 193, 194, 195, 196, 197,
+                   198, 199, 200, 201, 202, 203, 204, 205, 206, */
+       },
+       .oobavail = 4,
+       .oobfree = {
+               {0, 5},
+               {26, 8},
+               {52, 8},
+               {78, 8},
+               {104, 8},
+               {130, 8},
+               {156, 8},
+               {182, 8},
+       },
+};
+
+/*
+ * Functions to transfer data to/from spare erea.
+ */
+static void copy_from_spare(struct mtd_info *mtd, void *pbuf, int len)
+{
+       u16 ooblen = mtd->oobsize;
+       u8 i, count, size;
+
+       count = mtd->writesize >> 9;
+       size = (ooblen / count >> 1) << 1;
+
+       for (i = 0; i < count - 1; i++) {
+               memcpy_fromio(pbuf, SPARE_AREA(i), size);
+               pbuf += size;
+               len -= size;
+       }
+       memcpy_fromio(pbuf, SPARE_AREA(i), len);
+}
+
+static void copy_to_spare(struct mtd_info *mtd, void *pbuf, int len)
+{
+       u16 ooblen = mtd->oobsize;
+       u8 i, count, size;
+
+       count = mtd->writesize >> 9;
+       size = (ooblen / count >> 1) << 1;
+
+       for (i = 0; i < count - 1; i++) {
+               memcpy_toio(SPARE_AREA(i), pbuf, size);
+               pbuf += size;
+               len -= size;
+       }
+       memcpy_toio(SPARE_AREA(i), pbuf, len);
+}
+
+/*!
+ * This function polls the NFC to wait for the basic operation to complete by
+ * checking the INT bit of config2 register.
+ *
+ * @maxRetries number of retry attempts (separated by 1 us)
+ */
+static void wait_op_done(int maxRetries)
+{
+
+       while (1) {
+               maxRetries--;
+               if (in_be16(NFC_CONFIG2) & NFC_INT)
+                       break;
+               udelay(1);
+       }
+       if (maxRetries <= 0)
+               MTDDEBUG(MTD_DEBUG_LEVEL0, "%s: INT not set\n", __FUNCTION__);
+}
+
+/*!
+ * This function issues the specified command to the NAND device and
+ * waits for completion.
+ *
+ * @cmds       command for NAND Flash
+ */
+static void send_cmd(u16 cmd)
+{
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "send_cmd(%#x)\n", cmd);
+
+       out_be16(NFC_FLASH_CMD, cmd);
+       out_be16(NFC_CONFIG2, NFC_CMD);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+}
+
+/*!
+ * 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.
+ *
+ * @addr       address to be written to NFC.
+ */
+static void send_addr(u16 addr)
+{
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "send_addr(%#x)\n", addr);
+       out_be16(NFC_FLASH_ADDR, (addr << NFC_FLASH_ADDR_SHIFT));
+
+       out_be16(NFC_CONFIG2, NFC_ADDR);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+}
+
+/*!
+ * This function requests the NFC to initate the transfer
+ * of data currently in the NFC RAM buffer to the NAND device.
+ *
+ * @buf_id     Specify Internal RAM Buffer number (0-3)
+ */
+static void send_prog_page(u8 buf_id)
+{
+       u32 val = buf_id;
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+       /* Set RBA bits for BUFFER val */
+       out_be16(NFC_BUF_ADDR, val);
+
+       out_be16(NFC_CONFIG2, NFC_INPUT);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+}
+
+/*!
+ * This function requests the NFC to initated the transfer
+ * of data from the NAND device into in the NFC ram buffer.
+ *
+ * @buf_id             Specify Internal RAM Buffer number (0-3)
+ */
+static void send_read_page(u8 buf_id)
+{
+       u32 val = buf_id;
+       MTDDEBUG(MTD_DEBUG_LEVEL3, "%s\n", __FUNCTION__);
+
+       /* Set RBA bits for BUFFER val */
+       out_be16(NFC_BUF_ADDR, val);
+
+       out_be16(NFC_CONFIG2, NFC_OUTPUT);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+}
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device ID.
+ */
+static void send_read_id(void)
+{
+       u32 val = 0;
+
+       /* NFC buffer 0 is used for device ID output */
+       /* Set RBA bits for BUFFER0 */
+
+       out_be16(NFC_BUF_ADDR, val);
+
+       /* Read ID into main buffer */
+       out_be16(NFC_CONFIG2, NFC_ID);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+
+}
+
+/*!
+ * This function requests the NFC to perform a read of the
+ * NAND device status and returns the current status.
+ *
+ * @return     device status
+ */
+static u16 get_dev_status(void)
+{
+       u32 save;
+       u16 ret;
+       /* Issue status request to NAND device */
+
+       /* save the main area1 first word, later do recovery */
+       save = in_be32(MAIN_AREA(1));
+       out_be32(MAIN_AREA(1), 0);
+
+       /*
+        * NFC buffer 1 is used for device status to prevent
+        * corruption of read/write buffer on status requests.
+        */
+
+       /* Select BUFFER1 */
+       out_be16(NFC_BUF_ADDR, 1);
+
+       /* Read status into main buffer */
+       out_be16(NFC_CONFIG2, NFC_STATUS);
+
+       /* Wait for operation to complete */
+       wait_op_done(TROP_US_DELAY);
+
+       /* Status is placed in first word of main buffer */
+       /* get status, then recovery area 1 data */
+       if (in_be16(NFC_CONFIG1) & NFC_BIG)
+               ret = in_8(MAIN_AREA(1));
+       else
+               ret = in_8(MAIN_AREA(1) + 3);
+
+       out_be32(MAIN_AREA(1), save);
+       return ret;
+}
+
+/*!
+ * This functions is used by upper layer to checks if device is ready
+ *
+ * @mtd                MTD structure for the NAND Flash
+ *
+ * @return     0 if device is busy else 1
+ */
+static int mpc5121_nand_dev_ready(struct mtd_info *mtd)
+{
+       return 1;
+}
+
+static void mpc5121_nand_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_ECC_EN));
+       return;
+}
+
+/*
+ * Function to record the ECC corrected/uncorrected errors resulted
+ * after a page read. This NFC detects and corrects upto to 4 symbols
+ * of 9-bits each.
+ */
+static int mpc5121_check_ecc_status(struct mtd_info *mtd)
+{
+       u32 ecc_stat, err;
+       int no_subpages = 1;
+       int ret = 0;
+       u8 ecc_bit_mask, err_limit;
+       int is_4bit_ecc = in_be16(NFC_CONFIG1) & NFC_ECC_MODE_4;
+
+       ecc_bit_mask = (is_4bit_ecc ? 0x7 : 0xf);
+       err_limit = (is_4bit_ecc ? 0x4 : 0x8);
+
+       no_subpages = mtd->writesize >> 9;
+
+       ecc_stat = in_be16(NFC_ECC_STATUS1);
+       do {
+               err = ecc_stat & ecc_bit_mask;
+               if (err > err_limit)
+                       return -1;
+               else
+                       ret += err;
+               ecc_stat >>= 4;
+       } while (--no_subpages);
+
+       return ret;
+}
+
+/*
+ * Function to correct the detected errors. This NFC corrects all the errors
+ * detected. So this function is not required.
+ */
+static int mpc5121_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+                                u_char *read_ecc, u_char *calc_ecc)
+{
+       panic("Shouldn't be called here: %d\n", __LINE__);
+       return 0;               /* FIXME */
+}
+
+/*
+ * Function to calculate the ECC for the data to be stored in the Nand device.
+ * This NFC has a hardware RS(511,503) ECC engine together with the RS ECC
+ * CONTROL blocks are responsible for detection  and correction of up to
+ * 4 symbols of 9 bits each in 528 byte page.
+ * So this function is not required.
+ */
+
+static int mpc5121_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+                                       u_char *ecc_code)
+{
+       panic("Shouldn't be called here %d \n", __LINE__);
+       return 0;               /* FIXME */
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @mtd                MTD structure for the NAND Flash
+ *
+ * @return     data read from the NAND Flash
+ */
+static u_char mpc5121_nand_read_byte(struct mtd_info *mtd)
+{
+       void *area_buf;
+       u_char rv;
+
+       /* Check for status request */
+       if (priv->status_req) {
+               rv = get_dev_status() & 0xff;
+               return rv;
+       }
+
+       if (priv->spare_only)
+               area_buf = SPARE_AREA(0);
+       else
+               area_buf = MAIN_AREA(0);
+
+       rv = in_8(area_buf + priv->col_addr);
+       priv->col_addr++;
+       return rv;
+}
+
+/*!
+  * This function reads word from the NAND Flash
+  *
+  * @mtd       MTD structure for the NAND Flash
+  *
+  * @return    data read from the NAND Flash
+  */
+static u16 mpc5121_nand_read_word(struct mtd_info *mtd)
+{
+       u16 rv;
+       void *area_buf;
+
+       /* If we are accessing the spare region */
+       if (priv->spare_only)
+               area_buf = SPARE_AREA(0);
+       else
+               area_buf = MAIN_AREA(0);
+
+       /* Update saved column address */
+       rv = in_be16(area_buf + priv->col_addr);
+       priv->col_addr += 2;
+
+       return rv;
+}
+
+/*!
+ * This function reads byte from the NAND Flash
+ *
+ * @mtd                MTD structure for the NAND Flash
+ *
+ * @return     data read from the NAND Flash
+ */
+static u_char mpc5121_nand_read_byte16(struct mtd_info *mtd)
+{
+       /* Check for status request */
+       if (priv->status_req)
+               return (get_dev_status() & 0xff);
+
+       return mpc5121_nand_read_word(mtd) & 0xff;
+}
+
+/*!
+ * This function writes data of length \b len from buffer \b buf to the NAND
+ * internal RAM buffer's MAIN area 0.
+ *
+ * @mtd                MTD structure for the NAND Flash
+ * @buf                data to be written to NAND Flash
+ * @len                number of bytes to be written
+ */
+static void mpc5121_nand_write_buf(struct mtd_info *mtd,
+                                       const u_char *buf, int len)
+{
+       printf("re-work may be needed?\n");
+       if (priv->col_addr >= mtd->writesize || priv->spare_only) {
+               copy_to_spare(mtd, (char *)buf, len);
+               return;
+       } else {
+               priv->col_addr += len;
+               memcpy_toio(MAIN_AREA(0), (void *)buf, len);
+       }
+}
+
+/*!
+ * 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.
+ *
+ * @mtd                MTD structure for the NAND Flash
+ * @buf                data to be read from NAND Flash
+ * @len                number of bytes to be read
+ */
+static void mpc5121_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+
+       if (priv->col_addr >= mtd->writesize || priv->spare_only) {
+               copy_from_spare(mtd, buf, len);
+               return;
+       } else {
+               priv->col_addr += len;
+               memcpy_fromio((void *)buf, MAIN_AREA(0), len);
+       }
+}
+
+/*!
+ * This function is used by the upper layer to verify the data in NAND Flash
+ * with the data in the \b buf.
+ *
+ * @mtd                MTD structure for the NAND Flash
+ * @buf                data to be verified
+ * @len                length of the data to be verified
+ *
+ * @return     -1 if error else 0
+ *
+ */
+static int mpc5121_nand_verify_buf(struct mtd_info *mtd, const u_char *buf,
+                                       int len)
+{
+       void *main_buf = MAIN_AREA(0);
+       /* check for 32-bit alignment? */
+       u32 *p = (u32 *) buf;
+       u32 v;
+
+       for (; len > 0; len -= 4, main_buf += 4)
+               v = in_be32(main_buf);
+               if (v != *p++)
+                       return -1;
+       return 0;
+}
+
+static int mpc5121_nand_get_hw_config(struct nand_chip *this)
+{
+       immap_t *im = (immap_t *)CONFIG_SYS_IMMR;
+       u32 rcwh;
+       int rcwh_romloc;
+       int rcwh_ps;
+       int width;
+       int writesize = 0;
+       int sparesize = 0;
+
+       /*
+        * Only support 2K for now.
+        * Remove this when others are tested and debugged.
+        */
+#if 1
+       if (CONFIG_MPC5121_NAND_WRITE_SIZE != 2048) {
+               printf("MPC5121 NAND: "
+                       "%d byte write size flash support is untested\n",
+                       CONFIG_MPC5121_NAND_WRITE_SIZE);
+               return -1;
+       }
+#endif
+       rcwh = in_be32((void *)&(im->reset.rcwh));
+       width = ((rcwh >> 6) & 0x1) ? 2 : 1;
+
+       if (width != CONFIG_MPC5121_NAND_WIDTH) {
+               printf("MPC5121 NAND: Device width mismatch, compiled for %d, "
+                       "reset configuration word width is %d\n",
+                       CONFIG_MPC5121_NAND_WIDTH, width);
+               return -1;
+       }
+
+       if (width == 2) {
+               this->options |= NAND_BUSWIDTH_16;
+               this->read_byte = mpc5121_nand_read_byte16;
+       }
+
+       /*
+        * Decode the rcwh_ps and rcwh_romloc
+        * bits from reset config word
+        * to determine write size
+        */
+       rcwh_ps = (rcwh >> 7) & 0x1;
+       rcwh_romloc = (rcwh >> 21) & 0x3;
+       switch (rcwh_ps << 2 | rcwh_romloc) {
+       case 0x0:
+       case 0x1:
+               writesize = 512;
+               sparesize = 16;
+               break;
+       case 0x2:
+       case 0x3:
+               writesize = 4096;
+               sparesize = 128;
+               break;
+       case 0x4:
+       case 0x5:
+               writesize = 2048;
+               sparesize = 64;
+               break;
+       case 0x6:
+       case 0x7:
+               writesize = 4096;
+               sparesize = 218;
+               break;
+       }
+       if (CONFIG_MPC5121_NAND_WRITE_SIZE != writesize) {
+               printf("MPC5121 NAND: "
+                       "Device write size mismatch, "
+                       "compiled for %d, "
+                       "size from reset configuration word is %d\n",
+                       CONFIG_MPC5121_NAND_WRITE_SIZE, writesize);
+               return -1;
+       }
+       if (CONFIG_MPC5121_NAND_SPARE_SIZE != sparesize) {
+               printf("MPC5121 NAND: "
+                       "Device spare size mismatch, "
+                       "compiled for %d, "
+                       "size from reset configuration word is %d\n",
+                       CONFIG_MPC5121_NAND_SPARE_SIZE, sparesize);
+               return -1;
+       }
+
+       priv->sparesize = sparesize;
+       priv->width = width;
+       return 0;
+}
+
+static void mpc5121_cs_enable(int chip)
+{
+       unsigned char *csreg = (unsigned char *)CONFIG_SYS_CPLD_BASE + 0x09;
+       u8 v;
+
+       v = in_8(csreg);
+       v |= 0xf;
+       v &= ~(1<<chip);
+
+       out_8(csreg, v);
+}
+
+
+/*!
+ * This function is used by upper layer for select and deselect of the NAND
+ * chip
+ *
+ * @mtd                MTD structure for the NAND Flash
+ * @chip       val indicating select or deselect
+ */
+static void mpc5121_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+       /*
+        * This is different than the linux version.
+        * Switching between chips is done via
+        * board_nand_select_device.
+        *
+        * Only valid chip numbers here are
+        *      0 select
+        *      -1 deselect
+        */
+       if (chip < -1 || chip > 0) {
+               printf("MPC5121 NAND: "
+                       "ERROR: Illegal chip select (chip = %d)\n", chip);
+       }
+
+       if (chip < 0) {
+               out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) & ~NFC_CE));
+               return;
+       }
+
+       out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_CE));
+
+       /* Turn on appropriate chip */
+       mpc5121_cs_enable(priv->chipsel);
+}
+
+/*
+ * Function to perform the address cycles.
+ */
+static void mpc5121_nand_do_addr_cycle(struct mtd_info *mtd, int column,
+               int page_addr)
+{
+       struct nand_chip *this = mtd->priv;
+       u32 page_mask = this->pagemask;
+
+       if (column != -1) {
+               send_addr(column & 0xff);
+               /* large page nand needs an extra column addr cycle */
+               if (IS_2K_PAGE_NAND)
+                       send_addr((column >> 8) & 0xf);
+               else if (IS_4K_PAGE_NAND)
+                       send_addr((column >> 8) & 0x1f);
+       }
+       if (page_addr != -1)
+               do {
+                       send_addr((page_addr & 0xff));
+                       page_mask >>= 8;
+                       page_addr >>= 8;
+               } while (page_mask != 0);
+}
+
+/*
+ * Function to read a page from nand device.
+ */
+static void read_full_page(struct mtd_info *mtd, int page_addr)
+{
+       send_cmd(NAND_CMD_READ0);
+
+       mpc5121_nand_do_addr_cycle(mtd, 0, page_addr);
+
+       if (IS_LARGE_PAGE_NAND) {
+               send_cmd(NAND_CMD_READSTART);
+               send_read_page(0);
+       } else
+               send_read_page(0);
+}
+
+/*!
+ * This function is used by the upper layer to write command to NAND Flash for
+ * different operations to be carried out on NAND Flash
+ *
+ * @mtd                MTD structure for the NAND Flash
+ * @command    command for NAND Flash
+ * @column     column offset for the page read
+ * @page_addr  page to be read from NAND Flash
+ */
+static void mpc5121_nand_command(struct mtd_info *mtd, unsigned command,
+                                       int column, int page_addr)
+{
+       MTDDEBUG(MTD_DEBUG_LEVEL3,
+               "mpc5121_nand_command (cmd = %#x, col = %#x, page = %#x)\n",
+               command, column, page_addr);
+       /*
+        * Reset command state information
+        */
+       priv->status_req = 0;
+
+       /* Reset column address to 0 */
+       priv->col_addr = 0;
+
+       /*
+        * Command pre-processing step
+        */
+       switch (command) {
+       case NAND_CMD_STATUS:
+               priv->status_req = 1;
+               break;
+
+       case NAND_CMD_READ0:
+               priv->spare_only = 0;
+               break;
+
+       case NAND_CMD_READOOB:
+               priv->col_addr = column;
+               priv->spare_only = 1;
+               command = NAND_CMD_READ0;       /* only READ0 is valid */
+               break;
+
+       case NAND_CMD_SEQIN:
+               if (column >= mtd->writesize)
+                       priv->spare_only = 1;
+               else
+                       priv->spare_only = 0;
+               break;
+
+       case NAND_CMD_PAGEPROG:
+               if (!priv->spare_only)
+                       send_prog_page(0);
+               else
+                       return;
+               break;
+
+       case NAND_CMD_ERASE1:
+               break;
+       case NAND_CMD_ERASE2:
+               break;
+       }
+
+       /*
+        * Write out the command to the device.
+        */
+       send_cmd(command);
+
+       mpc5121_nand_do_addr_cycle(mtd, column, page_addr);
+
+       /*
+        * Command post-processing step
+        */
+       switch (command) {
+
+       case NAND_CMD_READOOB:
+       case NAND_CMD_READ0:
+               if (IS_LARGE_PAGE_NAND) {
+                       /* send read confirm command */
+                       send_cmd(NAND_CMD_READSTART);
+                       /* read for each AREA */
+                       send_read_page(0);
+               } else
+                       send_read_page(0);
+               break;
+
+       case NAND_CMD_READID:
+               send_read_id();
+               break;
+       }
+}
+
+static int mpc5121_nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
+{
+       return (int)(get_dev_status());
+}
+
+static int mpc5121_nand_read_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                               int page, int sndcmd)
+{
+       if (sndcmd) {
+               read_full_page(mtd, page);
+               sndcmd = 0;
+       }
+
+       copy_from_spare(mtd, chip->oob_poi, mtd->oobsize);
+       return sndcmd;
+}
+
+static int mpc5121_nand_write_oob(struct mtd_info *mtd, struct nand_chip *chip,
+                                       int page)
+{
+       int status = 0;
+       int read_oob_col = 0;
+
+       send_cmd(NAND_CMD_READ0);
+       send_cmd(NAND_CMD_SEQIN);
+       mpc5121_nand_do_addr_cycle(mtd, read_oob_col, page);
+
+       /* copy the oob data */
+       copy_to_spare(mtd, chip->oob_poi, mtd->oobsize);
+
+       send_prog_page(0);
+
+       send_cmd(NAND_CMD_PAGEPROG);
+
+       status = mpc5121_nand_wait(mtd, chip);
+       if (status & NAND_STATUS_FAIL)
+               return -1;
+       return 0;
+}
+
+static int mpc5121_nand_read_page(struct mtd_info *mtd, struct nand_chip *chip,
+                                       uint8_t *buf)
+{
+       int stat;
+
+       stat = mpc5121_check_ecc_status(mtd);
+       if (stat == -1) {
+               mtd->ecc_stats.failed++;
+               printf("MPC5121 NAND: UnCorrectable RS-ECC Error\n");
+       } else {
+               mtd->ecc_stats.corrected += stat;
+               if (stat)
+                       printf("%d Symbol Correctable RS-ECC Error\n", stat);
+       }
+
+       memcpy_fromio((void *)buf, MAIN_AREA(0), mtd->writesize);
+       copy_from_spare(mtd, chip->oob_poi, mtd->oobsize);
+       return 0;
+}
+
+static void mpc5121_nand_write_page(struct mtd_info *mtd,
+               struct nand_chip *chip, const uint8_t *buf)
+{
+       memcpy_toio(MAIN_AREA(0), buf, mtd->writesize);
+       copy_to_spare(mtd, chip->oob_poi, mtd->oobsize);
+}
+
+/* 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 = 5,
+       .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
+};
+
+static int mpc5121_nand_scan_bbt(struct mtd_info *mtd)
+{
+       struct nand_chip *this = mtd->priv;
+
+       if (IS_2K_PAGE_NAND)
+               this->ecc.layout = &nand_hw_eccoob_2k;
+       else if (IS_4K_PAGE_NAND)
+               if (priv->sparesize == 128)
+                       this->ecc.layout = &nand_hw_eccoob_4k;
+               else
+                       this->ecc.layout = &nand_hw_eccoob_4k_218_spare;
+       else
+               this->ecc.layout = &nand_hw_eccoob_512;
+
+       /* propagate ecc.layout to mtd_info */
+       mtd->ecclayout = this->ecc.layout;
+
+#if 0
+       /* jffs2 should not write oob */
+       mtd->flags &= ~MTD_OOB_WRITEABLE;
+#endif
+
+       /* 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)
+               this->badblock_pattern = (mtd->writesize > 512) ?
+                   &largepage_memorybased : &smallpage_memorybased;
+
+       /* Build bad block table */
+       return nand_scan_bbt(mtd, this->badblock_pattern);
+}
+
+void board_nand_select_device(struct nand_chip *nand, int chip)
+{
+       if (chip >= CONFIG_MPC5121_NAND_CHIPS) {
+               printf("MPC5121 NAND: "
+                       "ERROR: Illegal chip select (chip = %d)\n", chip);
+               return;
+       }
+       priv->chipsel = chip;
+}
+
+
+int board_nand_init(struct nand_chip *nand)
+{
+       struct mtd_info *mtd;
+
+       priv = malloc(sizeof(*priv));
+       if (!priv) {
+               printf("MPC5121 NAND: failed to allocate priv structure\n");
+               return -1;
+       }
+       memset(priv, 0, sizeof(*priv));
+
+       if (mpc5121_nand_get_hw_config(nand) < 0)
+               return -1;
+
+       mtd = &priv->mtd;
+       mtd->priv = nand;
+
+       /* 5 us command delay time */
+       nand->chip_delay = 5;
+
+       nand->dev_ready = mpc5121_nand_dev_ready;
+       nand->cmdfunc = mpc5121_nand_command;
+       nand->waitfunc = mpc5121_nand_wait;
+       nand->select_chip = mpc5121_nand_select_chip;
+       if (priv->width == 2) {
+               nand->options |= NAND_BUSWIDTH_16;
+               nand->read_byte = mpc5121_nand_read_byte16;
+       }
+       nand->read_byte = mpc5121_nand_read_byte;
+       nand->read_word = mpc5121_nand_read_word;
+       nand->write_buf = mpc5121_nand_write_buf;
+       nand->read_buf = mpc5121_nand_read_buf;
+       nand->verify_buf = mpc5121_nand_verify_buf;
+       nand->scan_bbt = mpc5121_nand_scan_bbt;
+
+       out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_RST));
+
+       /* Disable interrupt */
+       out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_INT_MSK));
+
+       if (hardware_ecc) {
+               nand->ecc.read_page = mpc5121_nand_read_page;
+               nand->ecc.write_page = mpc5121_nand_write_page;
+               nand->ecc.read_oob = mpc5121_nand_read_oob;
+               nand->ecc.write_oob = mpc5121_nand_write_oob;
+               nand->ecc.layout = &nand_hw_eccoob_512;
+               nand->ecc.calculate = mpc5121_nand_calculate_ecc;
+               nand->ecc.hwctl = mpc5121_nand_enable_hwecc;
+               nand->ecc.correct = mpc5121_nand_correct_data;
+               nand->ecc.mode = NAND_ECC_HW;
+               /* RS-ECC is applied for both MAIN+SPARE not MAIN alone */
+               nand->ecc.size = 512;
+               nand->ecc.bytes = 9;
+               out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_ECC_EN));
+       } else {
+               nand->ecc.mode = NAND_ECC_SOFT;
+               out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) & ~NFC_ECC_EN));
+       }
+
+       out_be16(NFC_CONFIG1, in_be16(NFC_CONFIG1) & ~NFC_SP_EN);
+
+
+       /* Reset NAND */
+       nand->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+       /* preset operation */
+       /* Unlock the internal RAM Buffer */
+       out_be16(NFC_CONFIG, NFC_BLS_UNLOCKED);
+
+       /* Blocks to be unlocked */
+       out_be16(NFC_UNLOCKSTART_BLKADDR0, 0x0);
+       out_be16(NFC_UNLOCKEND_BLKADDR0, 0xffff);
+
+       /* Unlock Block Command for given address range */
+       out_be16(NFC_WRPROT, NFC_WPC_UNLOCK);
+
+       /* Set sparesize */
+       out_be16(NFC_SPAS, (in_be16(NFC_SPAS) & 0xff00) | (priv->sparesize/2));
+
+       /*
+        * Only use 8bit ecc (aka not 4 bit) if large spare size
+        */
+       if (priv->sparesize == 218)
+               out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) & ~NFC_ECC_MODE_4));
+       else
+               out_be16(NFC_CONFIG1, (in_be16(NFC_CONFIG1) | NFC_ECC_MODE_4));
+
+       return 0;
+}
diff --git a/include/configs/ads5121.h b/include/configs/ads5121.h
index 8ec5e9d..ea79cd1 100644
--- a/include/configs/ads5121.h
+++ b/include/configs/ads5121.h
@@ -33,6 +33,7 @@
  *
  * 0x0000_0000 - 0x0FFF_FFFF   DDR RAM (256 MB)
  * 0x3000_0000 - 0x3001_FFFF   SRAM (128 KB)
+ * 0x4000_0000 - 0x400F_FFFF   NFC (1 MB)
  * 0x8000_0000 - 0x803F_FFFF   IMMR (4 MB)
  * 0x8200_0000 - 0x8200_001F   CPLD (32 B)
  * 0x8400_0000 - 0x82FF_FFFF   PCI I/O space (16 MB)
@@ -199,6 +200,32 @@
 #undef CONFIG_SYS_FLASH_CHECKSUM
 
 /*
+ * NAND FLASH
+ * drivers/mtd/nand/mpc5121_mpc.c (rev 2 silicon/rev 4 boards only)
+ */
+#ifdef CONFIG_NAND_SPL
+#define CONFIG_SYS_NAND_BASE           0xFFF00000
+#else
+#define CONFIG_SYS_NAND_BASE           0x40000000
+#endif
+#define CONFIG_CMD_NAND 1
+/*
+ * The flash on ADS5121 board is two flash chips in one package
+ */
+#define CONFIG_SYS_MAX_NAND_DEVICE     2
+#define NAND_MAX_CHIPS         CONFIG_SYS_MAX_NAND_DEVICE
+#define CONFIG_SYS_NAND_SELECT_DEVICE  1
+#define CONFIG_NAND_MPC5121
+/*
+ * Configuration parameters for MPC5121 NAND driver
+ */
+#define CONFIG_MPC5121_NAND_WIDTH 1
+#define CONFIG_MPC5121_NAND_WRITE_SIZE 2048
+#define CONFIG_MPC5121_NAND_SPARE_SIZE 64
+#define CONFIG_MPC5121_NAND_CHIPS 2
+
+
+/*
  * CPLD registers area is really only 32 bytes in size, but the smallest 
possible LP
  * window is 64KB
  */
-- 
1.5.6.2.255.gbed62

_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
http://lists.denx.de/mailman/listinfo/u-boot

Reply via email to