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

📄 nand.c

📁 linux下的MTD设备驱动源代码,配合jffs2 yaffss2文件系统.
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  drivers/mtd/nand.c * *  Overview: *   This is the generic MTD driver for NAND flash devices. It should be *   capable of working with almost all NAND chips currently available. *    *	Additional technical information is available on *	http://www.linux-mtd.infradead.org/tech/nand.html *	 *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * 		  2002 Thomas Gleixner (tglx@linutronix.de) * *  02-08-2004  tglx: support for strange chips, which cannot auto increment  *		pages on read / read_oob * *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes *		pointed this out, as he marked an auto increment capable chip *		as NOAUTOINCR in the board driver. *		Make reads over block boundaries work too * *  04-14-2004	tglx: first working version for 2k page size chips * * Credits: *	David Woodhouse for adding multichip support   *	 *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the *	rework for 2K page size chips	 * * TODO: *	Enable cached programming for 2k page size chips *	Check, if mtd->ecctype should be set to MTD_ECC_HW *	if we have HW ecc support. * * $Id: nand.c,v 1.83 2004/04/30 12:27:36 gleixner Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */#include <linux/delay.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/types.h>#include <linux/mtd/mtd.h>#include <linux/mtd/nand.h>#include <linux/mtd/nand_ecc.h>#include <linux/mtd/compatmac.h>#include <linux/interrupt.h>#include <linux/bitops.h>#include <asm/io.h>/* Define default oob placement schemes for large and small page devices */static struct nand_oobinfo nand_oob_8 = {	.useecc = MTD_NANDECC_AUTOPLACE,	.eccbytes = 3,	.eccpos = {0, 1, 2},	.oobfree = { {3, 2}, {6, 2} }};static struct nand_oobinfo nand_oob_16 = {	.useecc = MTD_NANDECC_AUTOPLACE,	.eccbytes = 6,	.eccpos = {0, 1, 2, 3, 6, 7},	.oobfree = { {8, 8} }};static struct nand_oobinfo nand_oob_64 = {	.useecc = MTD_NANDECC_AUTOPLACE,	.eccbytes = 24,	.eccpos = {		40, 41, 42, 43, 44, 45, 46, 47, 		48, 49, 50, 51, 52, 53, 54, 55, 		56, 57, 58, 59, 60, 61, 62, 63},	.oobfree = { {2, 38} }};/* This is used for padding purposes in nand_write_oob */static u_char ffchars[] = {	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,};/* * NAND low-level MTD interface functions */static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,			  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs,			unsigned long count, loff_t to, size_t * retlen);static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);static void nand_sync (struct mtd_info *mtd);/* Some internal functions */static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,		struct nand_oobinfo *oobsel, int mode);#ifdef CONFIG_MTD_NAND_VERIFY_WRITEstatic 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);#else#define nand_verify_pages(...) (0)#endif		static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state);/* Deselect and wake up anyone waiting on the device */static void nand_release_chip (struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	/* De-select the NAND device */	this->select_chip(mtd, -1);	/* Release the chip */	spin_lock_bh (&this->chip_lock);	this->state = FL_READY;	wake_up (&this->wq);	spin_unlock_bh (&this->chip_lock);}static u_char nand_read_byte(struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	return readb(this->IO_ADDR_R);}static void nand_write_byte(struct mtd_info *mtd, u_char byte){	struct nand_chip *this = mtd->priv;	writeb(byte, this->IO_ADDR_W);}static u_char nand_read_byte16(struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));}static void nand_write_byte16(struct mtd_info *mtd, u_char byte){	struct nand_chip *this = mtd->priv;	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);}static u16 nand_read_word(struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	return readw(this->IO_ADDR_R);}static void nand_write_word(struct mtd_info *mtd, u16 word){	struct nand_chip *this = mtd->priv;	writew(word, this->IO_ADDR_W);}static void nand_select_chip(struct mtd_info *mtd, int chip){	struct nand_chip *this = mtd->priv;	switch(chip) {	case -1:		this->hwcontrol(mtd, NAND_CTL_CLRNCE);			break;	case 0:		this->hwcontrol(mtd, NAND_CTL_SETNCE);		break;	default:		BUG();	}}static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	for (i=0; i<len; i++)		writeb(buf[i], this->IO_ADDR_W);}static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	for (i=0; i<len; i++)		buf[i] = readb(this->IO_ADDR_R);}static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	for (i=0; i<len; i++)		if (buf[i] != readb(this->IO_ADDR_R))			return -EFAULT;	return 0;}static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;		for (i=0; i<len; i++)		writew(p[i], this->IO_ADDR_W);		}static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;	for (i=0; i<len; i++)		p[i] = readw(this->IO_ADDR_R);}static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len){	int i;	struct nand_chip *this = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;	for (i=0; i<len; i++)		if (p[i] != readw(this->IO_ADDR_R))			return -EFAULT;	return 0;}static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip){	int page, chipnr, res = 0;	struct nand_chip *this = mtd->priv;	u16 bad;	if (getchip) {		/* Shift to get page */		page = ((int) ofs) >> this->page_shift;		chipnr = (int)((unsigned long) ofs / this->chipsize);		/* Grab the lock and see if the device is available */		nand_get_chip (this, mtd, FL_READING, NULL);		/* Select the NAND device */		this->select_chip(mtd, chipnr);	} else 		page = (int) ofs;		if (this->options & NAND_BUSWIDTH_16) {		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);		bad = cpu_to_le16(this->read_word(mtd));		if (this->badblockpos & 0x1)			bad >>= 1;		if ((bad & 0xFF) != 0xff)			res = 1;	} else {		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);		if (this->read_byte(mtd) != 0xff)			res = 1;	}			if (getchip) {		/* Deselect and wake up anyone waiting on the device */		nand_release_chip(mtd);	}			return res;}/* This is the default implementation, which can be overridden by * a hardware specific driver.*/static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs){	struct nand_chip *this = mtd->priv;	u_char buf[2] = {0, 0};	size_t	retlen;		ofs += mtd->oobsize + (this->badblockpos & ~0x01);		/* We write two bytes, so we dont have to mess with 16 bit access */	return nand_write_oob (mtd, ofs , 2, &retlen, buf);}/* Check, if the device is write protected  * The function expects, that the device is already selected  */static int nand_check_wp (struct mtd_info *mtd){	struct nand_chip *this = mtd->priv;	/* Check the WP bit */	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);	return (this->read_byte(mtd) & 0x80) ? 0 : 1; }/* * Send command to NAND device */static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr){	register struct nand_chip *this = mtd->priv;	/* Adjust columns for 16 bit buswidth */	if (this->options & NAND_BUSWIDTH_16)		column >>= 1;	/* Begin command latch cycle */	this->hwcontrol(mtd, NAND_CTL_SETCLE);	/*	 * Write out the command to the device.	 */	if (command == NAND_CMD_SEQIN) {		int readcmd;		if (column >= mtd->oobblock) {			/* OOB area */			column -= mtd->oobblock;			readcmd = NAND_CMD_READOOB;		} else if (column < 256) {			/* First 256 bytes --> READ0 */			readcmd = NAND_CMD_READ0;		} else {			column -= 256;			readcmd = NAND_CMD_READ1;		}		this->write_byte(mtd, readcmd);	}	this->write_byte(mtd, command);	/* Set ALE and clear CLE to start address cycle */	this->hwcontrol(mtd, NAND_CTL_CLRCLE);	if (column != -1 || page_addr != -1) {		this->hwcontrol(mtd, NAND_CTL_SETALE);		/* Serially input address */		if (column != -1)			this->write_byte(mtd, column);		if (page_addr != -1) {			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));			/* One more address cycle for higher density devices */			if (this->chipsize & 0x0c000000) 				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));		}		/* Latch in address */		this->hwcontrol(mtd, NAND_CTL_CLRALE);	}		/* 	 * program and erase have their own busy handlers 	 * status and sequential in needs no delay	*/	switch (command) {				case NAND_CMD_PAGEPROG:	case NAND_CMD_ERASE1:	case NAND_CMD_ERASE2:	case NAND_CMD_SEQIN:	case NAND_CMD_STATUS:		return;	case NAND_CMD_RESET:		if (this->dev_ready)				break;		udelay(this->chip_delay);		this->hwcontrol(mtd, NAND_CTL_SETCLE);		this->write_byte(mtd, NAND_CMD_STATUS);		this->hwcontrol(mtd, NAND_CTL_CLRCLE);		while ( !(this->read_byte(mtd) & 0x40));		return;	/* This applies to read commands */		default:		/* 		 * If we don't have access to the busy pin, we apply the given		 * command delay		*/		if (!this->dev_ready) {			udelay (this->chip_delay);			return;		}		}		/* wait until command is processed */	while (!this->dev_ready(mtd));}/* * Send command to NAND device. This is the version for the new large page devices * We dont have the seperate regions as we have in the small page devices. * We must emulate NAND_CMD_READOOB to keep the code compatible */static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr){	register struct nand_chip *this = mtd->priv;	/* Emulate NAND_CMD_READOOB */	if (command == NAND_CMD_READOOB) {		column += mtd->oobblock;		command = NAND_CMD_READ0;	}		/* Adjust columns for 16 bit buswidth */	if (this->options & NAND_BUSWIDTH_16)		column >>= 1;			/* Begin command latch cycle */	this->hwcontrol(mtd, NAND_CTL_SETCLE);	/* Write out the command to the device. */	this->write_byte(mtd, command);	/* End command latch cycle */	this->hwcontrol(mtd, NAND_CTL_CLRCLE);	if (column != -1 || page_addr != -1) {		this->hwcontrol(mtd, NAND_CTL_SETALE);		/* Serially input address */		if (column != -1) {			this->write_byte(mtd, column & 0xff);			this->write_byte(mtd, column >> 8);		}			if (page_addr != -1) {			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));			/* One more address cycle for devices > 128MiB */			if (this->chipsize > (128 << 20))				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));		}		/* Latch in address */		this->hwcontrol(mtd, NAND_CTL_CLRALE);	}		/* 	 * program and erase have their own busy handlers 	 * status and sequential in needs no delay	*/	switch (command) {				case NAND_CMD_CACHEDPROG:	case NAND_CMD_PAGEPROG:	case NAND_CMD_ERASE1:	case NAND_CMD_ERASE2:	case NAND_CMD_SEQIN:	case NAND_CMD_STATUS:		return;	case NAND_CMD_RESET:		if (this->dev_ready)				break;		udelay(this->chip_delay);		this->hwcontrol(mtd, NAND_CTL_SETCLE);		this->write_byte(mtd, NAND_CMD_STATUS);		this->hwcontrol(mtd, NAND_CTL_CLRCLE);		while ( !(this->read_byte(mtd) & 0x40));		return;	case NAND_CMD_READ0:		/* Begin command latch cycle */		this->hwcontrol(mtd, NAND_CTL_SETCLE);		/* Write out the start read command */		this->write_byte(mtd, NAND_CMD_READSTART);		/* End command latch cycle */		this->hwcontrol(mtd, NAND_CTL_CLRCLE);		/* Fall through into ready check */			/* This applies to read commands */		default:		/* 		 * If we don't have access to the busy pin, we apply the given		 * command delay		*/		if (!this->dev_ready) {			udelay (this->chip_delay);			return;		}		}		/* wait until command is processed */	while (!this->dev_ready(mtd));}/* *	Get chip for selected access */static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state){	DECLARE_WAITQUEUE (wait, current);	/* 	 * Grab the lock and see if the device is available 	*/retry:	spin_lock_bh (&this->chip_lock);	if (this->state == FL_READY) {		this->state = new_state;		spin_unlock_bh (&this->chip_lock);		return;	}	set_current_state (TASK_UNINTERRUPTIBLE);	add_wait_queue (&this->wq, &wait);

⌨️ 快捷键说明

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