在修改ver3.1的时候,只是扫了一眼源码,看到支持yaffs写命令,就想当然的认为“大页NAND出来这么多年了,uboot应该支持yaffs2的烧写了”。由于时间问题,当时也没有测试这个功能。这几天仔细看了源码,假象啊。ver3.1还是不支持yaffs2的烧写的。只支持yaffs(小页nand)的烧写。在此郑重的向被我“忽悠”的网友道歉。 也许现在烧写yaffs2的问题早已经被朋友们解决了,毕竟过去这么长时间了,对于有u-boot移植经验的朋友,这个小bug 1天应该就可以搞定。 现在写这个bug的解决方案,对大牛们来说可能没什么价值了,但是希望能给后来的朋友提供点移植思路。好了,废话不多说,进入正题。
ver4.0源码下载:u-boot for tiny210 ver4.0
下面的链接提供了历史版本的源码
ver3.1源码下载: u-boot for tiny210 ver3.1
ver3.0源码下载:u-boot for tiny 210 ver3.0
ver2.2源码下载: u-boot for tiny210 ver2.2
ver2.1源码下载:u-boot for tiny210 ver2.2
ver2.1源码下载:u-boot for tiny210 ver2.1
ver2.0源码下载:u-boot for tiny210 ver2.0
ver4.0的基本功能:
1. SD boot,基于linaro u-boot的SPL功能实现
2. 从SD卡的FAT分区上加载文件到SDRAM
3. 将环境变量保存至SD卡
4. 添加DM9000网卡驱动,开启网络功能(例如:tftp,nfs等)
5. 添加TAB键命令自动补全功能
6.修复bug:
修复bug 1:SD卡保存环境变量出现Writing to MMC(0)... mmc_send_cmd: error during transfer: 0x00208001 mmc write failed。
修复bug 2:每次启动只能保存一次环境变量。
7.添加NandFlash驱动,开启所有Nand cmd。
8.添加Yaffs文件系统烧写支持。
9.修改在SD卡启动及nand启动时对nandflash的烧写为8bit 硬件ECC校验。
10.添加Nandflash启动。
+12.添加yaffs2文件系统烧写支持。
+13.添加显示Nandflash烧写进度。
在正式修改u-boot前,希望大家能看一下这篇bloghttp://blog.csdn.net/liukun321/article/details/8558591。对yaffs2的原理有个大概了解。其实u-boot烧写yaffs2与往nand烧写普通数据区别在于:yaffs2的.img镜像中不仅包含了根文件系统的数据,而且还包含了oob区的数据。打开.img镜像截取第一块的oob数据内容如下图:
800h-830h就是镜像中第一块的oob数据(共64byte),u-boot烧写yaffs2时需要把这部分数据烧到nand的oob区。而800h开始的前两字节是用于标记坏块的,如果不是坏块则全为FF。从第三字节到第40个字节存放的是yaffs2每块的“标记数据”(这个词用的可能不恰当)。第41到第64字节存放的是yaffs2自己的ECC校验值。
这里还要说一下u-boot与友善的Superboot对烧写yaffs2的区别:Superboot烧写yaffs2是用的应该是4bit HW(硬件)ECC,那么nandflash第41到第64字节存放的数据就不在是yaffs2镜像的ECC值了,而是烧写过程中产生的4bit HW(硬件)ECC值--->对应在内核配置时要开启硬件ECC校验,关yaffs2自己的ECC校验。而u-boot烧写时是把yaffs2镜像文件里的oob数据烧到nandflash的oob区中,相当于用的是yaffs2自己的ECC校验,对应内核配置---->关硬件ECC,开启yaffs2自己的ECC(对于内核配置,我后面还会具体说明)。ver4.0 修改过程如下(会贴源码,在此声明一下,贴源码不是为了凑字数,blog不是写论文没有凑字数的必要,因为在贴源码的过程会穿插解释,只是为了更直观体现修改原理,这对“牛人”来说可能没什么用,“牛人”直接看源码就可以了。但是还有很多新手,新手需要更多的解释,希望各位“牛人”见谅):
拿到ver3.1的源码
1.在include/configs/tiny210.h 文件中对应行处添加下面红字部分:
412 #define CONFIG_CMD_NAND
413 #if defined(CONFIG_CMD_NAND)
414 #define CONFIG_CMD_NAND_YAFFS 1
415 #define CONFIG_CMD_NAND_YAFFS2 1
416 #define CONFIG_CMD_MTDPARTS
417 #define CONFIG_SYS_MAX_NAND_DEVICE 1
.............................
423 #define NF_TRANSRnB() do { while(!(NFSTAT_REG & (1 << 0))); } whil e(0)
—424 #define CONFIG_CMD_NAND_YAFFS_SKIPFB 1
425 #define CONFIG_NAND_USE_CHIP_NAME 1
426 #undef CFG_NAND_FLASH_BBT
427 #endif
由于在烧写时不需要跳过第一个good block ,所以去掉定义 #define CONFIG_CMD_NAND_YAFFS_SKIPFB 1
2.修改common/cmd_nand.c
592 WITH_DROP_FFS);
593 #endif
594 #if defined(CONFIG_CMD_NAND_YAFFS)&&(!defined(CONFIG_CMD_NAND_YAFFS2))
595 } else if (!strcmp(s, ".yaffs")) {
596 if (read) {
597 printf("Unknown nand command suffix '%s'.\n", s);
598 return 1;
599 }ret = nand_write_skip_bad(nand, off, &rwsize,
(u_char *)addr, WITH_YAFFS_OOB);
600 #endif
+601 #if defined(CONFIG_CMD_NAND_YAFFS)&&defined(CONFIG_CMD_NAND_YAFFS2)
+602 }else if ( s != NULL &&(!strcmp(s, ".yaffs") || !strcmp(s, ".yaffs1"))){
+603 if(read) {
+604 printf("nand read.yaffs[1] is not provide temporarily!");
+605 } else {
+606 nand->rw_oob = 1;
+607 #if defined(CONFIG_CMD_NAND_YAFFS_SKIPFB)
+608 nand->skipfirstblk = 1;
+609 #else
+610 nand->skipfirstblk = 0;
+611 #endif
+612 ret = nand_write_skip_bad(nand, off, &rwsize,
+613 (u_char *)addr, WITH_YAFFS_OOB);
+614
+615 #if defined(CONFIG_CMD_NAND_YAFFS_SKIPFB)
+616 nand->skipfirstblk = 0;
+617 #endif
+618 nand->rw_oob = 0;
+619 }
+620 #endif
621 } else if (!strcmp(s, ".oob")) {
622 /* out-of-band data */
623 mtd_oob_ops_t ops = {
nand->rw_oob = 0;,nand->skipfirstblk 是新定义的两个变量,分别作为烧写oob区标记变量和跳过第一个block的标记变量.nand_write_skip_bad(nand, off, &rwsize,(u_char *)addr, WITH_YAFFS_OOB); 函数执行时在烧写nand过程中会烧写oob区。
3. 修改drivers/mtd/nand/nand_util.c 文件的int nand_write_skip_bad 函数.使之支持烧写oob区
485 u_char *p_buffer = buffer;
486 int need_skip;
487
488 /*****************Modified by lk***********************/
+489 #if defined(CONFIG_CMD_NAND_YAFFS2)
+490 if(nand->rw_oob==1) {
+491 size_t oobsize = nand->oobsize;
+492 size_t datasize = nand->writesize;
+493 int datapages = 0;
+494
+495
+496 if (((*length)%(nand->oobsize+nand->writesize)) != 0) {
+497 printf ("Attempt to write error length data!\n");
+498 return -EINVAL;
+499 }
+500
+501 datapages = *length/(datasize+oobsize);
+502 *length = datapages*datasize;
+503 left_to_write = *length;
+504
+505 }
+506 else
+507 #else/*************************************************/
508 if (flags & WITH_YAFFS_OOB) {
509 if (flags & ~WITH_YAFFS_OOB)
510 return -EINVAL;
511
512 int pages;
513 pages = nand->erasesize / nand->writesize;
514 blocksize = (pages * nand->oobsize) + nand->erasesize;
515 if (*length % (nand->writesize + nand->oobsize)) {
516 printf ("Attempt to write incomplete page"
517 " in yaffs mode\n");
518 return -EINVAL;
519 }
520 } else
+521 #endif
522 {
523 blocksize = nand->erasesize;
524 }
.....................................
544 if (need_skip < 0) {
545 printf ("Attempt to write outside the flash area\n");
546 *length = 0;
547 return -EINVAL;
548 }
549 /********************Modified by lk**********************************/
+550 #if !defined(CONFIG_CMD_NAND_YAFFS2)
551 if (!need_skip && !(flags & WITH_DROP_FFS)) {
552 rval = nand_write (nand, offset, length, buffer);
...........................
559 return rval;
560 }
+561 #endif
562 /*******************************************************************/
563 while (left_to_write > 0) {
564 size_t block_offset = offset & (nand->erasesize - 1);
565 size_t write_size, truncated_write_size;
567 WATCHDOG_RESET ();
568
569 if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {
570 printf ("Skip bad block 0x%08llx\n",
571 offset & ~(nand->erasesize - 1));
572 offset += nand->erasesize - block_offset;
573 continue;
574 }
575 //********************Modified by lk*******************************/
+576 #if defined(CONFIG_CMD_NAND_YAFFS)&&defined(CONFIG_CMD_NAND_YAFFS2)
+577 if(nand->skipfirstblk==1) {
+578 nand->skipfirstblk=0;
+579 printf ("Skip the first good block %llx\n",
+580 offset & ~(nand->erasesize - 1));
+581 offset += nand->erasesize - block_offset;
+582 continue;
+583 }
+584 #endif
585 /****************************************************************/
586 if (left_to_write < (nand->erasesize- block_offset))//blocksize
587 write_size = left_to_write;
588 else
589 write_size = nand->erasesize- block_offset;
590 /*******************************Modified by lk*****************************/
591 #if defined(CONFIG_CMD_NAND_YAFFS)&&(!defined(CONFIG_CMD_NAND_YAFFS2))
592 if (flags & WITH_YAFFS_OOB) {
593
594 int page, pages;
............................................
609 ops.oobbuf = ops.datbuf + pagesize;
611 rval = nand->write_oob(nand, offset, &ops);
612 if (!rval)
613 break;
614
615 offset += pagesize;
616 p_buffer += pagesize_oob;
617 }
618 }
619 else
620 #endif/*********************************************************/
621 {
622 truncated_write_size = write_size;
623 #ifdef CONFIG_CMD_NAND_TRIMFFS
624 if (flags & WITH_DROP_FFS)
625 truncated_write_size = drop_ffs(nand, p_buffer,
626 &write_size);
627 #endif
+628 printf("\rWriting at 0x%llx -- ",offset);//Modified by lk 显示烧写位置
629 rval = nand_write(nand, offset, &truncated_write_size,
630 p_buffer);
631 //************************Modified by lk****************************
+632 #if (defined(CONFIG_CMD_NAND_YAFFS)&&(!defined(CONFIG_CMD_NAND_YAFFS2)))
633 offset += write_size;
634 p_buffer += write_size;
+635 #endif
636 //******************************************************************
637 }
638
639 if (rval != 0) {
640 printf ("NAND write to offset %llx failed %d\n",
641 offset, rval);
642 *length -= left_to_write;
643 return rval;
644 }
645 left_to_write -= write_size;
+646 printf("%d%% is complete.",100-(left_to_write/(*length/100)));//Modified by lk 显示烧写进度
647 //**************************************Modified by lk***************************
+648 #if (defined(CONFIG_CMD_NAND_YAFFS)&&defined(CONFIG_CMD_NAND_YAFFS2))
+649 offset += write_size;
+650 if(nand->rw_oob==1) {
+651 p_buffer += write_size+(write_size/nand->writesize*nand->oobsize);
+652 } else {
+653 p_buffer += write_size;
+654 }
+655 #else
656 p_buffer += write_size;
+657 #endif
658 //******************************************************************************
659 }
660
661 return 0;
662 }
因为在上面函数中会调用nand_write函数故做下面修改。
4.修改drivers/mtd/nand/nand_base.c文件,使nand_write支持64byte,oob的烧写
static int nand_write(struct mtd_info *mtd, loff_t to, size_t len,
1948 size_t *retlen, const uint8_t *buf)
1949 {
1950 struct nand_chip *chip = mtd->priv;
1951 int ret;
+1952 #if defined(CONFIG_CMD_NAND_YAFFS)
1953 /*Modified by lk*/
+1954 int oldopsmode = 0;
+1955 if(mtd->rw_oob==1) {
+1956
+1957 size_t oobsize = mtd->oobsize;
+1958 size_t datasize = mtd->writesize;
+1959 int i = 0;
+1960 uint8_t oobtemp[oobsize];
+1961 int datapages = 0;
+1962
+1963 datapages = len/(datasize);
+1964 for(i=0;i<(datapages);i++) {
+1965 memcpy((void *)oobtemp,
+1966 (void *)(buf+datasize*(i+1)),oobsize);
+1967 memmove((void *)(buf+datasize*(i+1)),(void *)(buf+datasize*(i+1)+o obsize),(datapages-(i+1))*(datasize)+(datapages-1)*oobsize);
+1968 }
1969 #endif
1970
1971 /* Do not allow writes past end of device */
1972 if ((to + len) > mtd->size)
1973 return -EINVAL;
1974 if (!len)
1975 return 0;
1976 nand_get_device(chip, mtd, FL_WRITING);
1977 chip->ops.len = len;
1978 chip->ops.datbuf = (uint8_t *)buf;
1979 //chip->ops.oobbuf = NULL;
1980
+1981 #if defined(CONFIG_CMD_NAND_YAFFS)
+1982 /*Modified by lk*/
+1983 if(mtd->rw_oob!=1) {
+1984 chip->ops.oobbuf = NULL;
+1985 } else {
+1986 chip->ops.oobbuf = (uint8_t *)(buf+len);
+1987 chip->ops.ooblen = mtd->oobsize;
+1988 oldopsmode = chip->ops.mode;
+1989 chip->ops.mode = MTD_OOB_RAW;
+1990 }
+1991 #else
1992 chip->ops.oobbuf = NULL;
+1993 #endif
1994
1995 ret = nand_do_write_ops(mtd, to, &chip->ops);
1996
1997 *retlen = chip->ops.retlen;
1998
1999 nand_release_device(mtd);
2000
+2001 #if defined(CONFIG_CMD_NAND_YAFFS)
+2002 /*Modified by lk*/
+2003 chip->ops.mode = oldopsmode;
+2004 #endif
2005
2006 return ret;
2007 }
5.修改include/linux/mtd/mtd.h 添加两变量定义
238 void (*put_device) (struct mtd_info *mtd);
+239 #if defined(CONFIG_CMD_NAND_YAFFS)
+240 u_char rw_oob;
+241 u_char skipfirstblk;
+242 #endif
至此 u-boot for tiny210 ver4.0修改完毕.
另外需更改内核配置:
1.去掉S3C NAND Hardware ECC 选项
2.选择yaffs2自己的ECC校验算法。
File systems --->
[*] Miscellaneous filesystems --->
<*> YAFFS2 file system support │ │
-*- 512 byte / page devices │ │
[ ] Use older-style on-NAND data format with pageStatus byt│ │
[*] Lets Yaffs do its own ECC │ │
[ ] Use the same ecc byte order as Steven Hill's nand_e│ │
-*- 2048 byte (or larger) / page devices │ │
[*] Autoselect yaffs2 format
配置好内核以后重新编译内核。
3.去掉内核的软件ECC校验。
修改内核\drivers\mtd\nand\S3c_nand.c
1172:#else
1173: nand->ecc.mode = NAND_ECC_NONE;
根据友善内核的分区表:
内核的烧写位置是0x600000开始的区域,文件系统烧写位置为0xe00000开始的区域。
用友善的镜像做烧写文件系统的测试,用下面三条命令完成烧写yaffs2文件系统(注:在烧写yaffs2时要擦除0xe00000开始后面所有的块中的数据,否则会由于残存的数据影响Android启动)
烧写过程如下图:
启动信息如下图:(注:若在校正触摸屏出现Calibration failed.的问题,多尝试校正(保存)几次就通过了。测试时偶尔出现过这个问题。)