📄 yynf_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@cotw.com) * 2002 Thomas Gleixner (tglx@linutronix.de) * * 10-29-2001 Thomas Gleixner (tglx@linutronix.de) * - Changed nand_chip structure for controlline function to * support different hardware structures (Access to * controllines ALE,CLE,NCE via hardware specific function. * - exit out of "failed erase block" changed, to avoid * driver hangup * - init_waitqueue_head added in function nand_scan !! * * 01-30-2002 Thomas Gleixner (tglx@linutronix.de) * change in nand_writev to block invalid vecs entries * * 02-11-2002 Thomas Gleixner (tglx@linutronix.de) * - major rewrite to avoid duplicated code * common nand_write_page function * common get_chip function * - added oob_config structure for out of band layouts * - write_oob changed for partial programming * - read cache for faster access for subsequent reads * from the same page. * - support for different read/write address * - support for device ready/busy line * - read oob for more than one page enabled * * 02-27-2002 Thomas Gleixner (tglx@linutronix.de) * - command-delay can be programmed * - fixed exit from erase with callback-function enabled * * 03-21-2002 Thomas Gleixner (tglx@linutronix.de) * - DEBUG improvements provided by Elizabeth Clarke * (eclarke@aminocom.com) * - added zero check for this->chip_delay * * 04-03-2002 Thomas Gleixner (tglx@linutronix.de) * - added added hw-driver supplied command and wait functions * - changed blocking for erase (erase suspend enabled) * - check pointers before accessing flash provided by * John Hall (john.hall@optionexist.co.uk) * * 04-09-2002 Thomas Gleixner (tglx@linutronix.de) * - nand_wait repaired * * 04-28-2002 Thomas Gleixner (tglx@linutronix.de) * - OOB config defines moved to nand.h * * 08-01-2002 Thomas Gleixner (tglx@linutronix.de) * - changed my mailaddress, added pointer to tech/nand.html * * 08-07-2002 Thomas Gleixner (tglx@linutronix.de) * forced bad block location to byte 5 of OOB, even if * CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent * erase /dev/mtdX from erasing bad blocks and destroying * bad block info * * 08-10-2002 Thomas Gleixner (tglx@linutronix.de) * Fixed writing tail of data. Thanks to Alice Hennessy * <ahennessy@mvista.com>. * * 08-10-2002 Thomas Gleixner (tglx@linutronix.de) * nand_read_ecc and nand_write_page restructured to support * hardware ECC. Thanks to Steven Hein (ssh@sgi.com) * for basic implementation and suggestions. * 3 new pointers in nand_chip structure: * calculate_ecc, correct_data, enabled_hwecc * forcing all hw-drivers to support page cache * eccvalid_pos is now mandatory * * 08-17-2002 tglx: fixed signed/unsigned missmatch in write.c * Thanks to Ken Offer <koffer@arlut.utexas.edu> * * 08-29-2002 tglx: use buffered read/write only for non pagealigned * access, speed up the aligned path by using the fs-buffer * reset chip removed from nand_select(), implicit done * only, when erase is interrupted * waitfuntion use yield, instead of schedule_timeout * support for 6byte/512byte hardware ECC * read_ecc, write_ecc extended for different oob-layout * selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB, * NAND_YAFFS_OOB. fs-driver gives one of these constants * to select the oob-layout fitting the filesystem. * oobdata can be read together with the raw data, when * the fs-driver supplies a big enough buffer. * size = 12 * number of pages to read (256B pagesize) * 24 * number of pages to read (512B pagesize) * the buffer contains 8/16 byte oobdata and 4/8 byte * returncode from calculate_ecc * oobdata can be given from filesystem to program them * in one go together with the raw data. ECC codes are * filled in at the place selected by oobsel. * * 09-04-2002 tglx: fixed write_verify (John Hall (john.hall@optionexist.co.uk)) * * 11-11-2002 tglx: fixed debug output in nand_write_page * (John Hall (john.hall@optionexist.co.uk)) * * 11-25-2002 tglx: Moved device ID/ manufacturer ID from nand_ids.h * Splitted device ID and manufacturer ID table. * Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for * mtd->read / mtd->write and is controllable by the fs driver * for mtd->read_ecc / mtd->write_ecc * some minor cleanups * * 12-05-2000 tglx: Dave Ellis (DGE@sixnetio) provided the fix for * WRITE_VERIFY long time ago. Thanks for remembering me. * * $Id: nand.c,v 1.36 2002/12/05 20:59:11 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/partitions.h>#include <linux/interrupt.h>#include <asm/io.h>/* * Macros for low-level register control */#define nand_select() this->hwcontrol(NAND_CTL_SETNCE);#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);/* * out of band configuration for different filesystems */static int oobconfigs[][6] = { { 0,0,0,0,0,0}, { NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2, NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 }, { NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2, NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 } };/* * 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 * eccbuf, int 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, int 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, int oobsel);static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);static void nand_sync (struct mtd_info *mtd);static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col, int last, u_char *oob_buf, int oobsel);/* * 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_W; /* Begin command latch cycle */ this->hwcontrol (NAND_CTL_SETCLE); /* * 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_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 */ this->hwcontrol (NAND_CTL_CLRCLE); if (column != -1 || page_addr != -1) { this->hwcontrol (NAND_CTL_SETALE); /* 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 */ this->hwcontrol (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; this->hwcontrol (NAND_CTL_SETCLE); writeb (NAND_CMD_STATUS, NAND_IO_ADDR); this->hwcontrol (NAND_CTL_CLRCLE); while ( !(readb (this->IO_ADDR_R) & 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());}/* * Get chip for selected access */static inline 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 * For erasing, we keep the spinlock until the * erase command is written. */retry: spin_lock_bh (&this->chip_lock); if (this->state == FL_READY) { this->state = new_state; if (new_state != FL_ERASING) spin_unlock_bh (&this->chip_lock); return; } if (this->state == FL_ERASING) { if (new_state != FL_ERASING) { this->state = new_state; spin_unlock_bh (&this->chip_lock); nand_select (); /* select in any case */ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); return; } } 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;}/* * Wait for command done. This applies to erase and program only * Erase can take up to 400ms and program up to 20ms according to * general NAND and SmartMedia specs **/static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state){ unsigned long timeo = jiffies; int status; if (state == FL_ERASING) timeo += (HZ * 400) / 1000; else timeo += (HZ * 20) / 1000; spin_lock_bh (&this->chip_lock); this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); while (time_before(jiffies, timeo)) { /* Check, if we were interrupted */ if (this->state != state) { spin_unlock_bh (&this->chip_lock); return 0; } if (this->dev_ready) { if (this->dev_ready ()) break; } if (readb (this->IO_ADDR_R) & 0x40) break; spin_unlock_bh (&this->chip_lock); yield (); spin_lock_bh (&this->chip_lock); } status = (int) readb (this->IO_ADDR_R); spin_unlock_bh (&this->chip_lock); return status;}/* * Nand_page_program function is used for write and writev ! */static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col, int last, u_char *oob_buf, int oobsel){ int i, status; u_char ecc_code[6], *oob_data; int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobconfigs[oobsel]; /* pad oob area, if we have no oob buffer from fs-driver */ if (!oob_buf) { oob_data = &this->data_buf[mtd->oobblock]; for (i = 0; i < mtd->oobsize; i++) oob_data[i] = 0xff; } else oob_data = oob_buf; /* software ecc 3 Bytes ECC / 256 Byte Data ? */ if (eccmode == NAND_ECC_SOFT) { /* Read back previous written data, if col > 0 */ if (col) { this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); for (i = 0; i < col; i++) this->data_poi[i] = readb (this->IO_ADDR_R); } if ((col < this->eccsize) && (last >= this->eccsize)) { this->calculate_ecc (&this->data_poi[0], &(ecc_code[0])); for (i = 0; i < 3; i++) oob_data[oob_config[i]] = ecc_code[i]; } /* Calculate and write the second ECC if we have enough data */ if ((mtd->oobblock == 512) && (last == 512)) { this->calculate_ecc (&this->data_poi[256], &(ecc_code[3])); for (i = 3; i < 6; i++) oob_data[oob_config[i]] = ecc_code[i]; } } else { /* For hardware ECC skip ECC, if we have no full page write */ if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock)) eccmode = NAND_ECC_NONE; } /* Prepad for partial page programming !!! */ for (i = 0; i < col; i++) this->data_poi[i] = 0xff; /* Postpad for partial page programming !!! oob is already padded */ for (i = last; i < mtd->oobblock; i++) this->data_poi[i] = 0xff; /* Send command to begin auto page programming */ this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); /* Write out complete page of data, take care of eccmode */ switch (this->eccmode) { /* No ecc and software ecc 3/256, write all */ case NAND_ECC_NONE: case NAND_ECC_SOFT: for (i = 0; i < mtd->oobblock; i++) writeb ( this->data_poi[i] , this->IO_ADDR_W); break; /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */ case NAND_ECC_HW3_256: this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */ for (i = 0; i < mtd->eccsize; i++) writeb ( this->data_poi[i] , this->IO_ADDR_W); this->calculate_ecc (NULL, &(ecc_code[0])); for (i = 0; i < 3; i++) oob_data[oob_config[i]] = ecc_code[i]; if (mtd->oobblock == 512) { this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ for (i = mtd->eccsize; i < mtd->oobblock; i++) writeb ( this->data_poi[i] , this->IO_ADDR_W); this->calculate_ecc (NULL, &(ecc_code[3])); for (i = 3; i < 6; i++) oob_data[oob_config[i]] = ecc_code[i]; } break; /* Hardware ecc 3 byte / 512 byte data, write full page */ case NAND_ECC_HW3_512: this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ for (i = 0; i < mtd->oobblock; i++) writeb ( this->data_poi[i] , this->IO_ADDR_W); this->calculate_ecc (NULL, &(ecc_code[0])); for (i = 0; i < 3; i++) oob_data[oob_config[i]] = ecc_code[i]; break; /* Hardware ecc 6 byte / 512 byte data, write full page */ case NAND_ECC_HW6_512: this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ for (i = 0; i < mtd->oobblock; i++) writeb ( this->data_poi[i] , this->IO_ADDR_W); this->calculate_ecc (NULL, &(ecc_code[0])); for (i = 0; i < 6; i++) oob_data[oob_config[i]] = ecc_code[i]; break; default: printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); BUG(); } /* Write out OOB data */ for (i = 0; i < mtd->oobsize; i++) writeb ( oob_data[i] , this->IO_ADDR_W); /* Send command to actually program the data */ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); /* call wait ready function */ status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); return -EIO; }#ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* * The NAND device assumes that it is always writing to * a cleanly erased page. Hence, it performs its internal * write verification only on bits that transitioned from * 1 to 0. The device does NOT verify the whole page on a * byte by byte basis. It is possible that the page was * not completely erased or the page is becoming unusable * due to wear. The read with ECC would catch the error * later when the ECC page check fails, but we would rather * catch it early in the page write stage. Better to write * no data than invalid data. */ /* Send command to read back the page */ this->cmdfunc (mtd, NAND_CMD_READ0, col, page); /* Loop through and verify the data */ for (i = col; i < last; i++) { if (this->data_poi[i] != readb (this->IO_ADDR_R)) { DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -