📄 nand_base.c
字号:
/* * 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/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 * * 05-19-2004 tglx: Basic support for Renesas AG-AND chips * * 09-24-2004 tglx: add support for hardware controllers (e.g. ECC) shared * among multiple independend devices. Suggestions and initial patch * from Ben Dooks <ben-mtd@fluff.org> * * 12-05-2004 dmarlin: add workaround for Renesas AG-AND chips "disturb" issue. * Basically, any block not rewritten may lose data when surrounding blocks * are rewritten many times. JFFS2 ensures this doesn't happen for blocks * it uses, but the Bad Block Table(s) may not be rewritten. To ensure they * do not lose data, force them to be rewritten when some of the surrounding * blocks are erased. Rather than tracking a specific nearby block (which * could itself go bad), use a page address 'mask' to select several blocks * in the same area, and rewrite the BBT when any of them are erased. * * 01-03-2005 dmarlin: added support for the device recovery command sequence for Renesas * AG-AND chips. If there was a sudden loss of power during an erase operation, * a "device recovery" operation must be performed when power is restored * to ensure correct operation. * * 01-20-2005 dmarlin: added support for optional hardware specific callback routine to * perform extra error status checks on erase and write failures. This required * adding a wrapper function for nand_read_ecc. * * 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. * * $Id: nand_base.c,v 1.147 2005/07/15 07:18:06 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/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 <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_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 kvec *vecs, unsigned long count, loff_t to, size_t * retlen);static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *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_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);/** * 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 *this = mtd->priv; /* De-select the NAND device */ this->select_chip(mtd, -1); if (this->controller) { /* Release the controller and the chip */ spin_lock(&this->controller->lock); this->controller->active = NULL; this->state = FL_READY; wake_up(&this->controller->wq); spin_unlock(&this->controller->lock); } else { /* Release the chip */ spin_lock(&this->chip_lock); this->state = FL_READY; wake_up(&this->wq); spin_unlock(&this->chip_lock); }}/** * nand_read_byte - [DEFAULT] read one byte from the chip * @mtd: MTD device structure * * Default read function for 8bit buswith */static u_char nand_read_byte(struct mtd_info *mtd){ struct nand_chip *this = mtd->priv; return readb(this->IO_ADDR_R);}/** * nand_write_byte - [DEFAULT] write one byte to the chip * @mtd: MTD device structure * @byte: pointer to data byte to write * * Default write function for 8it buswith */static void nand_write_byte(struct mtd_info *mtd, u_char byte){ struct nand_chip *this = mtd->priv; writeb(byte, this->IO_ADDR_W);}/** * 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 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));}/** * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip * @mtd: MTD device structure * @byte: pointer to data byte to write * * Default write function for 16bit buswith with * endianess conversion */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);}/** * 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 *this = mtd->priv; return readw(this->IO_ADDR_R);}/** * nand_write_word - [DEFAULT] write one word to the chip * @mtd: MTD device structure * @word: data word to write * * Default write function for 16bit buswith without * endianess conversion */static void nand_write_word(struct mtd_info *mtd, u16 word){ struct nand_chip *this = mtd->priv; writew(word, this->IO_ADDR_W);}/** * nand_select_chip - [DEFAULT] control CE line * @mtd: MTD device structure * @chip: chipnumber to select, -1 for deselect * * Default select function for 1 chip devices. */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(); }}/** * 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 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);}/** * 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, 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);}/** * 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 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;}/** * 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 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); }/** * 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, 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);}/** * 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 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;}/** * 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 *this = mtd->priv; u16 bad; if (getchip) { page = (int)(ofs >> this->page_shift); chipnr = (int)(ofs >> this->chip_shift); /* Grab the lock and see if the device is available */ nand_get_device (this, mtd, FL_READING); /* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -