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 + -
显示快捷键?