天天看點

有關去除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         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        

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

。。。。。。 454                 455                 memcpy(data_buf, buffer, readlen); 456                 buffer += readlen; 457  458                 if (opts->writeoob) { 459                         461                         memcpy(oob_buf, buffer, meminfo->oobsize); 462                         buffer += meminfo->oobsize; 463  464                         466                         result = meminfo->write_oob(meminfo, 467                                                     mtdoffset, 468                                                     meminfo->oobsize, 469                                                     &written, 470                                                     (unsigned char *) 471                                                     &oob_buf); 。。。。。。 480  481                 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         1680         if (oobsel == NULL) 1681                 oobsel = &mtd->oobinfo; 1682  1683         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         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          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                          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久。是以網絡的友善使得問題的解決變的簡單,但有時候也會帶來麻煩。在可能的情況下,還是要對抄來的代碼進行研究,了解背後的原因,知其是以然,否則不但學不到東西,還有可能導緻莫名其妙或者難以解決的問題。

繼續閱讀