nand flash粗析(二)------NAND flash 读写
1、坏块检测All device locations are erased(FFh) except locations where the initial invalid block(s) information is written prior to shipping. The 上图为数据手册中,检测有效块的流程图。它的大意是解释第一段英文的意思,一个block中前两页中的第517个字节是0xFF的话,这一个块就是有效的,否则这个块就是坏块。 在linux源码中快速检测有效块的代码(2.6.19) /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer * @buf: the buffer to search * @td: search pattern descriptor * * Check for a pattern at the given place. Used to search bad block * tables and good / bad block identifiers. Same as check_pattern,but * no optional empty check * */ static int check_short_pattern(uint8_t *buf,struct nand_bbt_descr *td) { int i; uint8_t *p = buf; /* Compare the pattern */ for (i = 0; i < td->len; i++) { if (p[td->offs + i] != td->pattern[i]) return -1; } return 0; } 以K9F1208U0M为例,上述代码中的td是这样的 static struct nand_bbt_descr smallpage_memorybased = { .options = NAND_BBT_SCAN2NDPAGE,.offs = 5,//刚好第517个字节,用来检测block是否invalid .len = 1,.pattern = scan_ff_pattern };结合datasheet中的描述,offs刚好为第517个字节,scan_ff_pattern是值为oxff的数组,也符合datasheet中的描述 2、写操作写操作的流程图。 代码: /** * nand_write_page - [REPLACEABLE] write one page * @mtd: MTD device structure * @chip: NAND chip descriptor * @buf: the data to write * @page: page number to write * @cached: cached programming * @raw: use _raw version of write_page */ static int nand_write_page(struct mtd_info *mtd,struct nand_chip *chip,const uint8_t *buf,int page,int cached,int raw) { int status; chip->cmdfunc(mtd,NAND_CMD_SEQIN,0x00,page); //0x80,发送命令 if (unlikely(raw)) chip->ecc.write_page_raw(mtd,chip,buf); else chip->ecc.write_page(mtd,buf); //数据写入 /* * Cached progamming disabled for now,Not sure if its worth the * trouble. The speed gain is not very impressive. (2.3->2.6Mib/s) */ cached = 0; if (!cached || !(chip->options & NAND_CACHEPRG)) { chip->cmdfunc(mtd,NAND_CMD_PAGEPROG,-1,-1); //0x10 status = chip->waitfunc(mtd,chip); //s3c2440_nand_devready,等NFSTAT第0位空闲 //返回值为状态值,由flash芯片发送的 /* * See if operation failed and additional status checks are * available */ if ((status & NAND_STATUS_FAIL) && (chip->errstat)) status = chip->errstat(mtd,FL_WRITING,status,page); if (status & NAND_STATUS_FAIL) return -EIO; } else { chip->cmdfunc(mtd,NAND_CMD_CACHEDPROG,-1); //0x15 status = chip->waitfunc(mtd,chip); } #ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ chip->cmdfunc(mtd,NAND_CMD_READ0,page); if (chip->verify_buf(mtd,buf,mtd->writesize)) return -EIO; #endif return 0; } 在chip->cmdfunc(nand_command)中已经写入了地址。 在chip->waitfunc中完成读状态寄存器,确保nand busy free,再判断是否是NAND_STATUS_FAIL(也就是I/O 0==0?) /** * nand_write_page_hwecc - [REPLACABLE] hardware ecc based page write function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: data buffer */ static void nand_write_page_hwecc(struct mtd_info *mtd,const uint8_t *buf) { int i,eccsize = chip->ecc.size; //hardware的话是512 int eccbytes = chip->ecc.bytes; // 3 int eccsteps = chip->ecc.steps; uint8_t *ecc_calc = chip->buffers->ecccalc; const uint8_t *p = buf; int *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--,i += eccbytes,p += eccsize) { chip->ecc.hwctl(mtd,NAND_ECC_WRITE); //Initialize ECC decoder/encoder chip->write_buf(mtd,p,eccsize); //buf写入 chip->ecc.calculate(mtd,&ecc_calc[i]);//计算buf的ecc } for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[eccpos[i]] = ecc_calc[i]; chip->write_buf(mtd,chip->oob_poi,mtd->oobsize); //ecc写入 } chip->write_buf static void s3c2440_nand_write_buf(struct mtd_info *mtd,const u_char *buf,int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); writesl(info->regs + S3C2440_NFDATA,len / 4); //2440的数据寄存器 NAND_LED_STATUS = ~ NAND_LED_STATUS; s3c2410_gpio_setpin(S3C2410_GPF4,NAND_LED_STATUS); //bank选择 } 3、读操作:代码: nand_do_read_ops中先发送0x00,再调用ret = chip->ecc.read_page(mtd,bufpoi);,这个函数可以是nand_read_page_hwecc,//将数据读取到bufopi里,且如果有错误ecc可矫正,两位以上ecc没法矫正 /** * nand_read_page_hwecc - [REPLACABLE] hardware ecc based page read function * @mtd: mtd info structure * @chip: nand chip info structure * @buf: buffer to store read data * * Not for syndrome calculating ecc controllers which need a special oob layout */ static int nand_read_page_hwecc(struct mtd_info *mtd,uint8_t *buf) { int i,eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint8_t *ecc_code = chip->buffers->ecccode; int *eccpos = chip->ecc.layout->eccpos; for (i = 0; eccsteps; eccsteps--,NAND_ECC_READ); chip->read_buf(mtd,eccsize); //将数据读出来 chip->ecc.calculate(mtd,&ecc_calc[i]); //根据读取数据,生成ecc } chip->read_buf(mtd,mtd->oobsize); //读出oob for (i = 0; i < chip->ecc.total; i++) ecc_code[i] = chip->oob_poi[eccpos[i]]; //硬件ecc eccsteps = chip->ecc.steps; p = buf; for (i = 0 ; eccsteps; eccsteps--,p += eccsize) { int stat; stat = chip->ecc.correct(mtd,&ecc_code[i],&ecc_calc[i]); //ecc校验 if (stat == -1) mtd->ecc_stats.failed++; //-1表明是坏块 else //0 or 1 mtd->ecc_stats.corrected += stat; //如果是1的话,表明是纠错过的,更新 } return 0; } chip->read_buf static void s3c2410_nand_read_buf(struct mtd_info *mtd,u_char *buf,int len) { struct nand_chip *this = mtd->priv; readsb(this->IO_ADDR_R,len); //2440的数据寄存器 NAND_LED_STATUS = ~ NAND_LED_STATUS; s3c2410_gpio_setpin(S3C2410_GPF4,NAND_LED_STATUS); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |