最近调试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 0xad
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_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的支持 生成镜像后就可以下载启动了.
============================================
作者:hclydao
http://blog.youkuaiyun.com/hclydao
版权没有,但是转载请保留此段声明
============================================
参考资料:
http://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html