📄 nand.c
字号:
/* * vivi/drivers/mtd/nand/nand.c: * * Based on linux/drivers/mtd/nand/nand.c * * $Id: smc_core.c,v 1.3 2002/08/10 07:47:08 nandy Exp $ * * * 棱淬: * 恐 this->oobblock捞扼绊 沁阑鳖? thsi->oobblcok_ofs捞扼绊 窍搁 * 粱歹 疙犬窍瘤 臼阑鳖? */#include "config.h"#include "mtd/mtd.h"#include "machine.h"#include "mtd/nand.h"#include "mtd/nand_ids.h"#include "mtd/nand_ecc.h"#include "time.h"#include "printk.h"#include "heap.h"#include "io.h"#include <types.h>#include <errno.h>static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel);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 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: ; }}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++) this->write_byte(mtd,buf[i]);}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] = this->read_byte(mtd);}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] != this->read_byte(mtd)) return i; return 0;}/* Appropriate chip should already be selected */static int nand_block_bad(struct mtd_info *mtd, unsigned long page){ struct nand_chip *this = mtd->priv; this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); if (this->read_byte(mtd) != 0xff) return 1; return 0;}/* * 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; /* Begin command latch cycle */ this->hwcontrol(mtd, NAND_CTL_SETCLE); this->hwcontrol(mtd,NAND_CTL_DAT_OUT); /* * 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_cmd(mtd, readcmd); }// this->write_byte(mtd, command); this->write_cmd(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); this->write_addr(mtd, column); if (page_addr != -1) {// this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); this->write_addr(mtd, (unsigned char) (page_addr & 0xff));// this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); this->write_addr(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); /* One more address cycle for higher density devices */ if (mtd->size & 0x0c000000) // this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); this->write_addr(mtd, (unsigned char) ((page_addr >> 16) & 0xff)); } /* Latch in address */ this->hwcontrol(mtd, NAND_CTL_CLRALE); this->hwcontrol(mtd,NAND_CTL_DAT_IN); } /* * 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->write_cmd(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 */ while (!this->dev_ready(mtd)){ udelay (this->chip_delay); return; } } /* wait until command is processed */ while (!this->dev_ready(mtd)) udelay (this->chip_delay);}static int nand_wait(struct mtd_info *mtd,struct nand_chip *this,int state){ int i; int status; for(i=0;i<32;i++){ udelay(125); this->cmdfunc(mtd,NAND_CMD_STATUS,-1,-1); if(this->dev_ready){ if(this->dev_ready(mtd)) break; } if(this->read_byte(mtd)&0x40) break; } status=(int)this->read_byte(mtd); 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, int col, int last, 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; /* 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); this->read_buf(mtd,&this->data_poi[0],col); } if ((col < this->eccsize) && (last >= this->eccsize)) { this->calculate_ecc (mtd,&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 (mtd,&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 (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");// this->write_buf(mtd, this->data_poi, mtd->oobblock);// break; case NAND_ECC_SOFT:// this->calculate_ecc(mtd, &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(mtd, &this->data_poi[256], &(ecc_code[3]));// for (i = 3; i < 6; i++)// oob_data[oob_config[i]] = ecc_code[i];// } this->write_buf(mtd, this->data_poi, mtd->oobblock); 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(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write */ this->write_buf(mtd, this->data_poi, mtd->eccsize); this->calculate_ecc(mtd, 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(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize); this->calculate_ecc(mtd, 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(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ this->write_buf(mtd, this->data_poi, mtd->oobblock); this->calculate_ecc(mtd, 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(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ this->write_buf(mtd, this->data_poi, mtd->oobblock); this->calculate_ecc(mtd, NULL, &(ecc_code[0])); for (i = 0; i < 6; i++) oob_data[oob_config[i]] = ecc_code[i]; break; default: printk ("Invalid NAND_ECC_MODE %d\n", this->eccmode); return -1; } /* Write out OOB data */ this->write_buf(mtd, oob_data, mtd->oobsize); /* Send command to actually program the data */ this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); /* Send command to actually program the data */ status=this->waitfunc (mtd, this,FL_WRITING); if(status & 0x01){// DEBUG(MTD_DEBUG_LEVEL0,"%S:" "Failed write,page 0x%08x,"__FUNCTION__,page); printk("%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, 0, page); /* Loop through and verify the data */ if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); printk("%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } /* check, if we have a fs-supplied oob-buffer */ if (oob_buf) { if (this->verify_buf(mtd, oob_data, mtd->oobsize)) {// DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); printk("%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); return -EIO; } } else { if (eccmode != NAND_ECC_NONE) { int ecc_bytes = 0; switch (this->eccmode) { case NAND_ECC_SOFT: case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break; case NAND_ECC_HW3_512: ecc_bytes = 3; break; case NAND_ECC_HW6_512: ecc_bytes = 6; break; } this->read_buf(mtd, oob_data, mtd->oobsize); for (i = 0; i < ecc_bytes; i++) { if (oob_data[oob_config[i]] != ecc_code[i]) {// DEBUG (MTD_DEBUG_LEVEL0, printk( "%s: Failed ECC write " "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); return -EIO; } } } }#endif return 0;}/** Use NAND read ECC*/static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf){ return nand_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);} /* * 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 * oob_buf, struct nand_oobinfo *oobsel){ int j, col, page, end, ecc;// int erase_state = 0; int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; u_char *data_poi, *oob_data = oob_buf; u_char ecc_calc[6]; u_char ecc_code[6]; int eccmode; int *oob_config; // use chip default if zero if (oobsel == NULL) oobsel = &mtd->oobinfo; eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; oob_config = oobsel->eccpos; 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"); printk("nand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Select the NAND device */ this->select_chip(mtd, 0); /* First we calculate the starting page */ page = from >> this->page_shift; /* Get raw starting column */ col = from & (mtd->oobblock - 1); end = mtd->oobblock; ecc = mtd->eccsize; /* Send the read command */ this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); /* Loop until all data read */ while (read < len) { /* If we have consequent page reads, apply delay or wait for ready/busy pin */ if (read) { if (!this->chip_delay) udelay (this->chip_delay); else while (!this->dev_ready(mtd)); } /* * If the read is not page aligned, we have to read into data buffer * due to ecc, else we read into return buffer direct */ if (!col && (len - read) >= end) data_poi = &buf[read]; else data_poi = this->data_buf; /* get oob area, if we have no oob buffer from fs-driver */ if (!oob_buf) { oob_data = &this->data_buf[end]; oob = 0; } j = 0; switch (eccmode) { case NAND_ECC_NONE: /* No ECC, Read in a page */ printk ( "Reading data from NAND FLASH without ECC is not recommended\n"); this->read_buf(mtd, data_poi, end); break; case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ this->read_buf(mtd, data_poi, end); this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); if (mtd->oobblock == 512) this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); break; case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, data_poi, ecc); this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, &data_poi[ecc], end-ecc); this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */ } break; case NAND_ECC_HW3_512: case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */ this->enable_hwecc(mtd, NAND_ECC_READ); this->read_buf(mtd, data_poi, end); this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ break; default: printk ( "Invalid NAND_ECC_MODE %d\n", this->eccmode); return -EIO; } /* read oobdata */ for (j = 0; j < mtd->oobsize; j++) oob_data[oob + j] = this->read_byte(mtd); /* Skip ECC, if not active */ if (eccmode == NAND_ECC_NONE) goto readdata; /* Pick the ECC bytes out of the oob data */ for (j = 0; j < 6; j++) ecc_code[j] = oob_data[oob + oob_config[j]]; /* correct data, if neccecary */ ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]); /* check, if we have a fs supplied oob-buffer */ if (oob_buf) { oob += mtd->oobsize; *((int *)&oob_data[oob]) = ecc_status; oob += sizeof(int); } if (ecc_status == -1) { // DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); printk("nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); ecc_failed++; } if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) { ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]); if (oob_buf) { *((int *)&oob_data[oob]) = ecc_status; oob += sizeof(int); }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -