onenand_base.c

来自「linux 内核源代码」· C语言 代码 · 共 2,493 行 · 第 1/5 页

C
2,493
字号
			printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret);			break;		}		read += thislen;		if (read == len)			break;		buf += thislen;		/* Read more? */		if (read < len) {			/* Page size */			from += mtd->writesize;			column = 0;		}	}	ops->oobretlen = read;	return ret;}/** * onenand_read - [MTD Interface] Read data from flash * @param mtd		MTD device structure * @param from		offset to read from * @param len		number of bytes to read * @param retlen	pointer to variable to store the number of read bytes * @param buf		the databuffer to put data * * Read with ecc*/static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len,	size_t *retlen, u_char *buf){	struct mtd_oob_ops ops = {		.len	= len,		.ooblen	= 0,		.datbuf	= buf,		.oobbuf	= NULL,	};	int ret;	onenand_get_device(mtd, FL_READING);	ret = onenand_read_ops_nolock(mtd, from, &ops);	onenand_release_device(mtd);	*retlen = ops.retlen;	return ret;}/** * onenand_read_oob - [MTD Interface] Read main and/or out-of-band * @param mtd:		MTD device structure * @param from:		offset to read from * @param ops:		oob operation description structure * Read main and/or out-of-band */static int onenand_read_oob(struct mtd_info *mtd, loff_t from,			    struct mtd_oob_ops *ops){	int ret;	switch (ops->mode) {	case MTD_OOB_PLACE:	case MTD_OOB_AUTO:		break;	case MTD_OOB_RAW:		/* Not implemented yet */	default:		return -EINVAL;	}	onenand_get_device(mtd, FL_READING);	if (ops->datbuf)		ret = onenand_read_ops_nolock(mtd, from, ops);	else		ret = onenand_read_oob_nolock(mtd, from, ops);	onenand_release_device(mtd);	return ret;}/** * onenand_bbt_wait - [DEFAULT] wait until the command is done * @param mtd		MTD device structure * @param state		state to select the max. timeout value * * Wait for command done. */static int onenand_bbt_wait(struct mtd_info *mtd, int state){	struct onenand_chip *this = mtd->priv;	unsigned long timeout;	unsigned int interrupt;	unsigned int ctrl;	/* The 20 msec is enough */	timeout = jiffies + msecs_to_jiffies(20);	while (time_before(jiffies, timeout)) {		interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);		if (interrupt & ONENAND_INT_MASTER)			break;	}	/* To get correct interrupt status in timeout case */	interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT);	ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);	if (ctrl & ONENAND_CTRL_ERROR) {		printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl);		/* Initial bad block case */		if (ctrl & ONENAND_CTRL_LOAD)			return ONENAND_BBT_READ_ERROR;		return ONENAND_BBT_READ_FATAL_ERROR;	}	if (interrupt & ONENAND_INT_READ) {		int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);		if (ecc & ONENAND_ECC_2BIT_ALL)			return ONENAND_BBT_READ_ERROR;	} else {		printk(KERN_ERR "onenand_bbt_wait: read timeout!"			"ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt);		return ONENAND_BBT_READ_FATAL_ERROR;	}	return 0;}/** * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan * @param mtd		MTD device structure * @param from		offset to read from * @param ops		oob operation description structure * * OneNAND read out-of-band data from the spare area for bbt scan */int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, 			    struct mtd_oob_ops *ops){	struct onenand_chip *this = mtd->priv;	int read = 0, thislen, column;	int ret = 0;	size_t len = ops->ooblen;	u_char *buf = ops->oobbuf;	DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len);	/* Initialize return value */	ops->oobretlen = 0;	/* Do not allow reads past end of device */	if (unlikely((from + len) > mtd->size)) {		printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n");		return ONENAND_BBT_READ_FATAL_ERROR;	}	/* Grab the lock and see if the device is available */	onenand_get_device(mtd, FL_READING);	column = from & (mtd->oobsize - 1);	while (read < len) {		cond_resched();		thislen = mtd->oobsize - column;		thislen = min_t(int, thislen, len);		this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize);		onenand_update_bufferram(mtd, from, 0);		ret = onenand_bbt_wait(mtd, FL_READING);		if (ret)			break;		this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen);		read += thislen;		if (read == len)			break;		buf += thislen;		/* Read more? */		if (read < len) {			/* Update Page size */			from += this->writesize;			column = 0;		}	}	/* Deselect and wake up anyone waiting on the device */	onenand_release_device(mtd);	ops->oobretlen = read;	return ret;}#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE/** * onenand_verify_oob - [GENERIC] verify the oob contents after a write * @param mtd		MTD device structure * @param buf		the databuffer to verify * @param to		offset to read from */static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to){	struct onenand_chip *this = mtd->priv;	char oobbuf[64];	int status, i;	this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize);	onenand_update_bufferram(mtd, to, 0);	status = this->wait(mtd, FL_READING);	if (status)		return status;	this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);	for (i = 0; i < mtd->oobsize; i++)		if (buf[i] != 0xFF && buf[i] != oobbuf[i])			return -EBADMSG;	return 0;}/** * onenand_verify - [GENERIC] verify the chip contents after a write * @param mtd          MTD device structure * @param buf          the databuffer to verify * @param addr         offset to read from * @param len          number of bytes to read and compare */static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len){	struct onenand_chip *this = mtd->priv;	void __iomem *dataram;	int ret = 0;	int thislen, column;	while (len != 0) {		thislen = min_t(int, this->writesize, len);		column = addr & (this->writesize - 1);		if (column + thislen > this->writesize)			thislen = this->writesize - column;		this->command(mtd, ONENAND_CMD_READ, addr, this->writesize);		onenand_update_bufferram(mtd, addr, 0);		ret = this->wait(mtd, FL_READING);		if (ret)			return ret;		onenand_update_bufferram(mtd, addr, 1);		dataram = this->base + ONENAND_DATARAM;		dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM);		if (memcmp(buf, dataram + column, thislen))			return -EBADMSG;		len -= thislen;		buf += thislen;		addr += thislen;	}	return 0;}#else#define onenand_verify(...)		(0)#define onenand_verify_oob(...)		(0)#endif#define NOTALIGNED(x)	((x & (this->subpagesize - 1)) != 0)/** * onenand_fill_auto_oob - [Internal] oob auto-placement transfer * @param mtd		MTD device structure * @param oob_buf	oob buffer * @param buf		source address * @param column	oob offset to write to * @param thislen	oob length to write */static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf,				  const u_char *buf, int column, int thislen){	struct onenand_chip *this = mtd->priv;	struct nand_oobfree *free;	int writecol = column;	int writeend = column + thislen;	int lastgap = 0;	unsigned int i;	free = this->ecclayout->oobfree;	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {		if (writecol >= lastgap)			writecol += free->offset - lastgap;		if (writeend >= lastgap)			writeend += free->offset - lastgap;		lastgap = free->offset + free->length;	}	free = this->ecclayout->oobfree;	for (i = 0; i < MTD_MAX_OOBFREE_ENTRIES && free->length; i++, free++) {		int free_end = free->offset + free->length;		if (free->offset < writeend && free_end > writecol) {			int st = max_t(int,free->offset,writecol);			int ed = min_t(int,free_end,writeend);			int n = ed - st;			memcpy(oob_buf + st, buf, n);			buf += n;		} else if (column == 0)			break;	}	return 0;}/** * onenand_write_ops_nolock - [OneNAND Interface] write main and/or out-of-band * @param mtd		MTD device structure * @param to		offset to write to * @param ops		oob operation description structure * * Write main and/or oob with ECC */static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,				struct mtd_oob_ops *ops){	struct onenand_chip *this = mtd->priv;	int written = 0, column, thislen, subpage;	int oobwritten = 0, oobcolumn, thisooblen, oobsize;	size_t len = ops->len;	size_t ooblen = ops->ooblen;	const u_char *buf = ops->datbuf;	const u_char *oob = ops->oobbuf;	u_char *oobbuf;	int ret = 0;	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_ops_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);	/* Initialize retlen, in case of early exit */	ops->retlen = 0;	ops->oobretlen = 0;	/* Do not allow writes past end of device */	if (unlikely((to + len) > mtd->size)) {		printk(KERN_ERR "onenand_write_ops_nolock: Attempt write to past end of device\n");		return -EINVAL;	}	/* Reject writes, which are not page aligned */        if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) {                printk(KERN_ERR "onenand_write_ops_nolock: Attempt to write not page aligned data\n");                return -EINVAL;        }	if (ops->mode == MTD_OOB_AUTO)		oobsize = this->ecclayout->oobavail;	else		oobsize = mtd->oobsize;	oobcolumn = to & (mtd->oobsize - 1);	column = to & (mtd->writesize - 1);	/* Loop until all data write */	while (written < len) {		u_char *wbuf = (u_char *) buf;		thislen = min_t(int, mtd->writesize - column, len - written);		thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);		cond_resched();		this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);		/* Partial page write */		subpage = thislen < mtd->writesize;		if (subpage) {			memset(this->page_buf, 0xff, mtd->writesize);			memcpy(this->page_buf + column, buf, thislen);			wbuf = this->page_buf;		}		this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);		if (oob) {			oobbuf = this->oob_buf;			/* We send data to spare ram with oobsize			 * to prevent byte access */			memset(oobbuf, 0xff, mtd->oobsize);			if (ops->mode == MTD_OOB_AUTO)				onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);			else				memcpy(oobbuf + oobcolumn, oob, thisooblen);			oobwritten += thisooblen;			oob += thisooblen;			oobcolumn = 0;		} else			oobbuf = (u_char *) ffchars;		this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);		this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);		ret = this->wait(mtd, FL_WRITING);		/* In partial page write we don't update bufferram */		onenand_update_bufferram(mtd, to, !ret && !subpage);		if (ONENAND_IS_2PLANE(this)) {			ONENAND_SET_BUFFERRAM1(this);			onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);		}		if (ret) {			printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);			break;		}		/* Only check verify write turn on */		ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen);		if (ret) {			printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);			break;		}		written += thislen;		if (written == len)			break;		column = 0;		to += thislen;		buf += thislen;	}	/* Deselect and wake up anyone waiting on the device */	onenand_release_device(mtd);	ops->retlen = written;	return ret;}/** * onenand_write_oob_nolock - [Internal] OneNAND write out-of-band * @param mtd		MTD device structure * @param to		offset to write to * @param len		number of bytes to write * @param retlen	pointer to variable to store the number of written bytes * @param buf		the data to write * @param mode		operation mode * * OneNAND write out-of-band */static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to,				    struct mtd_oob_ops *ops){	struct onenand_chip *this = mtd->priv;	int column, ret = 0, oobsize;	int written = 0;	u_char *oobbuf;	size_t len = ops->ooblen;	const u_char *buf = ops->oobbuf;	mtd_oob_mode_t mode = ops->mode;	to += ops->ooboffs;	DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob_nolock: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);	/* Initialize retlen, in case of early exit */	ops->oobretlen = 0;	if (mode == MTD_OOB_AUTO)		oobsize = this->ecclayout->oobavail;	else		oobsize = mtd->oobsize;	column = to & (mtd->oobsize - 1);	if (unlikely(column >= oobsize)) {		printk(KERN_ERR "onenand_write_oob_nolock: Attempted to start write outside oob\n");		return -EINVAL;	}	/* For compatibility with NAND: Do not allow write past end of page */	if (unlikely(column + len > oobsize)) {		printk(KERN_ERR "onenand_write_oob_nolock: "		      "Attempt to write past end of page\n");		return -EINVAL;	}	/* Do not allow reads past end of device */	if (unlikely(to >= mtd->size ||		     column + len > ((mtd->size >> this->page_shift) -

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?