⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nand.c

📁 linux下的MTD设备驱动源代码,配合jffs2 yaffss2文件系统.
💻 C
📖 第 1 页 / 共 4 页
字号:
	spin_unlock_bh (&this->chip_lock);	schedule ();	remove_wait_queue (&this->wq, &wait);	goto retry;}/* * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to  * general NAND and SmartMedia specs * * @mtd:	MTD device structure * @this:	NAND chip structure * @state:	state to select the max. timeout value **/static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state){	unsigned long	timeo = jiffies;	int	status;		if (state == FL_ERASING)		 timeo += (HZ * 400) / 1000;	else		 timeo += (HZ * 20) / 1000;	spin_lock_bh (&this->chip_lock);	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);	while (time_before(jiffies, timeo)) {				/* Check, if we were interrupted */		if (this->state != state) {			spin_unlock_bh (&this->chip_lock);			return 0;		}		if (this->dev_ready) {			if (this->dev_ready(mtd))				break;		}		if (this->read_byte(mtd) & 0x40)			break;								spin_unlock_bh (&this->chip_lock);		yield ();		spin_lock_bh (&this->chip_lock);	}	status = (int) this->read_byte(mtd);	spin_unlock_bh (&this->chip_lock);	return status;}/* *	Nand_page_program function is used for write and writev ! *	This function will always program a full page of data *	If you call it with a non page aligned buffer, you're lost :) * * @mtd:	MTD device structure * @this:	NAND chip structure * @page: 	startpage inside the chip, must be called with (page & this->pagemask) * @oob_buf:	out of band data buffer * @oobsel:	out of band selecttion structre * @cached:	1 = enable cached programming if supported by chip */static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, 	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached){	int 	i, status;	u_char	ecc_code[6];	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;	int  	*oob_config = oobsel->eccpos;	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;		/* FIXME: Enable cached programming */	cached = 0;		/* Send command to begin auto page programming */	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);	/* Write out complete page of data, take care of eccmode */	switch (eccmode) {	/* No ecc, write all */	case NAND_ECC_NONE:		printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");		this->write_buf(mtd, this->data_poi, mtd->oobblock);		break;			/* Software ecc 3/256, write all */	case NAND_ECC_SOFT:		for (; eccsteps; eccsteps--) {			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);			for (i = 0; i < 3; i++, eccidx++)				oob_buf[oob_config[eccidx]] = ecc_code[i];			datidx += this->eccsize;		}		this->write_buf(mtd, this->data_poi, mtd->oobblock);		break;			/* Hardware ecc 3 byte / 256 data, write 256 byte */		/* Hardware ecc 3 byte / 512 byte data, write 512 bytee */		case NAND_ECC_HW3_256:			case NAND_ECC_HW3_512:		for (; eccsteps; eccsteps--) {			this->enable_hwecc(mtd, NAND_ECC_WRITE);	/* enable hardware ecc logic for write */			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);			this->calculate_ecc(mtd, NULL, ecc_code);			for (i = 0; i < 3; i++, eccidx++)				oob_buf[oob_config[eccidx]] = ecc_code[i];			datidx += this->eccsize;		}		break;					/* Hardware ecc 6 byte / 512 byte data, write 512 byte */		case NAND_ECC_HW6_512:			for (; eccsteps; eccsteps--) {			this->enable_hwecc(mtd, NAND_ECC_WRITE);	/* enable hardware ecc logic for write */			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);			this->calculate_ecc(mtd, NULL, ecc_code);			for (i = 0; i < 6; i++, eccidx++)				oob_buf[oob_config[eccidx]] = ecc_code[i];			datidx += this->eccsize;		}		break;			default:		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);		BUG();		}											/* Write out OOB data */	this->write_buf(mtd, oob_buf, mtd->oobsize);	/* Send command to actually program the data */	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);	if (!cached) {		/* call wait ready function */		status = this->waitfunc (mtd, this, FL_WRITING);		/* See if device thinks it succeeded */		if (status & 0x01) {			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);			return -EIO;		}	} else {		/* FIXME: Implement cached programming ! */		/* wait until cache is ready*/		// status = this->waitfunc (mtd, this, FL_CACHEDRPG);	}	return 0;	}#ifdef CONFIG_MTD_NAND_VERIFY_WRITE/* * The NAND device assumes that it is always writing to a cleanly erased page. * Hence, it performs its internal write verification only on bits that  * transitioned from 1 to 0. The device does NOT verify the whole page on a * byte by byte basis. It is possible that the page was not completely erased  * or the page is becoming unusable due to wear. The read with ECC would catch  * the error later when the ECC page check fails, but we would rather catch  * it early in the page write stage. Better to write no data than invalid data. * * @mtd:	MTD device structure * @this:	NAND chip structure * @page: 	startpage inside the chip, must be called with (page & this->pagemask) * @numpages:	number of pages to verify * @oob_buf:	out of band data buffer * @oobsel:	out of band selecttion structre * @chipnr:	number of the current chip * @oobmode:	1 = full buffer verify, 0 = ecc only */static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode){	int i, datidx = 0, oobofs = 0, res = -EIO;	u_char oobdata[64];	/* Send command to read back the first page */	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);	for(;;) {			/* Loop through and verify the data */		if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->oobblock)) {			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);			goto out;		}		datidx += mtd->oobblock;		/* check, if we must compare all data or if we just have to		 * compare the ecc bytes		 */		if (oobmode) {			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize)) {				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);				goto out;			}		} else {			/* Read always, else autoincrement fails */			this->read_buf(mtd, oobdata, mtd->oobsize);			if (oobsel->useecc != MTD_NANDECC_OFF) {				int ecc_bytes = oobsel->eccbytes;						for (i = 0; i < ecc_bytes; i++) {					int idx = oobsel->eccpos[i];					if (oobdata[idx] != oob_buf[oobofs + idx] ) {						DEBUG (MTD_DEBUG_LEVEL0,					       	"%s: Failed ECC write "						"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);						goto out;					}				}			}			}		oobofs += mtd->oobsize;		page++;		numpages--;		if (!numpages)			break;				/* Apply delay or wait for ready/busy pin 		 * Do this before the AUTOINCR check, so no problems		 * arise if a chip which does auto increment		 * is marked as NOAUTOINCR by the board driver.		*/		if (!this->dev_ready) 			udelay (this->chip_delay);		else			while (!this->dev_ready(mtd));						/* Check, if the chip supports auto page increment */ 		if (!NAND_CANAUTOINCR(this))			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);	}	res = 0;	/* 	 * Terminate the read command. This is faster than sending a reset command or 	 * applying a 20us delay before issuing the next programm sequence.	 * This is not a problem for all chips, but I have found a bunch of them.	 */out:	 	this->select_chip(mtd, -1);	this->select_chip(mtd, chipnr);	return res;}#endif/* * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL * * @mtd:	MTD device structure * @from:	offset to read from * @len:	number of bytes to read * @retlen:	pointer to variable to store the number of read bytes * @buf:	the databuffer to put data*/static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){	return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);}			   /* * NAND read with ECC * * @mtd:	MTD device structure * @from:	offset to read from * @len:	number of bytes to read * @retlen:	pointer to variable to store the number of read bytes * @buf:	the databuffer to put data * @oob_buf:	filesystem supplied oob data buffer * @oobsel:	oob selection structure */static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel){	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;	int erase_state = 0;	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;	struct nand_chip *this = mtd->priv;	u_char *data_poi, *oob_data = oob_buf;	u_char ecc_calc[24];	u_char ecc_code[24];        int eccmode, eccsteps;	int	*oob_config, datidx;	int	blockcheck = (mtd->erasesize >> this->page_shift) - 1;	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);	/* Do not allow reads past end of device */	if ((from + len) > mtd->size) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");		*retlen = 0;		return -EINVAL;	}	/* Grab the lock and see if the device is available */	nand_get_chip (this, mtd ,FL_READING, &erase_state);	/* use userspace supplied oobinfo, if zero */	if (oobsel == NULL)		oobsel = &mtd->oobinfo;		/* Autoplace of oob data ? Use the default placement scheme */	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)		oobsel = this->autooob;			eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;	oob_config = oobsel->eccpos;	/* Select the NAND device */	chipnr = (int)((unsigned long)from / this->chipsize);	this->select_chip(mtd, chipnr);	/* First we calculate the starting page */	realpage = from >> this->page_shift;	page = realpage & this->pagemask;	/* Get raw starting column */	col = from & (mtd->oobblock - 1);	end = mtd->oobblock;	ecc = this->eccsize;	/* Loop until all data read */	while (read < len) {				int aligned = (!col && (len - read) >= end);		/* 		 * If the read is not page aligned, we have to read into data buffer		 * due to ecc, else we read into return buffer direct		 */		if (aligned)			data_poi = &buf[read];		else 			data_poi = this->data_buf;				/* Check, if we have this page in the buffer 		 *		 * FIXME: Make it work when we must provide oob data too,		 * check the usage of data_buf oob field		 */		if (realpage == this->pagebuf && !oob_buf) {			/* aligned read ? */			if (aligned)				memcpy (data_poi, this->data_buf, end);			goto readdata;		}		/* Check, if we must send the read command */		if (sndcmd) {			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);			sndcmd = 0;		}			/* get oob area, if we have no oob buffer from fs-driver */		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)			oob_data = &this->data_buf[end];		eccsteps = this->eccsteps;				switch (eccmode) {		case NAND_ECC_NONE: {	/* No ECC, Read in a page */			static unsigned long lastwhinge = 0;			if ((lastwhinge / HZ) != (jiffies / HZ)) {				printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");				lastwhinge = jiffies;			}			this->read_buf(mtd, data_poi, end);			break;		}					case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */			this->read_buf(mtd, data_poi, end);			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) 				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);			break;						case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */		case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */				for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) {				this->enable_hwecc(mtd, NAND_ECC_READ);					this->read_buf(mtd, &data_poi[datidx], ecc);				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);	/* read from hardware */			}			break;								case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=6, datidx += ecc) {				this->enable_hwecc(mtd, NAND_ECC_READ);					this->read_buf(mtd, &data_poi[datidx], ecc);				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);	/* read from hardware */			}			break;								default:			printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);			BUG();			}		/* read oobdata */		this->read_buf(mtd, oob_data, mtd->oobsize);				/* Skip ECC, if not active */		if (eccmode == NAND_ECC_NONE)			goto readdata;					/* Pick the ECC bytes out of the oob data */		for (j = 0; j < oobsel->eccbytes; j++)			ecc_code[j] = oob_data[oob_config[j]];		/* correct data, if neccecary */		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);						/* Get next chunk of ecc bytes */			j += eccmode == NAND_ECC_HW6_512 ? 6 : 3;						/* check, if we have a fs supplied oob-buffer, 			 * This is the legacy mode. Used by YAFFS1			 */			if (oob_buf && oobsel->useecc != MTD_NANDECC_AUTOPLACE) { 				int *p = (int *)(&oob_data[mtd->oobsize]);				p[i] = ecc_status;			}						if (ecc_status == -1) {					DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);				ecc_failed++;			}		}				/* check, if we have a fs supplied oob-buffer */		if (oob_buf) {			/* without autoplace. Legacy mode used by YAFFS1 */			if (oobsel->useecc != MTD_NANDECC_AUTOPLACE) {				oob_data += mtd->oobsize + this->eccsteps * sizeof (int);			} else {				/* Walk through the autoplace chunks */				for (i = 0, j = 0; j < mtd->oobavail; i++) {					int from = oobsel->oobfree[i][0];					int num = oobsel->oobfree[i][1];					memcpy(&oob_buf[oob], &oob_data[from], num);					j+= num;				}				oob += mtd->oobavail;			}		}readdata:		/* Partial page read, transfer data into fs buffer */		if (!aligned) { 			for (j = col; j < end && read < len; j++)				buf[read++] = data_poi[j];			this->pagebuf = realpage;			} else					read += mtd->oobblock;		if (read == len)			break;			/* For subsequent reads align to page boundary. */		col = 0;		/* Increment page address */		realpage++;		/* Apply delay or wait for ready/busy pin 		 * Do this before the AUTOINCR check, so no problems		 * arise if a chip which does auto increment		 * is marked as NOAUTOINCR by the board driver.		*/		if (!this->dev_ready) 			udelay (this->chip_delay);		else			while (!this->dev_ready(mtd));						page = realpage & this->pagemask;		/* Check, if we cross a chip boundary */		if (!page) {			chipnr++;			this->select_chip(mtd, -1);			this->select_chip(mtd, chipnr);		}		/* Check, if the chip supports auto page increment 		 * or if we have hit a block boundary. 		*/ 		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))			sndcmd = 1;					}	/* Deselect and wake up anyone waiting on the device */	nand_release_chip(mtd);	/*	 * Return success, if no ECC failures, else -EIO	 * fs driver will take care of that, because	 * retlen == desired len and result == -EIO	 */	*retlen = read;	return ecc_failed ? -EIO : 0;}/* * NAND read out-of-band * * @mtd:	MTD device structure * @from:	offset to read from * @len:	number of bytes to read * @retlen:	pointer to variable to store the number of read bytes * @buf:	the databuffer to put data */static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){	int i, col, page, chipnr;	int erase_state = 0;	struct nand_chip *this = mtd->priv;	int	blockcheck = (mtd->erasesize >> this->page_shift) - 1;	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);	/* Shift to get page */	page = ((int) from) >> this->page_shift;	chipnr = (int)((unsigned long)from / this->chipsize);		/* Mask to get column */	col = from & (mtd->oobsize - 1);	/* Initialize return length value */	*retlen = 0;	/* Do not allow reads past end of device */	if ((from + len) > mtd->size) {		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");		*retlen = 0;

⌨️ 快捷键说明

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