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

📄 nand_base.c

📁 基于linux-2.6.28的mtd驱动
💻 C
📖 第 1 页 / 共 5 页
字号:
/* *  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. *   Basic support for AG-AND chips is provided. * *	Additional technical information is available on *	http://www.linux-mtd.infradead.org/doc/nand.html * *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) *		  2002-2006 Thomas Gleixner (tglx@linutronix.de) * *  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. *	The AG-AND chips have nice features for speed improvement, *	which are not supported yet. Read / program 4 pages in one go. *	BBT table is not serialized, has to be fixed * * 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/module.h>#include <linux/delay.h>#include <linux/errno.h>#include <linux/err.h>#include <linux/sched.h>#include <linux/slab.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 <linux/leds.h>#include <asm/io.h>#ifdef CONFIG_MTD_PARTITIONS#include <linux/mtd/partitions.h>#endif/* Define default oob placement schemes for large and small page devices */static struct nand_ecclayout nand_oob_8 = {	.eccbytes = 3,	.eccpos = {0, 1, 2},	.oobfree = {		{.offset = 3,		 .length = 2},		{.offset = 6,		 .length = 2}}};static struct nand_ecclayout nand_oob_16 = {	.eccbytes = 6,	.eccpos = {0, 1, 2, 3, 6, 7},	.oobfree = {		{.offset = 8,		 . length = 8}}};static struct nand_ecclayout nand_oob_64 = {	.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 = {		{.offset = 2,		 .length = 38}}};static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,			   int new_state);static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,			     struct mtd_oob_ops *ops);/* * For devices which display every fart in the system on a separate LED. Is * compiled away when LED support is disabled. */DEFINE_LED_TRIGGER(nand_led_trigger);/** * nand_release_device - [GENERIC] release chip * @mtd:	MTD device structure * * Deselect, release chip lock and wake up anyone waiting on the device */static void nand_release_device(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	/* De-select the NAND device */	chip->select_chip(mtd, -1);	/* Release the controller and the chip */	spin_lock(&chip->controller->lock);	chip->controller->active = NULL;	chip->state = FL_READY;	wake_up(&chip->controller->wq);	spin_unlock(&chip->controller->lock);}/** * nand_read_byte - [DEFAULT] read one byte from the chip * @mtd:	MTD device structure * * Default read function for 8bit buswith */static uint8_t nand_read_byte(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	return readb(chip->IO_ADDR_R);}/** * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip * @mtd:	MTD device structure * * Default read function for 16bit buswith with * endianess conversion */static uint8_t nand_read_byte16(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	return (uint8_t) cpu_to_le16(readw(chip->IO_ADDR_R));}/** * nand_read_word - [DEFAULT] read one word from the chip * @mtd:	MTD device structure * * Default read function for 16bit buswith without * endianess conversion */static u16 nand_read_word(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	return readw(chip->IO_ADDR_R);}/** * nand_select_chip - [DEFAULT] control CE line * @mtd:	MTD device structure * @chipnr:	chipnumber to select, -1 for deselect * * Default select function for 1 chip devices. */static void nand_select_chip(struct mtd_info *mtd, int chipnr){	struct nand_chip *chip = mtd->priv;	switch (chipnr) {	case -1:		chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);		break;	case 0:		break;	default:		BUG();	}}/** * nand_write_buf - [DEFAULT] write buffer to chip * @mtd:	MTD device structure * @buf:	data buffer * @len:	number of bytes to write * * Default write function for 8bit buswith */static void nand_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	for (i = 0; i < len; i++)		writeb(buf[i], chip->IO_ADDR_W);}/** * nand_read_buf - [DEFAULT] read chip data into buffer * @mtd:	MTD device structure * @buf:	buffer to store date * @len:	number of bytes to read * * Default read function for 8bit buswith */static void nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	for (i = 0; i < len; i++)		buf[i] = readb(chip->IO_ADDR_R);}/** * nand_verify_buf - [DEFAULT] Verify chip data against buffer * @mtd:	MTD device structure * @buf:	buffer containing the data to compare * @len:	number of bytes to compare * * Default verify function for 8bit buswith */static int nand_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	for (i = 0; i < len; i++)		if (buf[i] != readb(chip->IO_ADDR_R))			return -EFAULT;	return 0;}/** * nand_write_buf16 - [DEFAULT] write buffer to chip * @mtd:	MTD device structure * @buf:	data buffer * @len:	number of bytes to write * * Default write function for 16bit buswith */static void nand_write_buf16(struct mtd_info *mtd, const uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;	for (i = 0; i < len; i++)		writew(p[i], chip->IO_ADDR_W);}/** * nand_read_buf16 - [DEFAULT] read chip data into buffer * @mtd:	MTD device structure * @buf:	buffer to store date * @len:	number of bytes to read * * Default read function for 16bit buswith */static void nand_read_buf16(struct mtd_info *mtd, uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;	for (i = 0; i < len; i++)		p[i] = readw(chip->IO_ADDR_R);}/** * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer * @mtd:	MTD device structure * @buf:	buffer containing the data to compare * @len:	number of bytes to compare * * Default verify function for 16bit buswith */static int nand_verify_buf16(struct mtd_info *mtd, const uint8_t *buf, int len){	int i;	struct nand_chip *chip = mtd->priv;	u16 *p = (u16 *) buf;	len >>= 1;	for (i = 0; i < len; i++)		if (p[i] != readw(chip->IO_ADDR_R))			return -EFAULT;	return 0;}/** * nand_block_bad - [DEFAULT] Read bad block marker from the chip * @mtd:	MTD device structure * @ofs:	offset from device start * @getchip:	0, if the chip is already selected * * Check, if the block is bad. */static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip){	int page, chipnr, res = 0;	struct nand_chip *chip = mtd->priv;	u16 bad;	page = (int)(ofs >> chip->page_shift) & chip->pagemask;	if (getchip) {		chipnr = (int)(ofs >> chip->chip_shift);		nand_get_device(chip, mtd, FL_READING);		/* Select the NAND device */		chip->select_chip(mtd, chipnr);	}	if (chip->options & NAND_BUSWIDTH_16) {		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos & 0xFE,			      page);		bad = cpu_to_le16(chip->read_word(mtd));		if (chip->badblockpos & 0x1)			bad >>= 8;		if ((bad & 0xFF) != 0xff)			res = 1;	} else {		chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page);		if (chip->read_byte(mtd) != 0xff)			res = 1;	}	if (getchip)		nand_release_device(mtd);	return res;}/** * nand_default_block_markbad - [DEFAULT] mark a block bad * @mtd:	MTD device structure * @ofs:	offset from device start * * 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 *chip = mtd->priv;	uint8_t buf[2] = { 0, 0 };	int block, ret;	/* Get block number */	block = (int)(ofs >> chip->bbt_erase_shift);	if (chip->bbt)		chip->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);	/* Do we have a flash based bad block table ? */	if (chip->options & NAND_USE_FLASH_BBT)		ret = nand_update_bbt(mtd, ofs);	else {		/* We write two bytes, so we dont have to mess with 16 bit		 * access		 */		nand_get_device(chip, mtd, FL_WRITING);		ofs += mtd->oobsize;		chip->ops.len = chip->ops.ooblen = 2;		chip->ops.datbuf = NULL;		chip->ops.oobbuf = buf;		chip->ops.ooboffs = chip->badblockpos & ~0x01;		ret = nand_do_write_oob(mtd, ofs, &chip->ops);		nand_release_device(mtd);	}	if (!ret)		mtd->ecc_stats.badblocks++;	return ret;}/** * nand_check_wp - [GENERIC] check if the chip is write protected * @mtd:	MTD device structure * 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 *chip = mtd->priv;	/* Check the WP bit */	chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);	return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;}/** * nand_block_checkbad - [GENERIC] Check if a block is marked bad * @mtd:	MTD device structure * @ofs:	offset from device start * @getchip:	0, if the chip is already selected * @allowbbt:	1, if its allowed to access the bbt area * * Check, if the block is bad. Either by reading the bad block table or * calling of the scan function. */static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,			       int allowbbt){	struct nand_chip *chip = mtd->priv;	if (!chip->bbt)		return chip->block_bad(mtd, ofs, getchip);	/* Return info from the table */	return nand_isbad_bbt(mtd, ofs, allowbbt);}/* * Wait for the ready pin, after a command * The timeout is catched later. */void nand_wait_ready(struct mtd_info *mtd){	struct nand_chip *chip = mtd->priv;	unsigned long timeo = jiffies + 2;	led_trigger_event(nand_led_trigger, LED_FULL);	/* wait until command is processed or timeout occures */	do {		if (chip->dev_ready(mtd))			break;		touch_softlockup_watchdog();	} while (time_before(jiffies, timeo));	led_trigger_event(nand_led_trigger, LED_OFF);}EXPORT_SYMBOL_GPL(nand_wait_ready);/** * nand_command - [DEFAULT] Send command to NAND device * @mtd:	MTD device structure * @command:	the command to be sent * @column:	the column address for this command, -1 if none * @page_addr:	the page address for this command, -1 if none * * Send command to NAND device. This function is used for small page * devices (256/512 Bytes per page) */static void nand_command(struct mtd_info *mtd, unsigned int command,			 int column, int page_addr){	register struct nand_chip *chip = mtd->priv;	int ctrl = NAND_CTRL_CLE | NAND_CTRL_CHANGE;	/*	 * Write out the command to the device.	 */	if (command == NAND_CMD_SEQIN) {		int readcmd;		if (column >= mtd->writesize) {			/* OOB area */			column -= mtd->writesize;			readcmd = NAND_CMD_READOOB;		} else if (column < 256) {			/* First 256 bytes --> READ0 */			readcmd = NAND_CMD_READ0;		} else {			column -= 256;			readcmd = NAND_CMD_READ1;		}		chip->cmd_ctrl(mtd, readcmd, ctrl);		ctrl &= ~NAND_CTRL_CHANGE;	}	chip->cmd_ctrl(mtd, command, ctrl);	/*	 * Address cycle, when necessary	 */	ctrl = NAND_CTRL_ALE | NAND_CTRL_CHANGE;	/* Serially input address */	if (column != -1) {		/* Adjust columns for 16 bit buswidth */		if (chip->options & NAND_BUSWIDTH_16)			column >>= 1;		chip->cmd_ctrl(mtd, column, ctrl);		ctrl &= ~NAND_CTRL_CHANGE;	}	if (page_addr != -1) {		chip->cmd_ctrl(mtd, page_addr, ctrl);		ctrl &= ~NAND_CTRL_CHANGE;		chip->cmd_ctrl(mtd, page_addr >> 8, ctrl);		/* One more address cycle for devices > 32MiB */		if (chip->chipsize > (32 << 20))			chip->cmd_ctrl(mtd, page_addr >> 16, ctrl);	}	chip->cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);	/*	 * program and erase have their own busy handlers	 * status and sequential in needs no delay	 */	switch (command) {

⌨️ 快捷键说明

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