📄 nand.c
字号:
/* * drivers/mtd/nand.c * * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) * * 10-29-2001 Thomas Gleixner (gleixner@autronix.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 (gleixner@autronix.de) * change in nand_writev to block invalid vecs entries * * 02-11-2002 Thomas Gleixner (gleixner@autronix.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 (gleixner@autronix.de) * - command-delay can be programmed * - fixed exit from erase with callback-function enabled * * $Id: nand.c,v 1.1.1.1 2004/02/04 12:56:25 laputa 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. * * Overview: * This is the generic MTD driver for NAND flash devices. It should be * capable of working with almost all NAND chips currently available. */#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_ids.h>#include <linux/interrupt.h>#include <asm/io.h>#ifdef CONFIG_MTD_NAND_ECC#include <linux/mtd/nand_ecc.h>#endif/* * Macros for low-level register control */#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); \ nand_command(mtd, NAND_CMD_RESET, -1, -1); \ udelay (this->chip_delay);#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);/* * Definition of the out of band configuration structure */struct nand_oob_config { int ecc_pos[6]; /* position of ECC bytes inside oob */ int badblock_pos; /* position of bad block flag inside oob -1 = inactive */ int eccvalid_pos; /* position of ECC valid flag inside oob -1 = inactive */};/** Constants for oob configuration*/#define NAND_NOOB_ECCPOS0 0#define NAND_NOOB_ECCPOS1 1#define NAND_NOOB_ECCPOS2 2#define NAND_NOOB_ECCPOS3 3#define NAND_NOOB_ECCPOS4 4#define NAND_NOOB_ECCPOS5 5#define NAND_NOOB_BADBPOS -1#define NAND_NOOB_ECCVPOS -1#define NAND_JFFS2_OOB_ECCPOS0 0#define NAND_JFFS2_OOB_ECCPOS1 1#define NAND_JFFS2_OOB_ECCPOS2 2#define NAND_JFFS2_OOB_ECCPOS3 3#define NAND_JFFS2_OOB_ECCPOS4 6#define NAND_JFFS2_OOB_ECCPOS5 7#define NAND_JFFS2_OOB_BADBPOS 5#define NAND_JFFS2_OOB_ECCVPOS 4static struct nand_oob_config oob_config;/* * 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 *ecc_code);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 *ecc_code);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_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 *ecc_code);/* * Send command to NAND device */static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr){ read_write_clock(1); 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_RESET, NAND_IO_ADDR); 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 */ if (column != -1 || page_addr != -1) this->hwcontrol(NAND_CTL_CLRALE); /* Pause for 15us */ udelay (this->chip_delay);}/* * Get chip for selected access */static inline void nand_get_chip(struct nand_chip *this,int new_state, int *erase_state) { DECLARE_WAITQUEUE(wait, current); /* Grab the lock and see if the device is available */retry: spin_lock_bh (&this->chip_lock); switch (this->state) { case FL_READY: this->state = new_state; if (new_state != FL_ERASING) spin_unlock_bh (&this->chip_lock); break; case FL_ERASING: if (new_state == FL_READING) { this->state = FL_READING; *erase_state = 1; spin_unlock_bh (&this->chip_lock); break; } default: 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; };}/* * 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 *ecc_code) { int i, status;#ifdef CONFIG_MTD_NAND_ECC int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;#endif /* pad oob area */ for(i=mtd->oobblock;i < mtd->oobblock+mtd->oobsize; i++) this->data_buf[i] = 0xff;#ifdef CONFIG_MTD_NAND_ECC /* Zero out the ECC array */ for (i=0 ; i < 6 ; i++) ecc_code[i] = 0x00; /* Read back previous written data, if col > 0 */ if(col) { nand_command (mtd, NAND_CMD_READ0, col, page); for (i=0 ; i < col ; i++) this->data_buf[i] = readb (this->IO_ADDR_R); } /* Calculate and write the ECC if we have enough data */ if ((col < mtd->eccsize) && (last >= mtd->eccsize)) { nand_calculate_ecc (&this->data_buf[0], &(ecc_code[0])); for (i=0 ; i<3 ; i++) this->data_buf[(mtd->oobblock + oob_config.ecc_pos[i])] = ecc_code[i]; if (oob_config.eccvalid_pos != -1) this->data_buf[mtd->oobblock + oob_config.eccvalid_pos] = 0xf0; } /* Calculate and write the second ECC if we have enough data */ if ((mtd->oobblock == 512) && (last == mtd->oobblock)) { nand_calculate_ecc (&this->data_buf[256],&(ecc_code[3])); for (i=3 ; i<6 ; i++) this->data_buf[(mtd->oobblock + oob_config.ecc_pos[i])] = ecc_code[i]; if (oob_config.eccvalid_pos != -1) this->data_buf[mtd->oobblock + oob_config.eccvalid_pos] &= 0x0f; }#endif /* Prepad for partial page programming !!! */ for (i=0 ; i < col ; i++) this->data_buf[i] = 0xff; /* Postpad for partial page programming !!! oob is already padded */ for (i=last ; i < mtd->oobblock ; i++) this->data_buf[i] = 0xff; /* Send command to begin auto page programming */ nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); /* Write out complete page of data */ for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) writeb (this->data_buf[i], this->IO_ADDR_W); /* Send command to actually program the data */ nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); /* * Wait for program operation to complete. This could * take up to 3000us (3ms) on some devices, so we try * and exit as quickly as possible. */ status = 0; for (i=0 ; i<24 ; i++) { /* Delay for 125us */ udelay (125); /* Check the status */ if (this->dev_ready) if (!this->dev_ready()) continue; nand_command (mtd, NAND_CMD_STATUS, -1, -1); status = (int) readb (this->IO_ADDR_R); if (status & 0x40) break; } /* See if device thinks it succeeded */ if (status & 0x01) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " \ "Failed write, page 0x%08x, " \ "%6i bytes were succesful\n", page, *retlen); 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 */ if (col < mtd->eccsize) nand_command (mtd, NAND_CMD_READ0, col, page); else nand_command (mtd, NAND_CMD_READ1, col - 256, page); /* Loop through and verify the data */ for (i=col ; i < last ; i++) { if (this->data_buf[i] != readb (this->IO_ADDR_R)) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: " \ "Failed write verify, page 0x%08x, " \ "%6i bytes were succesful\n", page, *retlen); return -EIO; } }#ifdef CONFIG_MTD_NAND_ECC /* * We also want to check that the ECC bytes wrote * correctly for the same reasons stated above. */ nand_command (mtd, NAND_CMD_READOOB, 0x00, page); for (i=0 ; i < mtd->oobsize ; i++) this->data_buf[i] = readb(this->IO_ADDR_R); for (i=0 ; i < ecc_bytes ; i++) { if ( (this->data_buf[(oob_config.ecc_pos[i])] != ecc_code[i]) && ecc_code[i]) { DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Failed ECC write " \ "verify, page 0x%08x, " \ "%6i bytes were succesful\n", page, i); return -EIO; } }#endif#endif return 0;}/* * NAND read */static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf){#ifdef CONFIG_MTD_NAND_ECC struct nand_chip *this = mtd->priv; return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf);#else return nand_read_ecc (mtd, from, len, retlen, buf, NULL);#endif}/* * NAND read with ECC */static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *ecc_code){ int j, col, page; int erase_state = 0; int ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; u_char *data_poi;#ifdef CONFIG_MTD_NAND_ECC u_char ecc_calc[6];#endif DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ nand_get_chip(this,FL_READING,&erase_state); /* First we calculate the starting page */ page = from >> this->page_shift; /* Get raw starting column */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -