有关去除dataflash以及换为64M nand的相关修改及bugfix

 

最近核心板进行了改版,以前两个bootloader放在dataflash里面,现在板子上只留了nand,所以bootstrap,u-boot,kernel,fs全存放在了nand上。同时,除了原有的256m,也新改出了一批64m nand的板子,针对这两种情况,需要对bsp进行修改。

同为256m的相对好改,只涉及到samba的烧写流程,偏移量,镜像生成等,没啥好说的。问题主要出在64m nand,由于原有的256使用的是yaffs2,而64m则是yaffs,所以有一定修改量,也出了些问题。我们主要靠u-boot烧写内核及fs的镜像,而问题就出现在fs的烧写上。最诡异的问题是,几块板子有的一直没问题,剩下的有时候有问题有时候又没问题,很难定性问题.


bootstrap的修改参考:

http://blog.csdn.net/kailan818/archive/2009/12/14/5004934.aspx

u-boot的镜像烧写修改参考:

http://blogold.chinaunix.net/u3/90065/showart_1780393.html

mkyaffsimage自然也不能用以前256m使用的工具,图省事,我拿了tq2440开发板自带的为64m内存提供的工具,谁知道其实给我添了不少麻烦。


bsp修改完,用samba把bootstrap,u-boot烧写到nand上,随后再用u-boot通过u盘烧写kernel,fs。完毕重启后进入系统发现,只要有写操作,yaffs就会报page xxx in gc has no object....的错误。跟踪发现这是yaffs损耗平衡出错信息。问题是将将写一点东西,怎么会需要做损耗均衡,并且出错呢?经过一长串痛苦的跟踪,发现最终是ecc对不上的问题。

问题看来出在镜像烧写上,那么我们来review一下后来添加的代码:

 343                 }else if (  s != NULL && !strcmp(s, ".yaffs")){

 344                         nand_write_options_t opts;

 345                         memset(&opts, 0, sizeof(opts));

 346                         opts.buffer = (u_char*) addr;

 347                         opts.length = size;

 348                         opts.offset = off;

 349 

 350                         opts.noecc = 1;

 351                         opts.writeoob = 1;

 352                         opts.blockalign = 1;

 353                         opts.quiet      = quiet;

 354                         opts.skipfirstblk = 1;

 355                         ret = nand_write_opts(nand, &opts);

最终证明,第350行导致出错,那么一步步跟下去,看看为什么我这里无法采用这个选项。
进入nand_write_opts:
338         /* write without ecc? */
339         if (opts->noecc) {
340                 memcpy(&meminfo->oobinfo, &none_oobinfo,
341                        sizeof(meminfo->oobinfo));
342                 oobinfochanged = 1;
343         }
这里使用none_oobinfo:
266 static struct nand_oobinfo none_oobinfo = {
267         .useecc = MTD_NANDECC_OFF,
268 };
可以看到,这里的ecc标志是eccoff。
继续往后走:
395         /* get data from input and write to the device */

396         while (imglen && (mtdoffset < meminfo->size)) {

。。。。。。
454                 /* read page data from input memory buffer */
455                 memcpy(data_buf, buffer, readlen);
456                 buffer += readlen;
457 
458                 if (opts->writeoob) {
459                         /* read OOB data from input memory block, exit
460                          * on failure */
461                         memcpy(oob_buf, buffer, meminfo->oobsize);
462                         buffer += meminfo->oobsize;
463 
464                         /* write OOB data first, as ecc will be placed
465                          * in there*/
466                         result = meminfo->write_oob(meminfo,
467                                                     mtdoffset,
468                                                     meminfo->oobsize,
469                                                     &written,
470                                                     (unsigned char *)
471                                                     &oob_buf);
。。。。。。
480 
481                 /* write out the page data */
482                 result = meminfo->write(meminfo,
483                                         mtdoffset,
484                                         meminfo->oobblock,
485                                         &written,
486                                         (unsigned char *) &data_buf);
下面开始按页写数据,从内存地址内读镜像向nand里写。注意由于是64m,所以是512+16。第466行调用writeoob先写16字节的oob数据。在第482行调用write开始写页。注意这里每页写完也根据情况计算ecc并且重填前面的oob区,分析我们关心的write,其实是nand_write:
1620 static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
1621 {
1622         return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
1623 }
再进入nand_write:
1679         /* if oobsel is NULL, use chip defaults */
1680         if (oobsel == NULL)
1681                 oobsel = &mtd->oobinfo;
1682 
1683         /* Autoplace of oob data ? Use the default placement scheme */
1684         if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
1685                 oobsel = this->autooob;
1686                 autoplace = 1;
1687         }
1688         if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
1689                 autoplace = 1;
由于nand_write传入的oobsel是null,所以这里会选择我们以前设置的结构体,用的也是我们指定的校验方法,后面开始写页:
1706         /* Loop until all data is written */
1707         while (written < len) {
。。。。。。
1715                 ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
再进去看一看:
903         int     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
。。。。。。
915         switch (eccmode) {
 916         /* No ecc, write all */
 917         case NAND_ECC_NONE:       
 918 //              printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
 919                 this->write_buf(mtd, this->data_poi, mtd->oobblock);
 920                 break;
。。。。。。
 932         default:
 933                 eccbytes = this->eccbytes;
 934                 for (; eccsteps; eccsteps--) {
 935                         /* enable hardware ecc logic for write */
 936                         this->enable_hwecc(mtd, NAND_ECC_WRITE);
 937                         this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
 938                         this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
 939                         for (i = 0; i < eccbytes; i++, eccidx++)
 940                                 oob_buf[oob_config[eccidx]] = ecc_code[i];
。。。。。。
955                 this->write_buf(mtd, oob_buf, mtd->oobsize);
这里会根据我们选择的ecc方式来做。首先我们看到,不管是无ecc,还是注释掉noecc=1后走的default分支,都是先write_buf这个512字节的data区,如果是noecc,那么就打住了。由于镜像中本身就有mkyaffsimage时生成的ecc,所以这里只是单纯的拷贝。而下面的分支,则会在写完nand以后,再进行一个ecc计算,随后根据计算结果重填前面已写过的oob区的相关位。938行计算ecc,940重填,第955行重写oob。这里noecc也会重填,不过由于无修改,所以无所谓。
为什么u-boot在mkyaffsimage已经算过ecc的情况下还要再计算一次才正确呢?其实这里有一个ecc配合的问题。ecc有mtd及yaffs两种,算法各不相同,所以一定要对应上。内核里编译选项选择的是let yaffs do its own ecc,那么镜像制作也一定要按照这个格式。这里由于偷懒,直接拿别人的工具,没源码也不知道实现,u-boot烧写部分又照抄网上的文章没仔细研究,内核也按照以前的需求来配的,所以3个都没有对上,导致了错误。除了u-boot的noecc=1去掉,也可以保留这里不动,将内核里let yaffs do its own ecc去掉, 让mtd层来做, 一样可以。总之镜像的校验和内核使用的ecc一定要对上。

其实最终的问题解决,只是注释掉一个noecc,或者改一个内核选项,但足足调了n久。所以网络的方便使得问题的解决变的简单,但有时候也会带来麻烦。在可能的情况下,还是要对抄来的代码进行研究,了解背后的原因,知其所以然,否则不但学不到东西,还有可能导致莫名其妙或者难以解决的问题。

你可能感兴趣的:(struct,Scheme,null,buffer,input,工具)