📄 nand.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. * * 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 * Read aligned to ECC boundaries if we dont read page aligned * (May speed things up alot) * Check if buffering the last not aligned read can speed up * performance * * $Id: nand.c,v 1.3 2004/05/24 01:48:08 nandy 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 <config.h>#include <errno.h>#include <types.h>#include <mtd/mtd.h>#include <mtd/nand.h>#include <mtd/nand_ecc.h>#include <io.h>#include <time.h>#include <vstring.h>#include <bitops.h>#define min(a, b) ((a) > (b)) ? (b) : (a)/* 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_erase (struct mtd_info *mtd, struct erase_info *instr);/* 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)#endifstatic int forced_nand_erase = 0; /* don't erase a block if it's bad */void enable_bad_block_erase(void){ printk("Try to erase a bad block\n"); forced_nand_erase = 1;}void disable_bad_block_erase(void){ printk("Don't try to erase a bad block\n"); forced_nand_erase = 0;}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 */ /* Nothing to do on the vivi */}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(); break; }}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 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -