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

📄 nand.c

📁 内核linux2.4.20,可跟rtlinux3.2打补丁 组成实时linux系统,编译内核
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *  drivers/mtd/nand.c * *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) * * $Id: nand.c,v 1.12 2001/10/02 15:05:14 dwmw2 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. * *  Overview: *   This is the generic MTD driver for NAND flash devices. It should be *   capable of working with almost all NAND chips currently available. */#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_ids.h>#include <linux/interrupt.h>#include <asm/io.h>#ifdef CONFIG_MTD_NAND_ECC#include <linux/mtd/nand_ecc.h>#endif/* * Macros for low-level register control */#define NAND_CTRL (*(volatile unsigned char *) \			((struct nand_chip *) mtd->priv)->CTRL_ADDR)#define nand_select()	NAND_CTRL &= ~this->NCE; \			nand_command(mtd, NAND_CMD_RESET, -1, -1); \			udelay (10);#define nand_deselect() NAND_CTRL |= ~this->NCE;/* * NAND low-level MTD interface functions */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 *ecc_code);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 *ecc_code);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_erase (struct mtd_info *mtd, struct erase_info *instr);static void nand_sync (struct mtd_info *mtd);/* * 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;	register unsigned long NAND_IO_ADDR = this->IO_ADDR;	/* Begin command latch cycle */	NAND_CTRL |= this->CLE;	/*	 * Write out the command to the device.	 */	if (command != NAND_CMD_SEQIN)			writeb (command, NAND_IO_ADDR);	else {		if (mtd->oobblock == 256 && column >= 256) {			column -= 256;			writeb(NAND_CMD_RESET, NAND_IO_ADDR);			writeb(NAND_CMD_READOOB, NAND_IO_ADDR);			writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);		}		else if (mtd->oobblock == 512 && column >= 256) {			if (column < 512) {				column -= 256;				writeb(NAND_CMD_READ1, NAND_IO_ADDR);				writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);			}			else {				column -= 512;				writeb(NAND_CMD_READOOB, NAND_IO_ADDR);				writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);			}		}		else {			writeb(NAND_CMD_READ0, NAND_IO_ADDR);			writeb(NAND_CMD_SEQIN, NAND_IO_ADDR);		}	}	/* Set ALE and clear CLE to start address cycle */	NAND_CTRL &= ~this->CLE;	NAND_CTRL |= this->ALE;	/* Serially input address */	if (column != -1)		writeb (column, NAND_IO_ADDR);	if (page_addr != -1) {		writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);		writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);		/* One more address cycle for higher density devices */		if (mtd->size & 0x0c000000) {			writeb ((unsigned char) ((page_addr >> 16) & 0x0f),					NAND_IO_ADDR);		}	}	/* Latch in address */	NAND_CTRL &= ~this->ALE;	/* Pause for 15us */	udelay (15);}/* * NAND read */static int nand_read (struct mtd_info *mtd, loff_t from, size_t len,			size_t *retlen, u_char *buf){#ifdef CONFIG_MTD_NAND_ECC	struct nand_chip *this = mtd->priv;		return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);#else	return nand_read_ecc (mtd, from, len, retlen, buf, NULL);#endif}/* * NAND read with ECC */static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,				size_t *retlen, u_char *buf, u_char *ecc_code){	int j, col, page, state;	int erase_state = 0;	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE(wait, current);#ifdef CONFIG_MTD_NAND_ECC	int ecc_result;	u_char ecc_calc[6];#endif	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 */retry:	spin_lock_bh (&this->chip_lock);	switch (this->state) {	case FL_READY:		this->state = FL_READING;		spin_unlock_bh (&this->chip_lock);		break;	case FL_ERASING:		this->state = FL_READING;		erase_state = 1;		spin_unlock_bh (&this->chip_lock);		break;	default:		set_current_state (TASK_UNINTERRUPTIBLE);		add_wait_queue (&this->wq, &wait);		spin_unlock_bh (&this->chip_lock);		schedule();		remove_wait_queue (&this->wq, &wait);		goto retry;	};	/* First we calculate the starting page */	page = from >> this->page_shift;	/* Get raw starting column */	col = from & (mtd->oobblock - 1);	/* State machine for devices having pages larger than 256 bytes */	state = (col < mtd->eccsize) ? 0 : 1;	/* Calculate column address within ECC block context */	col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col;	/* Initialize return value */	*retlen = 0;	/* Select the NAND device */	nand_select ();	/* Loop until all data read */	while (*retlen < len) {#ifdef CONFIG_MTD_NAND_ECC		/* Send the read command */		if (!state)			nand_command (mtd, NAND_CMD_READ0, 0x00, page);		else 			nand_command (mtd, NAND_CMD_READ1, 0x00, page);		/* Read in a block big enough for ECC */		for (j=0 ; j < mtd->eccsize ; j++)			this->data_buf[j] = readb (this->IO_ADDR);		/* Read in the out-of-band data */		if (!state) {			nand_command (mtd, NAND_CMD_READOOB, 0x00, page);			for (j=0 ; j<3 ; j++)				ecc_code[j] = readb(this->IO_ADDR);			nand_command (mtd, NAND_CMD_READ0, 0x00, page);		}		else {			nand_command (mtd, NAND_CMD_READOOB, 0x03, page);			for (j=3 ; j<6 ; j++)				ecc_code[j] = readb(this->IO_ADDR);			nand_command (mtd, NAND_CMD_READ0, 0x00, page);		}		/* Calculate the ECC and verify it */		if (!state) {			nand_calculate_ecc (&this->data_buf[0],						&ecc_calc[0]);			ecc_result = nand_correct_data (&this->data_buf[0],						&ecc_code[0], &ecc_calc[0]);		}		else {			nand_calculate_ecc (&this->data_buf[0],						&ecc_calc[3]);			ecc_result = nand_correct_data (&this->data_buf[0],						&ecc_code[3], &ecc_calc[3]);		}		if (ecc_result == -1) {			DEBUG (MTD_DEBUG_LEVEL0,				"nand_read_ecc: " \				"Failed ECC read, page 0x%08x\n", page);			nand_deselect ();			spin_lock_bh (&this->chip_lock);			if (erase_state)				this->state = FL_ERASING;			else				this->state = FL_READY;			wake_up (&this->wq);			spin_unlock_bh (&this->chip_lock);			return -EIO;		}		/* Read the data from ECC data buffer into return buffer */		if ((*retlen + (mtd->eccsize - col)) >= len) {			while (*retlen < len)				buf[(*retlen)++] = this->data_buf[col++];			/* We're done */			continue;		}		else			for (j=col ; j < mtd->eccsize ; j++)				buf[(*retlen)++] = this->data_buf[j];#else		/* Send the read command */		if (!state)			nand_command (mtd, NAND_CMD_READ0, col, page);		else 			nand_command (mtd, NAND_CMD_READ1, col, page);		/* Read the data directly into the return buffer */ 		if ((*retlen + (mtd->eccsize - col)) >= len) {			while (*retlen < len)				buf[(*retlen)++] = readb (this->IO_ADDR);			/* We're done */			continue;		}		else			for (j=col ; j < mtd->eccsize ; j++)				buf[(*retlen)++] = readb (this->IO_ADDR);#endif		/*		 * If the amount of data to be read is greater than		 * (256 - col), then all subsequent reads will take		 * place on page or half-page (in the case of 512 byte		 * page devices) aligned boundaries and the column		 * address will be zero. Setting the column address to		 * to zero after the first read allows us to simplify		 * the reading of data and the if/else statements above.		 */		if (col)			col = 0x00;		/* Increment page address */		if ((mtd->oobblock == 256) || state)			page++;		/* Toggle state machine */		if (mtd->oobblock == 512)			state = state ? 0 : 1;	}	/* De-select the NAND device */	nand_deselect ();	/* Wake up anyone waiting on the device */	spin_lock_bh (&this->chip_lock);	if (erase_state)		this->state = FL_ERASING;	else		this->state = FL_READY;	wake_up (&this->wq);	spin_unlock_bh (&this->chip_lock);		/* Return happy */	return 0;}/* * NAND read out-of-band */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;	int erase_state = 0;	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE(wait, current);		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;	/* Mask to get column */	col = from & 0x0f;	/* Initialize return length value */	*retlen = 0;	/* Do not allow read past end of page */	if ((col + len) > mtd->oobsize) {		DEBUG (MTD_DEBUG_LEVEL0,			"nand_read_oob: Attempt read past end of page " \			"0x%08x, column %i, length %i\n", page, col, len);		return -EINVAL;	}retry:	/* Grab the lock and see if the device is available */	spin_lock_bh (&this->chip_lock);	switch (this->state) {	case FL_READY:		this->state = FL_READING;		spin_unlock_bh (&this->chip_lock);		break;	case FL_ERASING:		this->state = FL_READING;		erase_state = 1;		spin_unlock_bh (&this->chip_lock);		break;	default:		set_current_state (TASK_UNINTERRUPTIBLE);		add_wait_queue (&this->wq, &wait);		spin_unlock_bh (&this->chip_lock);		schedule();		remove_wait_queue (&this->wq, &wait);		goto retry;	};	/* Select the NAND device */	nand_select ();	/* Send the read command */	nand_command (mtd, NAND_CMD_READOOB, col, page);		/* Read the data */	for (i = 0 ; i < len ; i++)		buf[i] = readb (this->IO_ADDR);	/* De-select the NAND device */	nand_deselect ();	/* Wake up anyone waiting on the device */	spin_lock_bh (&this->chip_lock);	if (erase_state)		this->state = FL_ERASING;	else		this->state = FL_READY;	wake_up (&this->wq);	spin_unlock_bh (&this->chip_lock);	/* Return happy */	*retlen = len;	return 0;}/* * NAND write */static int nand_write (struct mtd_info *mtd, loff_t to, size_t len,			size_t *retlen, const u_char *buf){#ifdef CONFIG_MTD_NAND_ECC	struct nand_chip *this = mtd->priv;		return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf);#else	return nand_write_ecc (mtd, to, len, retlen, buf, NULL);#endif}/* * NAND write with ECC */static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,				size_t *retlen, const u_char *buf,				u_char *ecc_code){	int i, page, col, cnt, status;	struct nand_chip *this = mtd->priv;	DECLARE_WAITQUEUE(wait, current);#ifdef CONFIG_MTD_NAND_ECC	int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;#endif	DEBUG (MTD_DEBUG_LEVEL3,		"nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to,		(int) len);	/* Do not allow write past end of page */	if ((to + len) > mtd->size) {		DEBUG (MTD_DEBUG_LEVEL0,			"nand_write_ecc: Attempted write past end of device\n");		return -EINVAL;	}

⌨️ 快捷键说明

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