UBOOT-2012-10在OK6410平臺的移植(十)MLC NAND驅動(硬件8位ECC)

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中啓動了。




















;

<script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/buttonLite.js#style=-1&uuid=&pophcol=3&lang=zh"></script> <script type=text/javascript charset=utf-8 src="http://static.bshare.cn/b/bshareC0.js"></script>
閱讀(7) | 評論(0) | 轉發(0) |
給主人留下些什麼吧!~~
評論熱議
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章