📄 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 * * 03-21-2002 Thomas Gleixner (gleixner@autronix.de) * - DEBUG improvements provided by Elizabeth Clarke * (eclarke@aminocom.com) * - added zero check for this->chip_delay * * 04-03-2002 Thomas Gleixner (gleixner@autronix.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 (gleixner@autronix.de) * - nand_wait repaired * * 04-28-2002 Thomas Gleixner (gleixner@autronix.de) * - OOB config defines moved to nand.h * * $Id: nand.c,v 1.24 2002/04/28 13:39:48 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. * * 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); \ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);#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 */};static 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){ 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); /* * 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; } /* program and erase have their own busy handlers */ switch (command) { case NAND_CMD_PAGEPROG: case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: 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, 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); 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){ DECLARE_WAITQUEUE (wait, current); 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); 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; } this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); if (readb (this->IO_ADDR_R) & 0x40) break; set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); schedule_timeout (1); remove_wait_queue (&this->wq, &wait); spin_lock_bh (&this->chip_lock); } this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); 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 * ecc_code){ int i, status;#ifdef CONFIG_MTD_NAND_VERIFY_WRITE#ifdef CONFIG_MTD_NAND_ECC int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3;#endif#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) { this->cmdfunc (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 */ this->cmdfunc (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 */ 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, "nand_write_ecc: " "Failed write, page 0x%08x, ", 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 */ if (col < mtd->eccsize) this->cmdfunc (mtd, NAND_CMD_READ0, col, page); else this->cmdfunc (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 ", page); return -EIO; } }#ifdef CONFIG_MTD_NAND_ECC /* * We also want to check that the ECC bytes wrote * correctly for the same reasons stated above. */ this->cmdfunc (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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -