s5pv210 S34ML04G2 SLC nandflash驱动调试笔记

来源:互联网 发布:hitv广西广电网络电视 编辑:程序博客网 时间:2024/05/29 14:17

最近调试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_SPANSION0x01#define NAND_MFR_TOSHIBA0x98#define NAND_MFR_SAMSUNG0xec#define NAND_MFR_FUJITSU0x04#define NAND_MFR_NATIONAL0x8f#define NAND_MFR_RENESAS0x07#define NAND_MFR_STMICRO0x20#define NAND_MFR_HYNIX0xad
spansion的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;

就是对应的多少字节生成多少个字节的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 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_decs3c_nand_wait_enc

gzsd_correct_data_4bit是基于s3c_nand_correct_data进行的修改 这个改动不大

gzsd_read_page_4bit与gzsd_write_page_4bit实际上就是s3c_nand_read_page_4bits3c_nand_write_page_4bit

gzsd_read_oob_4bit与gzsd_write_oob_4bit是s3c_nand_read_oob_8bits3c_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 spansionmtd->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的支持 生成镜像后就可以下载启动了.

============================================
作者: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

0 0