X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmtd%2Fnand%2Ffsl_elbc_nand.c;h=e85832d319d0270231aa5aadef1c424a555832ea;hb=1d2f74690cace803844bce198a6d5fa1b6cd11f9;hp=acdb43112a6de395f2a31ba78c38c6af5ea481ea;hpb=f51cdaf19141151ce2b40d562a468605340f2315;p=u-boot diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index acdb43112a..e85832d319 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -5,23 +5,12 @@ * Authors: Nick Spence , * Scott Wood * - * 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 + * SPDX-License-Identifier: GPL-2.0+ */ #include #include +#include #include #include @@ -48,7 +37,6 @@ #define MAX_BANKS 8 #define ERR_BYTE 0xFF /* Value returned for read bytes when read failed */ -#define FCM_TIMEOUT_MSECS 10 /* Maximum number of mSecs to wait for FCM */ #define LTESR_NAND_MASK (LTESR_FCT | LTESR_PAR | LTESR_CC) @@ -57,7 +45,6 @@ struct fsl_elbc_ctrl; /* mtd information per set */ struct fsl_elbc_mtd { - struct mtd_info mtd; struct nand_chip chip; struct fsl_elbc_ctrl *ctrl; @@ -85,7 +72,6 @@ struct fsl_elbc_ctrl { unsigned int mdr; /* UPM/FCM Data Register value */ unsigned int use_mdr; /* Non zero if the MDR is to be set */ unsigned int oob; /* Non zero if operating on OOB data */ - uint8_t *oob_poi; /* Place to write ECC after read back */ }; /* These map to the positions used by the FCM hardware ECC generator */ @@ -212,7 +198,8 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; fsl_lbc_t *lbc = ctrl->regs; - long long end_tick; + u32 timeo = (CONFIG_SYS_HZ * 10) / 1000; + u32 time_start; u32 ltesr; /* Setup the FMR[OP] to execute without write protection */ @@ -231,10 +218,10 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) out_be32(&lbc->lsor, priv->bank); /* wait for FCM complete flag or timeout */ - end_tick = usec2ticks(FCM_TIMEOUT_MSECS * 1000) + get_ticks(); + time_start = get_timer(0); ltesr = 0; - while (end_tick > get_ticks()) { + while (get_timer(time_start) < timeo) { ltesr = in_be32(&lbc->ltesr); if (ltesr & LTESR_CC) break; @@ -341,18 +328,21 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* READID must read all 5 possible bytes while CEB is active */ case NAND_CMD_READID: - vdbg("fsl_elbc_cmdfunc: NAND_CMD_READID.\n"); + case NAND_CMD_PARAM: + vdbg("fsl_elbc_cmdfunc: NAND_CMD 0x%x.\n", command); out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_UA << FIR_OP1_SHIFT) | (FIR_OP_RBW << FIR_OP2_SHIFT)); - out_be32(&lbc->fcr, NAND_CMD_READID << FCR_CMD0_SHIFT); - /* 5 bytes for manuf, device and exts */ - out_be32(&lbc->fbcr, 5); - ctrl->read_bytes = 5; + out_be32(&lbc->fcr, command << FCR_CMD0_SHIFT); + /* + * although currently it's 8 bytes for READID, we always read + * the maximum 256 bytes(for PARAM) + */ + out_be32(&lbc->fbcr, 256); + ctrl->read_bytes = 256; ctrl->use_mdr = 1; - ctrl->mdr = 0; - + ctrl->mdr = column; set_addr(mtd, 0, 0, 0); fsl_elbc_run_command(mtd); return; @@ -436,7 +426,6 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { - int full_page; vdbg("fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " "writing %d bytes.\n", ctrl->index); @@ -445,34 +434,13 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, * write so the HW generates the ECC. */ if (ctrl->oob || ctrl->column != 0 || - ctrl->index != mtd->writesize + mtd->oobsize) { + ctrl->index != mtd->writesize + mtd->oobsize) out_be32(&lbc->fbcr, ctrl->index); - full_page = 0; - } else { + else out_be32(&lbc->fbcr, 0); - full_page = 1; - } fsl_elbc_run_command(mtd); - /* Read back the page in order to fill in the ECC for the - * caller. Is this really needed? - */ - if (full_page && ctrl->oob_poi) { - out_be32(&lbc->fbcr, 3); - set_addr(mtd, 6, page_addr, 1); - - ctrl->read_bytes = mtd->writesize + 9; - - fsl_elbc_do_read(chip, 1); - fsl_elbc_run_command(mtd); - - memcpy_fromio(ctrl->oob_poi + 6, - &ctrl->addr[ctrl->index], 3); - ctrl->index += 3; - } - - ctrl->oob_poi = NULL; return; } @@ -593,39 +561,6 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) len, avail); } -/* - * Verify buffer against the FCM Controller Data Buffer - */ -static int fsl_elbc_verify_buf(struct mtd_info *mtd, - const u_char *buf, int len) -{ - struct nand_chip *chip = mtd->priv; - struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - int i; - - if (len < 0) { - printf("write_buf of %d bytes", len); - return -EINVAL; - } - - if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { - printf("verify_buf beyond end of buffer " - "(%d requested, %u available)\n", - len, ctrl->read_bytes - ctrl->index); - - ctrl->index = ctrl->read_bytes; - return -EINVAL; - } - - for (i = 0; i < len; i++) - if (in_8(&ctrl->addr[ctrl->index + i]) != buf[i]) - break; - - ctrl->index += len; - return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; -} - /* This function is called after Program and Erase Operations to * check for success or failure. */ @@ -660,9 +595,8 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) return fsl_elbc_read_byte(mtd); } -static int fsl_elbc_read_page(struct mtd_info *mtd, - struct nand_chip *chip, - uint8_t *buf, int page) +static int fsl_elbc_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, int page) { fsl_elbc_read_buf(mtd, buf, mtd->writesize); fsl_elbc_read_buf(mtd, chip->oob_poi, mtd->oobsize); @@ -676,17 +610,13 @@ static int fsl_elbc_read_page(struct mtd_info *mtd, /* ECC will be calculated automatically, and errors will be detected in * waitfunc. */ -static void fsl_elbc_write_page(struct mtd_info *mtd, - struct nand_chip *chip, - const uint8_t *buf) +static int fsl_elbc_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, int oob_required) { - struct fsl_elbc_mtd *priv = chip->priv; - struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_elbc_write_buf(mtd, buf, mtd->writesize); fsl_elbc_write_buf(mtd, chip->oob_poi, mtd->oobsize); - ctrl->oob_poi = chip->oob_poi; + return 0; } static struct fsl_elbc_ctrl *elbc_ctrl; @@ -711,10 +641,13 @@ static void fsl_elbc_ctrl_init(void) elbc_ctrl->addr = NULL; } -int board_nand_init(struct nand_chip *nand) +static int fsl_elbc_chip_init(int devnum, u8 *addr) { + struct mtd_info *mtd = &nand_info[devnum]; + struct nand_chip *nand; struct fsl_elbc_mtd *priv; uint32_t br = 0, or = 0; + int ret; if (!elbc_ctrl) { fsl_elbc_ctrl_init(); @@ -727,19 +660,19 @@ int board_nand_init(struct nand_chip *nand) return -ENOMEM; priv->ctrl = elbc_ctrl; - priv->vbase = nand->IO_ADDR_R; + priv->vbase = addr; /* Find which chip select it is connected to. It'd be nice * if we could pass more than one datum to the NAND driver... */ for (priv->bank = 0; priv->bank < MAX_BANKS; priv->bank++) { - phys_addr_t base_addr = virt_to_phys(nand->IO_ADDR_R); + phys_addr_t phys_addr = virt_to_phys(addr); br = in_be32(&elbc_ctrl->regs->bank[priv->bank].br); or = in_be32(&elbc_ctrl->regs->bank[priv->bank].or); if ((br & BR_V) && (br & BR_MSEL) == BR_MS_FCM && - (br & or & BR_BA) == BR_PHYS_ADDR(base_addr)) + (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) break; } @@ -749,6 +682,9 @@ int board_nand_init(struct nand_chip *nand) return -ENODEV; } + nand = &priv->chip; + mtd->priv = nand; + elbc_ctrl->chips[priv->bank] = priv; /* fill in nand_chip structure */ @@ -756,7 +692,6 @@ int board_nand_init(struct nand_chip *nand) nand->read_byte = fsl_elbc_read_byte; nand->write_buf = fsl_elbc_write_buf; nand->read_buf = fsl_elbc_read_buf; - nand->verify_buf = fsl_elbc_verify_buf; nand->select_chip = fsl_elbc_select_chip; nand->cmdfunc = fsl_elbc_cmdfunc; nand->waitfunc = fsl_elbc_wait; @@ -766,8 +701,8 @@ int board_nand_init(struct nand_chip *nand) nand->bbt_md = &bbt_mirror_descr; /* set up nand options */ - nand->options = NAND_NO_READRDY | NAND_NO_AUTOINCR | - NAND_USE_FLASH_BBT; + nand->options = NAND_NO_SUBPAGE_WRITE; + nand->bbt_options = NAND_BBT_USE_FLASH; nand->controller = &elbc_ctrl->controller; nand->priv = priv; @@ -775,20 +710,8 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.read_page = fsl_elbc_read_page; nand->ecc.write_page = fsl_elbc_write_page; -#ifdef CONFIG_FSL_ELBC_FMR - priv->fmr = CONFIG_FSL_ELBC_FMR; -#else priv->fmr = (15 << FMR_CWTO_SHIFT) | (2 << FMR_AL_SHIFT); - /* - * Hardware expects small page has ECCM0, large page has ECCM1 - * when booting from NAND. Board config can override if not - * booting from NAND. - */ - if (or & OR_FCM_PGS) - priv->fmr |= FMR_ECCM; -#endif - /* If CS Base Register selects full hardware ECC then use it */ if ((br & BR_DECC) == BR_DECC_CHK_GEN) { nand->ecc.mode = NAND_ECC_HW; @@ -800,16 +723,36 @@ int board_nand_init(struct nand_chip *nand) nand->ecc.size = 512; nand->ecc.bytes = 3; nand->ecc.steps = 1; + nand->ecc.strength = 1; } else { - /* otherwise fall back to default software ECC */ + /* otherwise fall back to software ECC */ +#if defined(CONFIG_NAND_ECC_BCH) + nand->ecc.mode = NAND_ECC_SOFT_BCH; +#else nand->ecc.mode = NAND_ECC_SOFT; +#endif } + ret = nand_scan_ident(mtd, 1, NULL); + if (ret) + return ret; + /* Large-page-specific setup */ - if (or & OR_FCM_PGS) { + if (mtd->writesize == 2048) { + setbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + priv->page_size = 1; nand->badblock_pattern = &largepage_memorybased; + /* + * Hardware expects small page has ECCM0, large page has + * ECCM1 when booting from NAND, and we follow that even + * when not booting from NAND. + */ + priv->fmr |= FMR_ECCM; + /* adjust ecc setup if needed */ if ((br & BR_DECC) == BR_DECC_CHK_GEN) { nand->ecc.steps = 4; @@ -817,7 +760,36 @@ int board_nand_init(struct nand_chip *nand) &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; } + } else if (mtd->writesize == 512) { + clrbits_be32(&elbc_ctrl->regs->bank[priv->bank].or, + OR_FCM_PGS); + in_be32(&elbc_ctrl->regs->bank[priv->bank].or); + } else { + return -ENODEV; } + ret = nand_scan_tail(mtd); + if (ret) + return ret; + + ret = nand_register(devnum); + if (ret) + return ret; + return 0; } + +#ifndef CONFIG_SYS_NAND_BASE_LIST +#define CONFIG_SYS_NAND_BASE_LIST { CONFIG_SYS_NAND_BASE } +#endif + +static unsigned long base_address[CONFIG_SYS_MAX_NAND_DEVICE] = + CONFIG_SYS_NAND_BASE_LIST; + +void board_nand_init(void) +{ + int i; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + fsl_elbc_chip_init(i, (u8 *)base_address[i]); +}