fsl_elbc_nand.c
来自「基于linux-2.6.28的mtd驱动」· C语言 代码 · 共 1,103 行 · 第 1/3 页
C
1,103 行
out_be32(&lbc->fbcr, 0); ctrl->read_bytes = 0; fsl_elbc_run_command(mtd); return; /* SEQIN sets up the addr buffer and all registers except the length */ case NAND_CMD_SEQIN: { __be32 fcr; dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_SEQIN/PAGE_PROG, " "page_addr: 0x%x, column: 0x%x.\n", page_addr, column); ctrl->column = column; ctrl->oob = 0; if (priv->page_size) { fcr = (NAND_CMD_SEQIN << FCR_CMD0_SHIFT) | (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT); out_be32(&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)); } else { fcr = (NAND_CMD_PAGEPROG << FCR_CMD1_SHIFT) | (NAND_CMD_SEQIN << FCR_CMD2_SHIFT); out_be32(&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->writesize) { /* OOB area --> READOOB */ column -= mtd->writesize; fcr |= NAND_CMD_READOOB << FCR_CMD0_SHIFT; ctrl->oob = 1; } else if (column < 256) { /* First 256 bytes --> READ0 */ fcr |= NAND_CMD_READ0 << FCR_CMD0_SHIFT; } else { /* Second 256 bytes --> READ1 */ fcr |= NAND_CMD_READ1 << FCR_CMD0_SHIFT; } } out_be32(&lbc->fcr, fcr); set_addr(mtd, column, page_addr, ctrl->oob); return; } /* PAGEPROG reuses all of the setup from SEQIN and adds the length */ case NAND_CMD_PAGEPROG: { int full_page; dev_vdbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_PAGEPROG " "writing %d bytes.\n", ctrl->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 (ctrl->oob || ctrl->column != 0 || ctrl->index != mtd->writesize + mtd->oobsize) { out_be32(&lbc->fbcr, ctrl->index); full_page = 0; } 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; } /* CMD_STATUS must read the status byte while CEB is active */ /* Note - it does not wait for the ready line */ case NAND_CMD_STATUS: out_be32(&lbc->fir, (FIR_OP_CM0 << FIR_OP0_SHIFT) | (FIR_OP_RBW << FIR_OP1_SHIFT)); out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); out_be32(&lbc->fbcr, 1); set_addr(mtd, 0, 0, 0); ctrl->read_bytes = 1; fsl_elbc_run_command(mtd); /* The chip always seems to report that it is * write-protected, even when it is not. */ setbits8(ctrl->addr, NAND_STATUS_WP); return; /* RESET without waiting for the ready line */ case NAND_CMD_RESET: dev_dbg(ctrl->dev, "fsl_elbc_cmdfunc: NAND_CMD_RESET.\n"); out_be32(&lbc->fir, FIR_OP_CM0 << FIR_OP0_SHIFT); out_be32(&lbc->fcr, NAND_CMD_RESET << FCR_CMD0_SHIFT); fsl_elbc_run_command(mtd); return; default: dev_err(ctrl->dev, "fsl_elbc_cmdfunc: error, unsupported command 0x%x.\n", command); }}static void fsl_elbc_select_chip(struct mtd_info *mtd, int chip){ /* The hardware does not seem to support multiple * chips per bank. */}/* * Write buf to the FCM Controller Data Buffer */static void fsl_elbc_write_buf(struct mtd_info *mtd, const u8 *buf, int len){ struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; unsigned int bufsize = mtd->writesize + mtd->oobsize; if (len <= 0) { dev_err(ctrl->dev, "write_buf of %d bytes", len); ctrl->status = 0; return; } if ((unsigned int)len > bufsize - ctrl->index) { dev_err(ctrl->dev, "write_buf beyond end of buffer " "(%d requested, %u available)\n", len, bufsize - ctrl->index); len = bufsize - ctrl->index; } memcpy_toio(&ctrl->addr[ctrl->index], buf, len); /* * This is workaround for the weird elbc hangs during nand write, * Scott Wood says: "...perhaps difference in how long it takes a * write to make it through the localbus compared to a write to IMMR * is causing problems, and sync isn't helping for some reason." * Reading back the last byte helps though. */ in_8(&ctrl->addr[ctrl->index] + len - 1); ctrl->index += len;}/* * 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 fsl_elbc_read_byte(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; /* If there are still bytes in the FCM, then use the next byte. */ if (ctrl->index < ctrl->read_bytes) return in_8(&ctrl->addr[ctrl->index++]); dev_err(ctrl->dev, "read_byte beyond end of buffer\n"); return ERR_BYTE;}/* * Read from the FCM Controller Data Buffer */static void fsl_elbc_read_buf(struct mtd_info *mtd, u8 *buf, int len){ struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; int avail; if (len < 0) return; avail = min((unsigned int)len, ctrl->read_bytes - ctrl->index); memcpy_fromio(buf, &ctrl->addr[ctrl->index], avail); ctrl->index += avail; if (len > avail) dev_err(ctrl->dev, "read_buf beyond end of buffer " "(%d requested, %d available)\n", 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) { dev_err(ctrl->dev, "write_buf of %d bytes", len); return -EINVAL; } if ((unsigned int)len > ctrl->read_bytes - ctrl->index) { dev_err(ctrl->dev, "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. */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; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; if (ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; /* Use READ_STATUS command, but wait for the device to be ready */ ctrl->use_mdr = 0; out_be32(&lbc->fir, (FIR_OP_CW0 << FIR_OP0_SHIFT) | (FIR_OP_RBW << FIR_OP1_SHIFT)); out_be32(&lbc->fcr, NAND_CMD_STATUS << FCR_CMD0_SHIFT); out_be32(&lbc->fbcr, 1); set_addr(mtd, 0, 0, 0); ctrl->read_bytes = 1; fsl_elbc_run_command(mtd); if (ctrl->status != LTESR_CC) return NAND_STATUS_FAIL; /* The chip always seems to report that it is * write-protected, even when it is not. */ setbits8(ctrl->addr, NAND_STATUS_WP); return fsl_elbc_read_byte(mtd);}static int fsl_elbc_chip_init_tail(struct mtd_info *mtd){ struct nand_chip *chip = mtd->priv; struct fsl_elbc_mtd *priv = chip->priv; struct fsl_elbc_ctrl *ctrl = priv->ctrl; struct fsl_lbc_regs __iomem *lbc = ctrl->regs; unsigned int al; /* calculate FMR Address Length field */ al = 0; if (chip->pagemask & 0xffff0000) al++; if (chip->pagemask & 0xff000000) al++; /* add to ECCM mode set in fsl_elbc_init */ priv->fmr |= (12 << FMR_CWTO_SHIFT) | /* Timeout > 12 ms */ (al << FMR_AL_SHIFT); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->numchips = %d\n", chip->numchips); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chipsize = %ld\n", chip->chipsize); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->pagemask = %8x\n", chip->pagemask); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_delay = %d\n", chip->chip_delay); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->badblockpos = %d\n", chip->badblockpos); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->chip_shift = %d\n", chip->chip_shift); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->page_shift = %d\n", chip->page_shift); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->phys_erase_shift = %d\n", chip->phys_erase_shift); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecclayout = %p\n", chip->ecclayout); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.mode = %d\n", chip->ecc.mode); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.steps = %d\n", chip->ecc.steps); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.bytes = %d\n", chip->ecc.bytes); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.total = %d\n", chip->ecc.total); dev_dbg(ctrl->dev, "fsl_elbc_init: nand->ecc.layout = %p\n", chip->ecc.layout); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->flags = %08x\n", mtd->flags); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->size = %d\n", mtd->size); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->erasesize = %d\n", mtd->erasesize); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->writesize = %d\n", mtd->writesize); dev_dbg(ctrl->dev, "fsl_elbc_init: mtd->oobsize = %d\n", mtd->oobsize); /* adjust Option Register and ECC to match Flash page size */ if (mtd->writesize == 512) { priv->page_size = 0; clrbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); } else if (mtd->writesize == 2048) { priv->page_size = 1; setbits32(&lbc->bank[priv->bank].or, OR_FCM_PGS); /* adjust ecc setup if needed */ if ((in_be32(&lbc->bank[priv->bank].br) & BR_DECC) == BR_DECC_CHK_GEN) { chip->ecc.size = 512; chip->ecc.layout = (priv->fmr & FMR_ECCM) ? &fsl_elbc_oob_lp_eccm1 : &fsl_elbc_oob_lp_eccm0; chip->badblock_pattern = &largepage_memorybased; } } else { dev_err(ctrl->dev, "fsl_elbc_init: page size %d is not supported\n", mtd->writesize); return -1; }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?