?? nand.c
字號(hào):
/* * 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) * * 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-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for * WRITE_VERIFY long time ago. Thanks for remembering me. * * 02-14-2003 tglx: Reject non page aligned writes * Fixed ecc select in nand_write_page to match semantics. * * 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector * * 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to + a structure, which will be supplied by a filesystem driver * If NULL is given, then the defaults (none or defaults * supplied by ioctl (MEMSETOOBSEL) are used. * For partitions the partition defaults are used (mtdpart.c) * * $Id: nand.c,v 1.43 2003/04/14 07:00:39 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/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);/* * 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, 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 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, struct nand_oobinfo *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, u_char *oob_buf, struct nand_oobinfo *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); NF_CMD(command); } else { if (mtd->oobblock == 256 && column >= 256) { column -= 256;// writeb (NAND_CMD_READOOB, NAND_IO_ADDR);// writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); NF_CMD(NAND_CMD_READOOB); NF_CMD(NAND_CMD_SEQIN); } 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); NF_CMD(NAND_CMD_READ1); NF_CMD(NAND_CMD_SEQIN); } else { column -= 512;// writeb (NAND_CMD_READOOB, NAND_IO_ADDR);// writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); NF_CMD(NAND_CMD_READOOB); NF_CMD(NAND_CMD_SEQIN); } } else {// writeb (NAND_CMD_READ0, NAND_IO_ADDR);// writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); NF_CMD(NAND_CMD_READ0); NF_CMD(NAND_CMD_SEQIN); } } /* 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); NF_ADDR (column); if (page_addr != -1) {// writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);// writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); NF_ADDR ((unsigned char) (page_addr & 0xff)); NF_ADDR ((unsigned char) ((page_addr >> 8) & 0xff)); /* One more address cycle for higher density devices */ if (mtd->size & 0x0c000000) // writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); NF_ADDR ((unsigned char) ((page_addr >> 16) & 0x0f)); } /* 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); NF_ADDR (NAND_CMD_STATUS); this->hwcontrol (NAND_CTL_CLRCLE);// while ( !(readb (this->IO_ADDR_R) & 0x40)); while ( !(NF_RDDATA() & 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) if (NF_RDDATA() & 0x40) break; spin_unlock_bh (&this->chip_lock); yield (); spin_lock_bh (&this->chip_lock); }// status = (int) readb (this->IO_ADDR_R); status = (int) NF_RDDATA(); spin_unlock_bh (&this->chip_lock); return status;}/* * Nand_page_program function is used for write and writev ! * This function will always program a full page of data * If you call it with a non page aligned buffer, you're lost :) */static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel){ int i, status; u_char ecc_code[6], *oob_data; int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; int *oob_config = oobsel->eccpos; if(eccmode == NAND_ECC_NONE) DEBUG (MTD_DEBUG_LEVEL3,"nand_write_page: eccmode == NAND_ECC_NONE\n"); /* 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; /* 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 (eccmode) { /* No ecc and software ecc 3/256, write all */ case NAND_ECC_NONE: printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); for (i = 0; i < mtd->oobblock; i++) // writeb ( this->data_poi[i] , this->IO_ADDR_W); NF_WRDATA(this->data_poi[i]); break; case NAND_ECC_SOFT: 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 for 512 Byte page size */ if (mtd->oobblock == 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]; } for (i = 0; i < mtd->oobblock; i++) // writeb ( this->data_poi[i] , this->IO_ADDR_W); NF_WRDATA(this->data_poi[i]); 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); NF_WRDATA(this->data_poi[i]); 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); NF_WRDATA(this->data_poi[i]); 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); NF_WRDATA(this->data_poi[i]); 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); NF_WRDATA(this->data_poi[i]); 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); NF_WRDATA(oob_data[i]); /* 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 0 write, page 0x%08x, ", __FUNCTION__, page); return -EIO;
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -