X-Git-Url: https://git.kernelconcepts.de/?p=karo-tx-uboot.git;a=blobdiff_plain;f=drivers%2Fmtd%2Fnand%2Ffsl_elbc_nand.c;h=3372b64212559cf1c9c922ec1a0f22cbea1d3c32;hp=367c7d7fcd29f53545f3b2685bf9d8e4b9c3217c;hb=1454ba8e56d88a8b95ac8050cde2c07c651cd0ae;hpb=4cc64742a89e8ce90c69c3c85e4e9f4706062f2f diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 367c7d7fcd..3372b64212 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; @@ -75,7 +62,7 @@ struct fsl_elbc_ctrl { struct fsl_elbc_mtd *chips[MAX_BANKS]; /* device info */ - fsl_lbus_t *regs; + fsl_lbc_t *regs; u8 __iomem *addr; /* Address of assigned FCM buffer */ unsigned int page; /* Last page written to / read from */ unsigned int read_bytes; /* Number of bytes read during command */ @@ -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 */ @@ -171,7 +157,7 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr, int oob) struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbus_t *lbc = ctrl->regs; + fsl_lbc_t *lbc = ctrl->regs; int buf_num; ctrl->page = page_addr; @@ -211,8 +197,9 @@ static int fsl_elbc_run_command(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbus_t *lbc = ctrl->regs; - long long end_tick; + fsl_lbc_t *lbc = ctrl->regs; + 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; @@ -261,7 +248,7 @@ static void fsl_elbc_do_read(struct nand_chip *chip, int oob) { struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbus_t *lbc = ctrl->regs; + fsl_lbc_t *lbc = ctrl->regs; if (priv->page_size) { out_be32(&lbc->fir, @@ -295,7 +282,7 @@ static void fsl_elbc_cmdfunc(struct mtd_info *mtd, unsigned int command, struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbus_t *lbc = ctrl->regs; + fsl_lbc_t *lbc = ctrl->regs; ctrl->use_mdr = 0; @@ -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,6 +561,7 @@ static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len) len, avail); } +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) /* * Verify buffer against the FCM Controller Data Buffer */ @@ -625,6 +594,7 @@ static int fsl_elbc_verify_buf(struct mtd_info *mtd, ctrl->index += len; return i == len && ctrl->status == LTESR_CC ? 0 : -EIO; } +#endif /* This function is called after Program and Erase Operations to * check for success or failure. @@ -633,7 +603,7 @@ static int fsl_elbc_wait(struct mtd_info *mtd, struct nand_chip *chip) { struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; - fsl_lbus_t *lbc = ctrl->regs; + fsl_lbc_t *lbc = ctrl->regs; if (ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; @@ -660,9 +630,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) +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 +645,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; @@ -697,11 +662,7 @@ static void fsl_elbc_ctrl_init(void) if (!elbc_ctrl) return; -#ifdef CONFIG_MPC85xx - elbc_ctrl->regs = (void *)CONFIG_SYS_MPC85xx_LBC_ADDR; -#else - elbc_ctrl->regs = &((immap_t *)CONFIG_SYS_IMMR)->lbus; -#endif + elbc_ctrl->regs = LBC_BASE_ADDR; /* clear event registers */ out_be32(&elbc_ctrl->regs->ltesr, LTESR_NAND_MASK); @@ -715,10 +676,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, or; + uint32_t br = 0, or = 0; + int ret; if (!elbc_ctrl) { fsl_elbc_ctrl_init(); @@ -731,17 +695,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 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) == (phys_addr_t)nand->IO_ADDR_R) + (br & or & BR_BA) == BR_PHYS_ADDR(phys_addr)) break; } @@ -751,6 +717,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 */ @@ -758,7 +727,9 @@ 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; +#if defined(CONFIG_MTD_NAND_VERIFY_WRITE) nand->verify_buf = fsl_elbc_verify_buf; +#endif nand->select_chip = fsl_elbc_select_chip; nand->cmdfunc = fsl_elbc_cmdfunc; nand->waitfunc = fsl_elbc_wait; @@ -768,8 +739,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; @@ -777,20 +748,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; @@ -802,16 +761,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; @@ -819,7 +798,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]); +}