[U-Boot-Users] [PATCH 2/2] NAND: Add mpc831x support.
Scott Wood
scottwood at freescale.com
Wed May 16 18:28:05 CEST 2007
Signed-off-by: Nick Spence <nick.spence at freescale.com>
Signed-off-by: Scott Wood <scottwood at freescale.com>
---
cpu/mpc83xx/Makefile | 2 +-
cpu/mpc83xx/nand-831x.c | 764 +++++++++++++++++++++++++++++++++++++++++
include/configs/MPC8313ERDB.h | 4 +-
3 files changed, 768 insertions(+), 2 deletions(-)
create mode 100644 cpu/mpc83xx/nand-831x.c
diff --git a/cpu/mpc83xx/Makefile b/cpu/mpc83xx/Makefile
index bb96f77..e698a02 100644
--- a/cpu/mpc83xx/Makefile
+++ b/cpu/mpc83xx/Makefile
@@ -29,7 +29,7 @@ LIB = $(obj)lib$(CPU).a
START = start.o
COBJS = traps.o cpu.o cpu_init.o speed.o interrupts.o \
- spd_sdram.o qe_io.o pci.o
+ spd_sdram.o qe_io.o pci.o nand-831x.o
SRCS := $(START:.o=.S) $(SOBJS:.o=.S) $(COBJS:.o=.c)
OBJS := $(addprefix $(obj),$(SOBJS) $(COBJS))
diff --git a/cpu/mpc83xx/nand-831x.c b/cpu/mpc83xx/nand-831x.c
new file mode 100644
index 0000000..4319128
--- /dev/null
+++ b/cpu/mpc83xx/nand-831x.c
@@ -0,0 +1,764 @@
+/*
+ * Copyright (C) Freescale Semiconductor, Inc. 2006.
+ *
+ * Authors: Nick.Spence at freescale.com
+ * Wilson.Lo at freescale.com
+ * scottwood at freescale.com
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+
+#ifdef CFG_NAND_MPC831X
+#if defined(CFG_NAND_LEGACY)
+#error "U-Boot legacy NAND commands not supported."
+#endif
+
+#include <malloc.h>
+#include <asm/errno.h>
+#include <nand.h>
+
+#undef CFG_FCM_DEBUG
+#define CFG_FCM_DEBUG_LVL 5
+#ifdef CFG_FCM_DEBUG
+#define FCM_DEBUG(n, args...) \
+ do { \
+ if (n <= CFG_FCM_DEBUG_LVL) \
+ printf(args); \
+ } while (0)
+#else /* CONFIG_FCM_DEBUG */
+#define FCM_DEBUG(n, args...) do { } while (0)
+#endif
+
+#define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */
+#define FCM_TIMEOUT_USECS 100000 /* Maximum number of uSecs to wait for FCM */
+
+/* Private structure holding NAND Flash device specific information */
+struct fcm_nand {
+ int bank; /* Chip select bank number */
+ uint base; /* Chip select base address */
+ int pgs; /* NAND page size */
+ uint page; /* Last page written to / read from */
+ uint fmr; /* FCM Flash Mode Register value */
+ uint mdr; /* UPM/FCM Data Register value */
+ uint use_mdr; /* Non zero if the MDR is to be set */
+ u8 *addr; /* Address of assigned FCM buffer */
+ uint read_bytes; /* Number of bytes read during command */
+ uint index; /* Pointer to next byte to 'read' */
+ uint req_bytes; /* Number of bytes read if command ok */
+ uint req_index; /* New read index if command ok */
+ uint status; /* status read from LTESR after last op*/
+ int use_ecc; /* If non-zero, use ECC on next xfer */
+};
+
+
+/* These map to the positions used by the FCM hardware ECC generator */
+
+/* Small Page FLASH with FMR[ECCM] = 0 */
+static struct nand_oobinfo fcm_oob_sp_eccm0 = {
+ .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */
+ .eccbytes = 3,
+ .eccpos = {6, 7, 8},
+ .oobfree = { {0, 5}, {9, 7} }
+};
+
+/* Small Page FLASH with FMR[ECCM] = 1 */
+static struct nand_oobinfo fcm_oob_sp_eccm1 = {
+ .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */
+ .eccbytes = 3,
+ .eccpos = {8, 9, 10},
+ .oobfree = { {0, 5}, {6, 2}, {11, 5} }
+};
+
+/* Large Page FLASH with FMR[ECCM] = 0 */
+static struct nand_oobinfo fcm_oob_lp_eccm0 = {
+ .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */
+ .eccbytes = 12,
+ .eccpos = {6, 7, 8, 22, 23, 24, 38, 39, 40, 54, 55, 56},
+ .oobfree = { {1, 5}, {9, 13}, {25, 13}, {41, 13}, {57, 7} }
+};
+
+/* Large Page FLASH with FMR[ECCM] = 1 */
+static struct nand_oobinfo fcm_oob_lp_eccm1 = {
+ .useecc = MTD_NANDECC_AUTOPL_USR, /* MTD_NANDECC_PLACEONLY, */
+ .eccbytes = 12,
+ .eccpos = {8, 9, 10, 24, 25, 26, 40, 41, 42, 56, 57, 58},
+ .oobfree = { {1, 7}, {11, 13}, {27, 13}, {43, 13}, {59, 5} }
+};
+
+/*
+ * execute FCM command and wait for it to complete
+ */
+static int fcm_run_command(struct mtd_info *mtd)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+ long long end_tick;
+
+ /* Setup the FMR[OP] to execute without write protection */
+ lbc->fmr = fcm->fmr | 3;
+ if (fcm->use_mdr)
+ lbc->mdr = fcm->mdr;
+
+ FCM_DEBUG(5, "fcm_run_command: fmr= %08X fir= %08X fcr= %08X\n",
+ lbc->fmr, lbc->fir, lbc->fcr);
+ FCM_DEBUG(5, "fcm_run_command: fbar=%08X fpar=%08X fbcr=%08X bank=%d\n",
+ lbc->fbar, lbc->fpar, lbc->fbcr, fcm->bank);
+
+ /* clear event registers */
+ lbc->lteatr = 0;
+ lbc->ltesr |= LTESR_FCT | LTESR_PAR | LTESR_CC;
+
+ /* execute special operation */
+ lbc->lsor = fcm->bank;
+
+ /* wait for FCM complete flag or timeout */
+ fcm->status = 0;
+ end_tick = usec2ticks(FCM_TIMEOUT_USECS) + get_ticks();
+
+ while (end_tick > get_ticks()) {
+ if (lbc->ltesr & LTESR_CC) {
+ fcm->status = lbc->ltesr &
+ (LTESR_FCT | LTESR_PAR | LTESR_CC);
+ break;
+ }
+ }
+
+ /* store mdr value in case it was needed */
+ if (fcm->use_mdr)
+ fcm->mdr = lbc->mdr;
+
+ fcm->use_mdr = 0;
+
+ FCM_DEBUG(5, "fcm_run_command: stat=%08X mdr= %08X fmr= %08X\n",
+ fcm->status, fcm->mdr, lbc->fmr);
+
+ /* if the operation completed ok then set the read buffer pointers */
+ if (fcm->status == LTESR_CC) {
+ fcm->read_bytes = fcm->req_bytes;
+ fcm->index = fcm->req_index;
+ return 0;
+ }
+
+ printf("831x NAND flash: ");
+
+ if (fcm->status & LTESR_FCT) {
+ printf("Timeout");
+
+ if (fcm->status & LTESR_PAR)
+ printf(", ");
+ }
+
+ if (fcm->status & LTESR_PAR)
+ printf("ECC error");
+
+ printf("\n");
+ return -1;
+}
+
+/*
+ * Set up the FCM hardware block and page address fields, and the fcm
+ * structure addr field to point to the correct FCM buffer in memory
+ */
+static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+ int buf_num;
+
+ fcm->page = page_addr;
+
+ lbc->fbar = page_addr >> (this->phys_erase_shift - this->page_shift);
+ if (fcm->pgs) {
+ lbc->fpar = ((page_addr << FPAR_LP_PI_SHIFT) & FPAR_LP_PI) |
+ (oob ? FPAR_LP_MS : 0) | column;
+ buf_num = (page_addr & 1) << 2;
+ } else {
+ lbc->fpar = ((page_addr << FPAR_SP_PI_SHIFT) & FPAR_SP_PI) |
+ (oob ? FPAR_SP_MS : 0) | column;
+ buf_num = page_addr & 7;
+ }
+
+ fcm->addr = (u8 *)(fcm->base + buf_num * 1024);
+
+ /* for OOB data point to the second half of the buffer */
+ if (oob)
+ fcm->addr += fcm->pgs ? 2048 : 512;
+}
+
+/* not required for FCM */
+static void fcm_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+{
+}
+
+/*
+ * FCM does not support 16 bit data busses
+ */
+static u16 fcm_read_word(struct mtd_info *mtd)
+{
+ printf("fcm_read_word: UNIMPLEMENTED.\n");
+ return 0;
+}
+
+static void fcm_write_word(struct mtd_info *mtd, u16 word)
+{
+ printf("fcm_write_word: UNIMPLEMENTED.\n");
+}
+
+/*
+ * Write buf to the FCM Controller Data Buffer
+ */
+static void fcm_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ FCM_DEBUG(3, "fcm_write_buf: writing %d bytes starting with 0x%x"
+ " at %d.\n", len, *((u32 *)buf), fcm->index);
+
+ /* copy the data into the FCM hardware buffer and update the index */
+ memcpy(&fcm->addr[fcm->index], buf, len);
+ fcm->index += len;
+}
+
+
+/*
+ * FCM does not support individual writes. Instead these are either commands
+ * or data being written, both of which are handled through the cmdfunc
+ * handler.
+ */
+static void fcm_write_byte(struct mtd_info *mtd, u8 byte)
+{
+ printf("fcm_write_byte: UNIMPLEMENTED.\n");
+}
+
+/*
+ * read a byte from either the FCM hardware buffer if it has any data left
+ * otherwise issue a command to read a single byte.
+ */
+static u8 fcm_read_byte(struct mtd_info *mtd)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+ u8 byte;
+
+ /* If there are still bytes in the FCM then use the next byte */
+ if (fcm->index < fcm->read_bytes) {
+ byte = fcm->addr[fcm->index++];
+ FCM_DEBUG(4, "fcm_read_byte: byte %u (%02X): %d of %d.\n",
+ byte, byte, fcm->index - 1, fcm->read_bytes);
+ return byte;
+ }
+
+ return ERR_BYTE;
+}
+
+/*
+ * Read from the FCM Controller Data Buffer
+ */
+static void fcm_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ FCM_DEBUG(3, "fcm_read_buf: reading %d bytes.\n", len);
+
+ /* If last read failed then return error bytes */
+ if (fcm->status != LTESR_CC) {
+ /* just keep copying bytes so that the oob works */
+ memcpy(buf, &fcm->addr[fcm->index], len);
+ fcm->index += len;
+ return;
+ }
+
+ len = min(len, fcm->read_bytes - fcm->index);
+
+ memcpy(buf, &fcm->addr[fcm->index], len);
+ fcm->index += len;
+}
+
+/*
+ * Verify buffer against the FCM Controller Data Buffer
+ */
+static int fcm_verify_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ FCM_DEBUG(3, "fcm_verify_buf: checking %d bytes starting with 0x%02x.\n",
+ len, *((u32 *)buf));
+
+ /* If last read failed then return error bytes */
+ if (fcm->status != LTESR_CC)
+ return -EFAULT;
+
+ /* see how much is still in the FCM buffer */
+ if ((uint)len > fcm->read_bytes - fcm->index)
+ return -EFAULT;
+
+ if (memcmp(buf, &fcm->addr[fcm->index], len)) {
+ fcm->index += len;
+ return -EFAULT;
+ }
+
+ fcm->index += len;
+ return 0;
+}
+
+/* This function is called after Program and Erase Operations to
+ * check for success or failure
+ */
+static int fcm_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct fcm_nand *fcm = this->priv;
+
+ if (fcm->status != LTESR_CC)
+ return 1; /* Status Read error */
+
+ /* Use READ_STATUS command, but wait for the device to be ready */
+ fcm->use_mdr = 0;
+ fcm->req_index = 0;
+ fcm->read_bytes = 0;
+ fcm->index = 0;
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_RBW << FIR_OP1_SHIFT);
+ lbc->fcr = NAND_CMD_STATUS << FCR_CMD0_SHIFT;
+ set_addr(mtd, 0, 0, 0);
+ lbc->fbcr = 1;
+ fcm->req_bytes = 1;
+ fcm_run_command(mtd);
+
+ if (fcm->status != LTESR_CC)
+ return 1; /* Status Read error */
+
+ return fcm_read_byte(mtd);
+}
+
+/* cmdfunc send commands to the FCM */
+static void fcm_cmdfunc(struct mtd_info *mtd, unsigned command,
+ int column, int page_addr)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ fcm->use_mdr = 0;
+ fcm->req_index = 0;
+
+ /* clear the read buffer */
+ fcm->read_bytes = 0;
+ if (command != NAND_CMD_PAGEPROG)
+ fcm->index = 0;
+
+ switch (command) {
+ /* READ0 reads the entire buffer to use hardware ECC. */
+ case NAND_CMD_READ0:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_READ0, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+ fcm->req_index = column;
+
+ if (fcm->pgs) {
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT);
+ } else {
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT);
+ }
+ lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT);
+
+ fcm->req_bytes = mtd->oobblock + mtd->oobsize;
+
+ if (fcm->use_ecc)
+ lbc->fbcr = 0; /* read entire page to enable ECC */
+ else
+ lbc->fbcr = fcm->req_bytes;
+
+ set_addr(mtd, 0, page_addr, 0);
+ goto write_cmd2;
+
+ /* READOOB read only the OOB becasue no ECC is performed */
+ case NAND_CMD_READOOB:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_READOOB, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+ if (fcm->pgs) {
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP3_SHIFT) |
+ (FIR_OP_RBW << FIR_OP4_SHIFT);
+ lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_READSTART << FCR_CMD1_SHIFT);
+ } else {
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_RBW << FIR_OP3_SHIFT);
+ lbc->fcr = NAND_CMD_READOOB << FCR_CMD0_SHIFT;
+ }
+ lbc->fbcr = mtd->oobsize - column;
+ set_addr(mtd, column, page_addr, 1);
+ goto write_cmd1;
+
+ /* READID must read all 5 possible bytes while CEB is active */
+ case NAND_CMD_READID:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_READID.\n");
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_UA << FIR_OP1_SHIFT) |
+ (FIR_OP_RBW << FIR_OP2_SHIFT);
+ lbc->fcr = NAND_CMD_READID << FCR_CMD0_SHIFT;
+ lbc->fbcr = 5; /* 5 bytes for manuf, device and exts */
+ fcm->use_mdr = 1;
+ fcm->mdr = 0;
+ goto write_cmd0;
+
+ /* ERASE1 stores the block and page address */
+ case NAND_CMD_ERASE1:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_ERASE1, page_addr:"
+ " 0x%x.\n", page_addr);
+ set_addr(mtd, 0, page_addr, 0);
+ return;
+
+ /* ERASE2 uses the block and page address from ERASE1 */
+ case NAND_CMD_ERASE2:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_ERASE2.\n");
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_PA << FIR_OP1_SHIFT) |
+ (FIR_OP_CM1 << FIR_OP2_SHIFT);
+ lbc->fcr = (NAND_CMD_ERASE1 << FCR_CMD0_SHIFT) |
+ (NAND_CMD_ERASE2 << FCR_CMD1_SHIFT);
+ lbc->fbcr = 0;
+ goto write_cmd1;
+
+ /* SEQIN sets up the addr buffer and all registers except the length */
+ case NAND_CMD_SEQIN:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, page_addr:"
+ " 0x%x, column: 0x%x.\n", page_addr, column);
+ if (column == 0) {
+ lbc->fbcr = 0; /* write entire page to enable ECC */
+ } else {
+ lbc->fbcr = 1; /* mark as partial page so no HW ECC */
+ }
+ if (fcm->pgs) {
+ /* always use READ0 for large page devices */
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CA << FIR_OP1_SHIFT) |
+ (FIR_OP_PA << FIR_OP2_SHIFT) |
+ (FIR_OP_WB << FIR_OP3_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP4_SHIFT);
+ lbc->fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) |
+ (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT);
+ set_addr(mtd, column, page_addr, 0);
+ } else {
+ lbc->fir = (FIR_OP_CW0 << FIR_OP0_SHIFT) |
+ (FIR_OP_CM2 << FIR_OP1_SHIFT) |
+ (FIR_OP_CA << FIR_OP2_SHIFT) |
+ (FIR_OP_PA << FIR_OP3_SHIFT) |
+ (FIR_OP_WB << FIR_OP4_SHIFT) |
+ (FIR_OP_CW1 << FIR_OP5_SHIFT);
+ if (column >= mtd->oobblock) {
+ /* OOB area --> READOOB */
+ column -= mtd->oobblock;
+ lbc->fcr = (NAND_CMD_READOOB << FCR_CMD0_SHIFT)
+ | (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT)
+ | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
+ set_addr(mtd, column, page_addr, 1);
+ } else if (column < 256) {
+ /* First 256 bytes --> READ0 */
+ lbc->fcr = (NAND_CMD_READ0 << FCR_CMD0_SHIFT)
+ | (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT)
+ | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
+ set_addr(mtd, column, page_addr, 0);
+ } else {
+ /* Second 256 bytes --> READ1 */
+ column -= 256;
+ lbc->fcr = (NAND_CMD_READ1 << FCR_CMD0_SHIFT)
+ | (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT)
+ | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT);
+ set_addr(mtd, column, page_addr, 0);
+ }
+ }
+ return;
+
+ /* PAGEPROG reuses all of the setup from SEQIN and adds the length */
+ case NAND_CMD_PAGEPROG:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_PAGEPROG"
+ " writing %d bytes.\n", fcm->index);
+ /* if the write did not start at 0 or is not a full page */
+ /* then set the exact length, otherwise use a full page */
+ /* write so the HW generates the ECC. */
+ if (lbc->fbcr || fcm->index != mtd->oobblock + mtd->oobsize ||
+ !fcm->use_ecc)
+ lbc->fbcr = fcm->index;
+ fcm->req_bytes = 0;
+ goto write_cmd2;
+
+ /* CMD_STATUS must read the status byte while CEB is active */
+ /* Note - it does not wait for the ready line */
+ case NAND_CMD_STATUS:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_STATUS.\n");
+ lbc->fir = (FIR_OP_CM0 << FIR_OP0_SHIFT) |
+ (FIR_OP_RBW << FIR_OP1_SHIFT);
+ lbc->fcr = NAND_CMD_STATUS << FCR_CMD0_SHIFT;
+ lbc->fbcr = 1;
+ goto write_cmd0;
+
+ /* RESET without waiting for the ready line */
+ case NAND_CMD_RESET:
+ FCM_DEBUG(2, "fcm_cmdfunc: NAND_CMD_RESET.\n");
+ lbc->fir = FIR_OP_CM0 << FIR_OP0_SHIFT;
+ lbc->fcr = NAND_CMD_RESET << FCR_CMD0_SHIFT;
+ lbc->fbcr = 0;
+ goto write_cmd0;
+
+ default:
+ printk("fcm_cmdfunc: error, unsupported command.\n");
+ return;
+ }
+
+ /* Short cuts fall through to save code */
+ write_cmd0:
+ set_addr(mtd, 0, 0, 0);
+ write_cmd1:
+ fcm->req_bytes = lbc->fbcr;
+ write_cmd2:
+ fcm_run_command(mtd);
+}
+
+static void fcm_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ fcm->use_ecc = 1;
+}
+
+/*
+ * fcm_correct_data - Detect and correct bit error(s)
+ * The detection and correction is done automatically by the hardware,
+ * if the complete page was read. If the status code is okay then there
+ * was no error, otherwise we return an error code indicating an uncorrectable
+ * error.
+ */
+static int fcm_correct_data(struct mtd_info *mtd, u8 *dat, u8 *read_ecc, u8 *calc_ecc)
+{
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+
+ /* No errors */
+ if (fcm->status == LTESR_CC)
+ return 0;
+
+ return -1; /* uncorrectable error */
+}
+
+/*
+ * Dummy scan_bbt to complete setup of the FMR based on NAND size
+ */
+static int fcm_scan_bbt (struct mtd_info *mtd)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct nand_chip *this = mtd->priv;
+ struct fcm_nand *fcm = this->priv;
+ uint al;
+
+ if (!fcm) {
+ printk (KERN_ERR "fcm_scan_bbt():" \
+ " Failed to allocate chip specific data structure\n");
+ return -1;
+ }
+
+ /* calculate FMR Address Length field */
+ al = 0;
+ if (this->pagemask & 0xffff0000)
+ al++;
+ if (this->pagemask & 0xff000000)
+ al++;
+
+ /* add to ECCM mode set in fcm_init */
+ fcm->fmr |= 12 << FMR_CWTO_SHIFT | /* Timeout > 12 mSecs */
+ al << FMR_AL_SHIFT;
+
+ FCM_DEBUG(1, "fcm_init: nand->options = %08X\n", this->options);
+ FCM_DEBUG(1, "fcm_init: nand->numchips = %10d\n", this->numchips);
+ FCM_DEBUG(1, "fcm_init: nand->chipsize = %10d\n", this->chipsize);
+ FCM_DEBUG(1, "fcm_init: nand->pagemask = %10X\n", this->pagemask);
+ FCM_DEBUG(1, "fcm_init: nand->eccmode = %10d\n", this->eccmode);
+ FCM_DEBUG(1, "fcm_init: nand->eccsize = %10d\n", this->eccsize);
+ FCM_DEBUG(1, "fcm_init: nand->eccbytes = %10d\n", this->eccbytes);
+ FCM_DEBUG(1, "fcm_init: nand->eccsteps = %10d\n", this->eccsteps);
+ FCM_DEBUG(1, "fcm_init: nand->chip_delay = %8d\n", this->chip_delay);
+ FCM_DEBUG(1, "fcm_init: nand->badblockpos = %7d\n", this->badblockpos);
+ FCM_DEBUG(1, "fcm_init: nand->chip_shift = %8d\n", this->chip_shift);
+ FCM_DEBUG(1, "fcm_init: nand->page_shift = %8d\n", this->page_shift);
+ FCM_DEBUG(1, "fcm_init: nand->phys_erase_shift = %2d\n",
+ this->phys_erase_shift);
+ FCM_DEBUG(1, "fcm_init: mtd->flags = %08X\n", mtd->flags);
+ FCM_DEBUG(1, "fcm_init: mtd->size = %10d\n", mtd->size);
+ FCM_DEBUG(1, "fcm_init: mtd->erasesize = %10d\n", mtd->erasesize);
+ FCM_DEBUG(1, "fcm_init: mtd->oobblock = %10d\n", mtd->oobblock);
+ FCM_DEBUG(1, "fcm_init: mtd->oobsize = %10d\n", mtd->oobsize);
+ FCM_DEBUG(1, "fcm_init: mtd->oobavail = %10d\n", mtd->oobavail);
+ FCM_DEBUG(1, "fcm_init: mtd->ecctype = %10d\n", mtd->ecctype);
+ FCM_DEBUG(1, "fcm_init: mtd->eccsize = %10d\n", mtd->eccsize);
+
+ /* adjust Option Register and ECC to match Flash page size */
+ if (mtd->oobblock == 512)
+ lbc->bank[fcm->bank].or &= ~(OR_FCM_PGS);
+ else if (mtd->oobblock == 2048) {
+ lbc->bank[fcm->bank].or |= OR_FCM_PGS;
+ /* adjust ecc setup if needed */
+ if ((lbc->bank[fcm->bank].br & BR_DECC) == BR_DECC_CHK_GEN) {
+ mtd->eccsize = 2048;
+ mtd->oobavail -= 9;
+ this->eccsize = 2048;
+ this->eccbytes += 9;
+ this->eccsteps = 1;
+ this->autooob = (fcm->fmr & FMR_ECCM) ?
+ &fcm_oob_lp_eccm1 : &fcm_oob_lp_eccm0;
+ memcpy(&mtd->oobinfo, this->autooob,
+ sizeof(mtd->oobinfo));
+ }
+ } else {
+ printf("fcm_init: page size %d is not supported\n",
+ mtd->oobblock);
+ return -1;
+ }
+
+ fcm->pgs = (lbc->bank[fcm->bank].or >> OR_FCM_PGS_SHIFT) & 1;
+
+ /* restore default scan_bbt function and call it */
+ this->scan_bbt = nand_default_bbt;
+ return nand_default_bbt(mtd);
+}
+
+/*
+ * Board-specific NAND initialization. The following members of the
+ * argument are board-specific (per include/linux/mtd/nand_new.h):
+ * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
+ * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
+ * - hwcontrol: hardwarespecific function for accesing control-lines
+ * - dev_ready: hardwarespecific function for accesing device ready/busy line
+ * - enable_hwecc: function to enable (reset) hardware ecc generator. Must
+ * only be provided if a hardware ECC is available
+ * - eccmode: mode of ecc, see defines
+ * - chip_delay: chip dependent delay for transfering data from array to
+ * read regs (tR)
+ * - options: various chip options. They can partly be set to inform
+ * nand_scan about special functionality. See the defines for further
+ * explanation
+ * Members with a "?" were not set in the merged testing-NAND branch,
+ * so they are not set here either.
+ */
+int board_nand_init(struct nand_chip *nand)
+{
+ volatile immap_t *im = (immap_t *)CFG_IMMR;
+ volatile lbus83xx_t *lbc = &im->lbus;
+ struct fcm_nand *fcm;
+ uint bank;
+
+ /* Enable FCM detection of timeouts, ECC errors and completion */
+ lbc->ltedr &= ~(LTESR_FCT | LTESR_PAR | LTESR_CC);
+
+ fcm = kmalloc (sizeof(struct fcm_nand), GFP_KERNEL);
+ if (!fcm) {
+ printk (KERN_ERR "board_nand_init():" \
+ " Cannot allocate read buffer data structure\n");
+ return -1;
+ }
+
+ /* Find which chip select bank is being used for this device */
+ for (bank = 0; bank < 8; bank++) {
+ /* Not valid */
+ if (!(lbc->bank[bank].br & BR_V))
+ continue;
+
+ /* Not NAND */
+ if ((lbc->bank[bank].br & BR_MSEL) != BR_MS_FCM)
+ continue;
+
+ /* Wrong address */
+ if ((lbc->bank[bank].br & BR_BA) !=
+ ((u32)(nand->IO_ADDR_R) & lbc->bank[bank].or & OR_FCM_AM))
+ continue;
+
+ fcm->bank = bank;
+ fcm->fmr = FMR_ECCM; /* rest filled in later */
+ fcm->fmr = 0; /* rest filled in later */
+ fcm->read_bytes = 0;
+ fcm->index = 0;
+ fcm->pgs = (lbc->bank[bank].or >> OR_FCM_PGS_SHIFT) & 1;
+ fcm->base = lbc->bank[bank].br & BR_BA;
+ fcm->addr = (u8 *)fcm->base;
+ nand->priv = fcm;
+ break;
+ }
+
+ if (!nand->priv) {
+ printk (KERN_ERR "board_nand_init():" \
+ " Could not find matching Chip Select\n");
+ return -1;
+ }
+
+ /* set up nand options */
+ nand->options = NAND_NO_AUTOINCR;
+ /* set up function call table */
+ nand->hwcontrol = fcm_hwcontrol;
+ nand->waitfunc = fcm_wait;
+ nand->read_byte = fcm_read_byte;
+ nand->write_byte = fcm_write_byte;
+ nand->read_word = fcm_read_word;
+ nand->write_word = fcm_write_word;
+ nand->read_buf = fcm_read_buf;
+ nand->verify_buf = fcm_verify_buf;
+ nand->write_buf = fcm_write_buf;
+ nand->cmdfunc = fcm_cmdfunc;
+ nand->scan_bbt = fcm_scan_bbt;
+
+ /* If CS Base Register selects full hardware ECC then use it */
+ if (((lbc->bank[bank].br & BR_DECC) >> BR_DECC_SHIFT) == 2) {
+ /* put in small page settings and adjust later if needed */
+ nand->eccmode = NAND_ECC_TRANSPARENT;
+ nand->autooob = (fcm->fmr & FMR_ECCM) ?
+ &fcm_oob_sp_eccm1 : &fcm_oob_sp_eccm0;
+ nand->enable_hwecc = fcm_enable_hwecc;
+ nand->correct_data = fcm_correct_data;
+ } else {
+ /* otherwise fall back to default software ECC */
+ nand->eccmode = NAND_ECC_SOFT;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/include/configs/MPC8313ERDB.h b/include/configs/MPC8313ERDB.h
index 2519c6b..536f9ce 100644
--- a/include/configs/MPC8313ERDB.h
+++ b/include/configs/MPC8313ERDB.h
@@ -200,6 +200,7 @@
#define CFG_LBC_MRTPR 0x20000000 /*TODO */ /* LB refresh timer prescal, 266MHz/32 */
/* drivers/nand/nand.c */
+#define CFG_NAND_MPC831X
#define CFG_NAND_BASE 0xE2800000 /* 0xF0000000 */
#define CFG_MAX_NAND_DEVICE 1
#define NAND_MAX_CHIPS 1
@@ -355,7 +356,8 @@
| CFG_CMD_I2C \
| CFG_CMD_MII \
| CFG_CMD_DATE \
- | CFG_CMD_PCI)
+ | CFG_CMD_PCI \
+ | CFG_CMD_NAND)
#define CONFIG_CMDLINE_EDITING 1
--
1.5.0.3
More information about the U-Boot
mailing list