最近调试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, 0x00, 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;
然后增加如下这些函数
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 detected\n"); 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 successfully\n", 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, NAND_CMD_RNDOUT, col, -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, 0); stat = chip->ecc.correct(mtd, p, 0, 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, struct nand_chip *chip, 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--, i += eccbytes, p += eccsize) { chip->ecc.hwctl(mtd, NAND_ECC_WRITE); chip->write_buf(mtd, p, eccsize); chip->ecc.calculate(mtd, p, &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, chip->oob_poi, mtd->oobsize); } int gzsd_read_oob_4bit(struct mtd_info *mtd, struct nand_chip *chip, int page, int sndcmd) { int eccbytes = chip->ecc.bytes; int secc_start = mtd->oobsize - eccbytes; if (sndcmd) { chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); sndcmd = 0; } chip->ecc.hwctl(mtd, NAND_ECC_READ); chip->read_buf(mtd, chip->oob_poi, secc_start); return sndcmd; } int gzsd_write_oob_4bit(struct mtd_info *mtd, struct nand_chip *chip, 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, chip->oob_poi,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 hclydaou-boot基本上就只改动这些 然后就是内核里发动基本和u-boot一样 内核里只需要修改s3c_nand.c就可以了
然后就是文件系统制作下载一个mkyaffs2image工具源码加上对2k+128的支持 生成镜像后就可以下载启动了.
============================================
作者:hclydao
http://blog.csdn.net/hclydao
版权没有,但是转载请保留此段声明
============================================
参考资料:
http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html
http://blog.csdn.net/brantyou/article/details/8185547