Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150740
  • 博文数量: 25
  • 博客积分: 165
  • 博客等级: 入伍新兵
  • 技术积分: 460
  • 用 户 组: 普通用户
  • 注册时间: 2012-07-15 19:24
文章分类

全部博文(25)

文章存档

2013年(21)

2012年(4)

我的朋友

分类:

2012-12-10 15:37:48

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中启动了。




















;

阅读(1366) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~