[U-Boot-Users] [PATCH 2/2] NAND: Add mpc831x support.

Signed-off-by: Nick Spence nick.spence@freescale.com Signed-off-by: Scott Wood scottwood@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@freescale.com + * Wilson.Lo@freescale.com + * scottwood@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
participants (1)
-
Scott Wood