2013年(12)
分类: LINUX
2013-04-15 18:59:44
U-boot-2012-10的NAND驱动默认是采用了4位ECC 纠正,由于S3C6410对MLC nand也支持8位ECC,所以本次实验将移植8位ECC到OK6410板子上。
首先解释前面所说的OOB size=128的问题:
这块开发板使用的NAND型号是K9GAG08U0D,查看手册可以知道OOB SIZE是218字节。但我们前面移植时在配置NAND的信息后打印出来的OOB size =128。这显然不对,通过查看代码我们知道在nand_get_flash_type(drivers/mtd/nand/nand_base.c)函数中有这一段:
if (!type->pagesize) {
..........
extid >>= 2;//zxd extid=0x29
/* Calc oobsize */
switch (extid & 0x03) {
case 1:
mtd->oobsize = 128;
break;
case 2:
mtd->oobsize = 218;
break;
case 3:
mtd->oobsize = 400;
break;
default:
mtd->oobsize = 436;
break;
}
.........
刚开始我们采用uboot2012.10原来的NAND配置信息时,有提示oob size是218的,但我们配置信息后就没有了,显然要么是配置不对,要么是程序没有走到这段来,再看进入这段程序的条件是if (!type->pagesize) 也就是说当type->pagesize=0时才进入这段程序。type首先是指到nand_flash_ids这个结构体上,然后再通过
for (; type->name != NULL; type++)
if (*dev_id == type->id)
break;
找到对于的NAND信息。记得我们前面有对nand_flash_ids这个结构体做了修改:
##{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},##改为
{"NAND 2GiB 3,3V 8-bit", 0xD5, 4096, 2048, 512*1024, LP_OPTIONS},
那么就是说
type->pagesize=4096.当然就不符合if (!type->pagesize)这个条件了。把它改回来后打印:
SMDK6410 # nand info
Device 0: nand0, sector size 512 KiB
Page size 4096 b
OOB size 218 b
Erase size 524288 b
SMDK6410 #
可以看到已经对了。
OK,现在来讲一下8bit ECC的移植过程:
1.smdk6410.h(include/configs)增加
#define CONFIG_NAND_BL1_8BIT_ECC
再修改:
/* Size of the block protected by one OOB (Spare Area in Samsung terminology) */
#define CONFIG_SYS_NAND_ECCSIZE 512
/* Number of ECC bytes per OOB - S3C6400 calculates 4 bytes ECC in 1-bit mode */
#ifdef CONFIG_NAND_BL1_8BIT_ECC
#define CONFIG_SYS_NAND_ECCBYTES 13
#else
#define CONFIG_SYS_NAND_ECCBYTES 4
#endif
2.s3c64xx.c(drivers/mtd/nand/)增加这几个函数:
#if defined(CONFIG_NAND_BL1_8BIT_ECC) && (defined(CONFIG_S3C6410) || defined(CONFIG_S3C6430))
/***************************************************************
* jsgood: Temporary 8 Bit H/W ECC supports for BL1 (6410/6430 only)
***************************************************************/
int cur_ecc_mode=0;
/*
* Function for checking ECCEncDone in NFSTAT
* Written by jsgood
*/
static void s3c_nand_wait_enc(void)
{
while (!(readl(NFSTAT) & NFSTAT_ECCENCDONE)) {}
}
/*
* Function for checking ECCDecDone in NFSTAT
* Written by jsgood
*/
static void s3c_nand_wait_dec(void)
{
while (!(readl(NFSTAT) & NFSTAT_ECCDECDONE)) {}
}
static void s3c_nand_wait_ecc_busy_8bit(void)
{
while (readl(NF8ECCERR0) & NFESTAT0_ECCBUSY) {}
}
void s3c_nand_enable_hwecc_8bit(struct mtd_info *mtd, int mode)
{
u_long nfcont, nfconf;
cur_ecc_mode = mode;
/* 8 bit selection */
nfconf = readl(NFCONF);
nfconf &= ~(0x3 << 23);
nfconf |= (0x1 << 23);
writel(nfconf, NFCONF);
/* Initialize & unlock */
nfcont = readl(NFCONT);
nfcont |= NFCONT_INITECC;
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 s3c_nand_calculate_ecc_8bit(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
u_long nfcont, nfm8ecc0, nfm8ecc1, nfm8ecc2, nfm8ecc3;
/* Lock */
nfcont = readl(NFCONT);
nfcont |= NFCONT_MECCLOCK;
writel(nfcont, NFCONT);
if (cur_ecc_mode == NAND_ECC_READ)
s3c_nand_wait_dec();
else {
s3c_nand_wait_enc();
nfm8ecc0 = readl(NFM8ECC0);
nfm8ecc1 = readl(NFM8ECC1);
nfm8ecc2 = readl(NFM8ECC2);
nfm8ecc3 = readl(NFM8ECC3);
ecc_code[0] = nfm8ecc0 & 0xff;
ecc_code[1] = (nfm8ecc0 >> 8) & 0xff;
ecc_code[2] = (nfm8ecc0 >> 16) & 0xff;
ecc_code[3] = (nfm8ecc0 >> 24) & 0xff;
ecc_code[4] = nfm8ecc1 & 0xff;
ecc_code[5] = (nfm8ecc1 >> 8) & 0xff;
ecc_code[6] = (nfm8ecc1 >> 16) & 0xff;
ecc_code[7] = (nfm8ecc1 >> 24) & 0xff;
ecc_code[8] = nfm8ecc2 & 0xff;
ecc_code[9] = (nfm8ecc2 >> 8) & 0xff;
ecc_code[10] = (nfm8ecc2 >> 16) & 0xff;
ecc_code[11] = (nfm8ecc2 >> 24) & 0xff;
ecc_code[12] = nfm8ecc3 & 0xff;
}
return 0;
}
int s3c_nand_correct_data_8bit(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
int ret = -1;
u_long nf8eccerr0, nf8eccerr1, nf8eccerr2, nfmlc8bitpt0, nfmlc8bitpt1;
u_char err_type;
s3c_nand_wait_ecc_busy_8bit();
nf8eccerr0 = readl(NF8ECCERR0);
nf8eccerr1 = readl(NF8ECCERR1);
nf8eccerr2 = readl(NF8ECCERR2);
nfmlc8bitpt0 = readl(NFMLC8BITPT0);
nfmlc8bitpt1 = readl(NFMLC8BITPT1);
err_type = (nf8eccerr0 >> 25) & 0xf;
/* No error, If free page (all 0xff) */
if ((nf8eccerr0 >> 29) & 0x1)
err_type = 0;
switch (err_type)
{
case 8: /* 8 bit error (Correctable) */
dat[(nf8eccerr2 >> 22) & 0x3ff] ^= ((nfmlc8bitpt1 >> 24) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 7: /* 7 bit error (Correctable) */
dat[(nf8eccerr2 >> 11) & 0x3ff] ^= ((nfmlc8bitpt1 >> 16) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 6: /* 6 bit error (Correctable) */
dat[nf8eccerr2 & 0x3ff] ^= ((nfmlc8bitpt1 >> 8) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 5: /* 5 bit error (Correctable) */
dat[(nf8eccerr1 >> 22) & 0x3ff] ^= (nfmlc8bitpt1 & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 4: /* 4 bit error (Correctable) */
dat[(nf8eccerr1 >> 11) & 0x3ff] ^= ((nfmlc8bitpt0 >> 24) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 3: /* 3 bit error (Correctable) */
dat[nf8eccerr1 & 0x3ff] ^= ((nfmlc8bitpt0 >> 16) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 2: /* 2 bit error (Correctable) */
dat[(nf8eccerr0 >> 15) & 0x3ff] ^= ((nfmlc8bitpt0 >> 8) & 0xff);
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
case 1: /* 1 bit error (Correctable) */
printk("s3c-nand: %d bit(s) error detected, corrected successfully\n", err_type);
dat[nf8eccerr0 & 0x3ff] ^= (nfmlc8bitpt0 & 0xff);
ret = err_type;
break;
case 0: /* No error */
ret = 0;
break;
}
return ret;
}
void s3c_nand_write_page_8bit(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf)
{
int i, eccsize = 512;
int eccbytes = 13;
int eccsteps = mtd->writesize / eccsize;
uint8_t *ecc_calc = chip->buffers->ecccalc;
uint8_t *p = buf;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
s3c_nand_enable_hwecc_8bit(mtd, NAND_ECC_WRITE);
chip->write_buf(mtd, p, eccsize);
s3c_nand_calculate_ecc_8bit(mtd, p, &ecc_calc[i]);
}
for (i = 0; i < eccbytes * (mtd->writesize / eccsize); i++)
chip->oob_poi[i+24] = ecc_calc[i];
chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
}
int s3c_nand_read_page_8bit(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf)
{
int i, stat, eccsize = 512;
int eccbytes = 13;
int eccsteps = mtd->writesize / eccsize;
int col = 0;
uint8_t *p = buf;
/* 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);
s3c_nand_enable_hwecc_8bit(mtd, NAND_ECC_READ);
chip->read_buf(mtd, p, eccsize);
chip->write_buf(mtd, chip->oob_poi + 24 + (((mtd->writesize / eccsize) - eccsteps) * eccbytes), eccbytes);
s3c_nand_calculate_ecc_8bit(mtd, 0, 0);
stat = s3c_nand_correct_data_8bit(mtd, p, 0, 0);
if (stat == -1)
mtd->ecc_stats.failed++;
col = eccsize * ((mtd->writesize / eccsize) + 1 - eccsteps);
}
return 0;
}
/********************************************************/
#endif
然后在board_nand_init()函数中找到CONFIG_SYS_S3C_NAND_HWECC这个地方,改为:
#ifdef CONFIG_SYS_S3C_NAND_HWECC
#ifdef CONFIG_NAND_BL1_8BIT_ECC
printf("USE HWECC 8BIT\n");//zxd
nand->ecc.hwctl = s3c_nand_enable_hwecc_8bit;
nand->ecc.calculate = s3c_nand_calculate_ecc_8bit;
nand->ecc.correct = s3c_nand_correct_data_8bit;
nand->ecc.read_page = s3c_nand_read_page_8bit;
nand->ecc.write_page = s3c_nand_write_page_8bit;
#else
printf("USE HWECC Default\n");//zxd
nand->ecc.hwctl = s3c_nand_enable_hwecc;
nand->ecc.calculate = s3c_nand_calculate_ecc;
nand->ecc.correct = s3c_nand_correct_data;
#endif
/*
* If you get more than 1 NAND-chip with different page-sizes on the
* board one day, it will get more complicated...
*/
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
printf("ECC Size:%d ECC Bytes:%d\n",nand->ecc.size,nand->ecc.bytes);//zxd
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif /* ! CONFIG_SYS_S3C_NAND_HWECC */
就可以了。
最后再说一下把uboot写入NAND的问题,通过前面那篇NAND的文章可以知道,要想让uboot从NAND启动首先要注意写入NAND的方法:先写前面4个page,再写后面剩下的。下面是给nand 命令增加write.uboot的方法:
cmd_nand.c在do_nand函数中找到
if (strncmp(cmd, "read", 4) == 0 || strncmp(cmd, "write", 5) == 0) {
size_t rwsize;
ulong pagecount = 1;
int read;
int raw=0;//zxd 注意这里要先初始化赋值0.
........
增加部分在这里:
........
else if (raw) //zxd 注意raw初始化的问题
{
ret = raw_access(nand, addr, off, pagecount, read);
}
//zxd -->
else if (!strcmp(s, ".uboot") && !read)//zxd 增加write.uboot参数
{
rwsize=CONFIG_SYS_NAND_PAGE_SIZE;//page_size=4096 or 8192
for(i=0;i<4;i++)
{
ret = nand_write_skip_bad(nand, off, &rwsize, (u_char *)addr,0);
off+=CONFIG_SYS_NAND_PAGE_SIZE;
addr+=0x800; //2k
}
rwsize=size;
size-=CONFIG_SYS_NAND_PAGE_SIZE*4;
ret = nand_write_skip_bad(nand, off, &size, (u_char *)addr,0);
}
//zxd<--
else
{
printf("Unknown nand command suffix '%s'.\n", s);
return 1;
}
在U_BOOT_CMD(nand, CONFIG_SYS_MAXARGS, 1, do_nand中增加write.uboot的说明:
"nand write.uboot - addr off|partition size\n"
再结合前篇所提的读取NAND前面4page的方法,相信你的uboot就能够从NAND中启动了。
;