s5pv210 S34ML04G2 SLC nandflash驱动调试笔记
最近调试S34ML04G2与之前的K9F4G08区别主要是oob和ecc校验位数不一样 发现直接用之前的驱动就可以启动 为了确保准确性还是来重新调试下驱动 S34ML04G2 页大小是2K+128B 4bit ecc/528B(512+16) K9F4G08页大小2K+64B 1bit ecc/528 这里需要注意的是528B是512+16B网上看的说这16B是oob现在还没搞清楚是什么情况 还有后面的4bit ecc不是说512B会产生4bit ecc这里不要搞混了 这个是与控制端对应的 210支持1bit 4bit 8,12,16bit ecc最后寄存器设置都是不一样的 这个4bit是指每512B可以纠正4bit的错误 下面我们结合代码整体来看下区别 首先是u-boot里主要涉及的文件有cpu/s5pc11x/nand.c drivers/mtd/nand/nand_base.cdrivers/mtd/nand/nand_util.c drivers/mtd/nand/nand_ids.c include/linux/mtd/nand.h 首先在include/linux/mtd/nand.h增加一个宏
#define NAND_MFR_SPANSION 0x01 #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_RENESAS 0x07 #define NAND_MFR_STMICRO 0x20 #define NAND_MFR_HYNIX 0xadspansion的id是1 然后是drivers/mtd/nand/nand_ids.c
struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_SPANSION,"Spansion"},{NAND_MFR_TOSHIBA,"Toshiba"},{NAND_MFR_SAMSUNG,"Samsung"},{NAND_MFR_FUJITSU,"Fujitsu"},{NAND_MFR_NATIONAL,"National"},{NAND_MFR_RENESAS,"Renesas"},{NAND_MFR_STMICRO,"ST Micro"},{NAND_MFR_HYNIX,"Hynix"},{0x0,"Unknown"} };增加spansion 在nand_flash_ids中由于samsung有spansion这两个芯片读出来的dev id都是0xdc所以里面就不用加了 然后就是cpu/s5pc11x/nand.c 增加
static struct nand_ecclayout gzsd_oob_128 = {//add by hclydao .useecc = MTD_NANDECC_AUTOPLACE,/* Only for U-Boot */ .eccbytes = 32,.eccpos = { 96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127},.oobfree = { {.offset = 2,.length = 94}} };这里对这个做一个简单的说明? 我在网上看到一些感觉说的不好理解 首先eccbytes为什么是32B 在210芯片手册中有说明 对于4bit ecc校验每512B会生成8个字节的ecc parity code其实文档上也没有明确的文字说明 210 datasheet相关说明如下 第3点 当你写512B后,校验值会在NFMECC0与NFMECC1里更新 而这两个寄存器里存的刚好就是8个字节(最后一个字节是保留的) 就是说每512B会生成8B校验码(这里我也设置成7B过 似乎是不行) 而一页是4x512B=2K就会生成4x8B=32B检验码 所以eccbytes是32B 而oobfree里的offset = 2这个一般是固定的 网上说oob前两个字节是用来设置标记坏块用的,length就是oob里可用的空间的长度 lenght之所以是94是因为我看代码里的这几个结构体ecc大部分都是保存在oob的最后空间 因为ecc是32B所以 存放ecc的eccpos就是从96开始到最后的这32个字节 那前面的2-95就是自由的空间这个自由空间的长度就是length(94 这个我应该没算错)这里所说的oob就是nandflash手册上说的spare size也有的手册上叫redundant area 对于S34ML04G2来说oob总大小就是128B void board_nand_init(struct nand_chip *nand) { #if defined(CFG_NAND_HWECC) ?? ?int i; ?? ?u_char tmp; ?? ?struct nand_flash_dev *type = NULL; #endif ?? ?int maf_id;//add by hclydao 20150707 ?? ?NFCONT_REG ?? ??? ?&= ~NFCONT_WP; ?? ?nand->IO_ADDR_R?? ??? ?= (void __iomem *)(NFDATA); ?? ?nand->IO_ADDR_W?? ??? ?= (void __iomem *)(NFDATA); ?? ?nand->cmd_ctrl?? ??? ?= s3c_nand_hwcontrol; ?? ?nand->dev_ready?? ??? ?= s3c_nand_device_ready; ?? ?nand->scan_bbt?? ??? ?= s3c_nand_scan_bbt; ?? ?nand->options?? ??? ?= 0; #if defined(CFG_NAND_FLASH_BBT) ?? ??? ?nand->options ?? ??? ?|= NAND_USE_FLASH_BBT; #else ?? ??? ?nand->options?? ??? ?|= NAND_SKIP_BBTSCAN; #endif #if defined(CFG_NAND_HWECC) ?? ?nand->ecc.mode?? ??? ?= NAND_ECC_HW; ?? ?nand->ecc.hwctl?? ??? ?= s3c_nand_enable_hwecc; ?? ?nand->ecc.calculate?? ?= s3c_nand_calculate_ecc; ?? ?nand->ecc.correct?? ?= s3c_nand_correct_data; ?? ?s3c_nand_hwcontrol(0,NAND_CMD_READID,NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); ?? ?s3c_nand_hwcontrol(0,0x00,NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE); ?? ?s3c_nand_hwcontrol(0,NAND_NCE | NAND_ALE); ?? ?s3c_nand_hwcontrol(0,NAND_CMD_NONE,NAND_NCE | NAND_CTRL_CHANGE); ?? ?s3c_nand_device_ready(0); ?? ?tmp = readb(nand->IO_ADDR_R); /* Maf. ID */ ?? ?maf_id = tmp; ?? ?tmp = readb(nand->IO_ADDR_R); /* Device ID */ ?? ?for (i = 0; nand_flash_ids[i].name != NULL; i++) { ?? ??? ?if (tmp == nand_flash_ids[i].id) { ?? ??? ??? ?type = &nand_flash_ids[i]; ?? ??? ??? ?break; ?? ??? ?} ?? ?} ?? ?nand->cellinfo = readb(nand->IO_ADDR_R);?? ?/* 3rd byte */ ?? ?tmp = readb(nand->IO_ADDR_R);?? ??? ??? ?/* 4th byte */ ?? ?if (!type->pagesize) { ?? ??? ?if (((nand->cellinfo >> 2) & 0x3) == 0) { ?? ??? ??? ?nand_type = S3C_NAND_TYPE_SLC; ?? ??? ??? ?nand->ecc.size = 512; ?? ??? ??? ?nand->ecc.bytes?? ?= 4; ?? ??? ??? ?if ((1024 << (tmp & 0x3)) > 512) { ?? ??? ??? ??? ?if(maf_id == NAND_MFR_SPANSION) { ?? ??? ??? ??? ??? ?nand->ecc.read_page = gzsd_read_page_4bit; ?? ??? ??? ??? ??? ?nand->ecc.write_page = gzsd_write_page_4bit; ?? ??? ??? ??? ??? ?nand->ecc.read_oob = gzsd_read_oob_4bit; ?? ??? ??? ??? ??? ?nand->ecc.write_oob = gzsd_write_oob_4bit; ?? ??? ??? ??? ??? ?nand->ecc.layout = &gzsd_oob_128; ?? ??? ??? ??? ??? ?nand->ecc.hwctl = gzsd_enable_hwecc_4bit; ?? ??? ??? ??? ??? ?nand->ecc.calculate = gzsd_calculate_ecc_4bit; ?? ??? ??? ??? ??? ?nand->ecc.correct = gzsd_correct_data_4bit; ?? ??? ??? ??? ??? ?nand->ecc.size = 512; ?? ??? ??? ??? ??? ?nand->ecc.bytes = 8; ?? ??? ??? ??? ??? ?nand->options |= NAND_NO_SUBPAGE_WRITE; ?? ??? ??? ??? ?} else { ?? ??? ??? ??? ??? ?nand->ecc.read_page = s3c_nand_read_page_1bit; ?? ??? ??? ??? ??? ?nand->ecc.write_page = s3c_nand_write_page_1bit; ?? ??? ??? ??? ??? ?nand->ecc.read_oob = s3c_nand_read_oob_1bit; ?? ??? ??? ??? ??? ?nand->ecc.write_oob = s3c_nand_write_oob_1bit; ?? ??? ??? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_64; ?? ??? ??? ??? ??? ?nand->ecc.hwctl = s3c_nand_enable_hwecc; ?? ??? ??? ??? ??? ?nand->ecc.calculate = s3c_nand_calculate_ecc; ?? ??? ??? ??? ??? ?nand->ecc.correct = s3c_nand_correct_data; ?? ??? ??? ??? ??? ?nand->options |= NAND_NO_SUBPAGE_WRITE; ?? ??? ??? ??? ?} ?? ??? ??? ?} else { ?? ??? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_16; ?? ??? ??? ?} ?? ??? ?} else { ?? ??? ??? ?nand_type = S3C_NAND_TYPE_MLC; ?? ??? ??? ?nand->options |= NAND_NO_SUBPAGE_WRITE;?? ?/* NOP = 1 if MLC */ ?? ??? ??? ?nand->ecc.read_page = s3c_nand_read_page_4bit; ?? ??? ??? ?nand->ecc.write_page = s3c_nand_write_page_4bit; ?? ??? ??? ?nand->ecc.size = 512; ?? ??? ??? ?nand->ecc.bytes = 8;?? ?/* really 7 bytes */ ?? ??? ??? ?nand->ecc.layout = &s3c_nand_oob_mlc_64; ?? ??? ?} ?? ?} else { ?? ??? ?nand_type = S3C_NAND_TYPE_SLC; ?? ??? ?nand->ecc.size = 512; ?? ??? ?nand->cellinfo = 0; ?? ??? ?nand->ecc.bytes = 4; ?? ??? ?nand->ecc.layout = &s3c_nand_oob_16; ?? ?} #else ?? ?nand->ecc.mode = NAND_ECC_SOFT; #endif }这里的
nand->ecc.size = 512; nand->ecc.bytes = 8; 就是对应的多少字节生成多少个字节的ecc校验值 我们这里是每512字节生成8字节的ecc校验值. 然后增加如下这些函数
static void gzsd_wait_4bitenc(void) { while (!(readl(NFSTAT) & (1 << 7))) {} } static void gzsd_wait_4bitdec(void) { while (!(readl(NFSTAT) & (1 << 6))) {} } void gzsd_enable_hwecc_4bit(struct mtd_info *mtd,int mode) { u_long nfreg; u_long nfcont; cur_ecc_mode = mode; /* set msglenght 512byte and 4 bit selection */ nfreg = readl(NFCONF); nfreg &= ~(0x7 << 23); nfreg |= (0x2 << 23); writel(nfreg,NFCONF); /* Initialize & unlock */ nfcont = readl(NFCONT); nfcont |= NFCONT_INITMECC; nfcont &= ~NFCONT_MECCLOCK; if (mode == NAND_ECC_WRITE) nfcont |= NFCONT_ECC_ENC; else if (mode == NAND_ECC_READ) nfcont &= ~NFCONT_ECC_ENC; writel(nfcont,NFCONT); } int gzsd_calculate_ecc_4bit(struct mtd_info *mtd,const u_char *dat,u_char *ecc_code) { u_long nfcont,nfmecc0,nfmecc1; int i; /* Lock */ nfcont = readl(NFCONT); nfcont |= NFCONT_MECCLOCK; writel(nfcont,NFCONT); if (cur_ecc_mode == NAND_ECC_READ) { gzsd_wait_4bitdec(); }else { gzsd_wait_4bitenc(); nfmecc0 = readl(NFMECC0); nfmecc1 = readl(NFMECC1); ecc_code[0] = nfmecc0 & 0xff; ecc_code[1] = (nfmecc0 >> 8) & 0xff; ecc_code[2] = (nfmecc0 >> 16) & 0xff; ecc_code[3] = (nfmecc0 >> 24) & 0xff; ecc_code[4] = nfmecc1 & 0xff; ecc_code[5] = (nfmecc1 >> 8) & 0xff; ecc_code[6] = (nfmecc1 >> 16) & 0xff; ecc_code[7] = (nfmecc1 >> 24) & 0xff; } } int gzsd_correct_data_4bit(struct mtd_info *mtd,u_char *dat) { int ret = -1; u_long nfestat0,nfestat1,nfmeccdata0,nfmeccdata1,nfmlcbitpt; u_char err_type; s3c_nand_wait_ecc_busy(); nfestat0 = readl(NFESTAT0); nfestat1 = readl(NFESTAT1); nfmlcbitpt = readl(NFMLCBITPT); err_type = (nfestat0 >> 26) & 0x7; /* No error,If free page (all 0xff) */ if ((nfestat0 >> 29) & 0x1) { err_type = 0; } else { /* No error,If all 0xff from 17th byte in oob (in case of JFFS2 format) */ if (dat) { if (dat[17] == 0xff && dat[26] == 0xff && dat[35] == 0xff && dat[44] == 0xff && dat[54] == 0xff) err_type = 0; } } switch (err_type) { case 5: /* Uncorrectable */ printk("s3c-nand: ECC uncorrectable error detectedn"); ret = -1; break; case 4: /* 4 bit error (Correctable) */ dat[(nfestat1 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 24) & 0xff); case 3: /* 3 bit error (Correctable) */ dat[nfestat1 & 0x3ff] ^= ((nfmlcbitpt >> 16) & 0xff); case 2: /* 2 bit error (Correctable) */ dat[(nfestat0 >> 16) & 0x3ff] ^= ((nfmlcbitpt >> 8) & 0xff); case 1: /* 1 bit error (Correctable) */ printk("s3c-nand: %d bit(s) error detected,corrected successfullyn",err_type); dat[nfestat0 & 0x3ff] ^= (nfmlcbitpt & 0xff); ret = err_type; break; case 0: /* No error */ ret = 0; break; } return ret; } int gzsd_read_page_4bit(struct mtd_info *mtd,struct nand_chip *chip,uint8_t *buf) { int i,stat,eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; int col = 0; uint8_t *p = buf; uint32_t *mecc_pos = chip->ecc.layout->eccpos; /* Step1: read whole oob */ col = mtd->writesize; chip->cmdfunc(mtd,NAND_CMD_RNDOUT,col,-1); chip->read_buf(mtd,chip->oob_poi,mtd->oobsize); col = 0; for (i = 0; eccsteps; eccsteps--,i += eccbytes,p += eccsize) { chip->cmdfunc(mtd,-1); chip->ecc.hwctl(mtd,NAND_ECC_READ); chip->read_buf(mtd,p,eccsize); chip->write_buf(mtd,chip->oob_poi + mecc_pos[0] + ((chip->ecc.steps - eccsteps) * eccbytes),eccbytes); chip->ecc.calculate(mtd,0); stat = chip->ecc.correct(mtd,0); if (stat == -1) mtd->ecc_stats.failed++; col = eccsize * (chip->ecc.steps + 1 - eccsteps); } return 0; } void gzsd_write_page_4bit(struct mtd_info *mtd,const uint8_t *buf) { int i,eccsize = chip->ecc.size; int eccbytes = chip->ecc.bytes; int eccsteps = chip->ecc.steps; const uint8_t *p = buf; uint8_t *ecc_calc = chip->buffers->ecccalc; uint32_t *mecc_pos = chip->ecc.layout->eccpos; /* Step1: write main data and encode mecc */ for (i = 0; eccsteps; eccsteps--,p += eccsize) { chip->ecc.hwctl(mtd,NAND_ECC_WRITE); chip->write_buf(mtd,eccsize); chip->ecc.calculate(mtd,&ecc_calc[i]); } /* Step2: save encoded mecc */ for (i = 0; i < chip->ecc.total; i++) chip->oob_poi[mecc_pos[i]] = ecc_calc[i]; chip->write_buf(mtd,mtd->oobsize); } int gzsd_read_oob_4bit(struct mtd_info *mtd,int page,int sndcmd) { int eccbytes = chip->ecc.bytes; int secc_start = mtd->oobsize - eccbytes; if (sndcmd) { chip->cmdfunc(mtd,NAND_CMD_READOOB,page); sndcmd = 0; } chip->ecc.hwctl(mtd,NAND_ECC_READ); chip->read_buf(mtd,secc_start); return sndcmd; } int gzsd_write_oob_4bit(struct mtd_info *mtd,int page) { int status = 0; int eccbytes = chip->ecc.bytes; int secc_start = mtd->oobsize - eccbytes; chip->cmdfunc(mtd,NAND_CMD_SEQIN,mtd->writesize,page); /* spare area */ chip->ecc.hwctl(mtd,NAND_ECC_WRITE); chip->write_buf(mtd,secc_start); /* Send command to program the OOB data */ chip->cmdfunc(mtd,NAND_CMD_PAGEPROG,-1,-1); status = chip->waitfunc(mtd,chip); return status & NAND_STATUS_FAIL ? -EIO : 0; }上面大部分函数都是在原有的函数的基础上copy来的 gzsd_wait_4bitenc与gzsd_wait_4bitdec是我自己加上的 不能使用之前的s3c_nand_wait_dec和s3c_nand_wait_enc这两个函数是给MLC类型的用的 其中需要注意的是210手册里有很多寄存器的位是以MLC开头的不要弄混的 这些位有的也适用于SLC gzsd_enable_hwecc_4bit是基于s3c_nand_enable_hwecc进行的修改 主要设置为4bit ecc模式以及4bit ecc模式编解码的设置还有一些流程在手册上有说明 gzsd_calculate_ecc_4bit是基于s3c_nand_calculate_ecc_1bit基础上进行的修改 在原有的驱动里已经有了MLC 4bit ecc的驱动 所以这里改一下就可以用了 这里面主要就是不能使用s3c_nand_wait_dec与s3c_nand_wait_enc gzsd_correct_data_4bit是基于s3c_nand_correct_data进行的修改 这个改动不大 gzsd_read_page_4bit与gzsd_write_page_4bit实际上就是s3c_nand_read_page_4bit与s3c_nand_write_page_4bit gzsd_read_oob_4bit与gzsd_write_oob_4bit是s3c_nand_read_oob_8bit与s3c_nand_write_oob_8bit 很多操作基本上跟1bit与8bit的类似 所以直接可以抄过来 u-boot里实际上并没有用到read_oob与write_oob这两个函数 而read oob与write oob是在read_page和write_page里完成的 接着是drivers/mtd/nand/nand_base.c里函数nand_get_flash_type 加上
/* Calc oobsize */ if(maf_id == NAND_MFR_SPANSION) {//add by hclydao 20150707 for spansion mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 8); } else { mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); }oobsize改为128字节 默认的是64 接着是drivers/mtd/nand/nand_util.c里 在有meminfo->oobsize与meminfo->writesize的判断里加上自己的
&& !(meminfo->oobsize == 128 && meminfo->writesize == 2048)//add by hclydao?u-boot基本上就只改动这些 然后就是内核里发动基本和u-boot一样 内核里只需要修改s3c_nand.c就可以了 然后就是文件系统制作下载一个mkyaffs2image工具源码加上对2k+128的支持 生成镜像后就可以下载启动了.
============================================ ============================================ 参考资料: http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html http://www.voidcn.com/article/p-zwbwumid-x.html (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |