基於MTD的NAND驅動開發(一)

○、說明
 
大約用了兩個禮拜不到的時間爲公司的IPcamera 系統寫了基於MTDNAND 驅動(linux-2.6.22.10 內核) ,目前已可以在該驅動的支持下跑cramfsjffs2 文件系統,另外,該驅動也可以同時支持small page( 每頁512 Byte)big page( 每頁2048 Byte) 兩種NAND 芯片。在此整理一下與NAND 驅動相關的概念,結構體,驅動框架和流程,同時分析一下基於MTDNAND 驅動的部分函數,尤其是其中的nand_scan() 函數。( 涉及到具體NAND 芯片時,若不做說明,將以small pageNAND 芯片爲例。)
 
注:個人理解,有誤難免!—— 筆者:曹榮榮
 
 
MTD 驅動程序是專門針對嵌入式 Linux 的一種驅動程序,相對於常規塊設備驅動程序(比如 PC 中的 IDE 硬盤)而言, MTD 驅動程序能更好的支持和管理閃存設備,因爲它本身就是專爲閃存設備而設計的。
具體地講,基於 MTD FLASH 驅動,承上可以很好地支持 cramfs jffs2 yaffs 等文件系統,啓下也能對 FLASH 的擦除,讀寫, FLASH 壞塊以及損耗平衡進行很好的管理。所謂損耗平衡,是指對 NAND 的擦寫不能總是集中在某一個或某幾個 block 中,這是由 NAND 芯片有限的擦寫次數的特性決定的。
總之,在現階段,要爲 FLASH 設備開發 Linux 下的驅動程序,那麼基於 MTD 的開發將幾乎是省時又省力的唯一選擇!
 
一、NAND NOR 的區別
 
Google Nand FlashNor Flash 的區別”。
 
簡單點說,主要的區別就是:
 
1、  NANDNOR 便宜;NAND 的容量比NOR 大(指相同成本);NAND 的擦寫次數是NOR 的十倍;NAND 的擦除和寫入速度比NOR 快,讀取速度比NOR 稍慢;
 
2、  NANDNOR 的讀都可以以字節爲單位,但NAND 的寫以page 爲單位,而NOR 可以隨機寫每一個字節。NANDNOR 的擦除都以block 爲單位,但一般NANDblockNORblock 小。另外,不管是NAND 還是NOR ,在寫入前,都必須先進行擦除操作,但是NOR 在擦除前要先寫0
 
3、  NAND 不能在片內運行程序,而NOR 可以。但目前很多CPU 都可以在上電時,以硬件的方式先將NAND 的第一個block 中的內容(一般是程序代碼,且也許不足一個block ,如2KB 大小)自動copyram 中,然後再運行,因此只要CPU 支持,NAND 也可以當成啓動設備;
 
4、  NANDNOR 都可能發生比特位反轉(但NAND 反轉的機率遠大於NOR ),因此這兩者都必須進行ECC 操作;NAND 可能會有壞塊(出廠時廠家會對壞塊做標記),在使用過程中也還有可能會出現新的壞塊,因此NAND 驅動必須對壞塊進行管理。
 
二、 內核樹中基於 MTD NAND 驅動代碼的佈局
 
Linux 內核中,MTD 源代碼放在linux-2.6.22.10/driver/mtd 目錄中,該目錄中包含chipsdevicesmapsnandonenandubi 六個子目錄。
 
其中只有nandonenand 目錄中的代碼才與NAND 驅動相關,不過nand 目錄中的代碼比較通用,而onenand 目錄中的代碼相對於nand 中 的代碼而言則簡化了很多,它是針對三星公司開發的另一類Flash芯片,即OneNAND Flash。我尚未對OneNand FLASH有過研究,只是通過網上資料得知,OneNand FLASH克服了傳統NAND Flash接口複雜的缺點,具有接口簡單、讀寫速度快、容量大、壽命長、成本低等優點,因此應該是一種較常用NAND先進的FLASH吧,只是目前似乎普 及率並不高,本文也將不做討論。
 
因此,若只是開發基於MTDNAND 驅動程序,那麼我們需要關注的代碼就基本上全在linux-2.6.22.10/drivers/mtd/nand 目錄中了,而該目錄中也不是所有的代碼文件都與我們將要開發的NAND 驅動有關,除了MakefileKconfig 之外,其中真正與NAND 驅動有關的代碼文件只有6 個,即:
 
1、  nand_base.c
定義了NAND 驅動中對NAND 芯片最基本的操作函數和操作流程,如擦除、讀寫page 、讀寫oob 等。當然這些函數都只是進行一些default 的操作,若你的系統在對NAND 操作時有一些特殊的動作,則需要在你自己的驅動代碼中進行定義,然後Replace 這些default 的函數。
 
2、  nand_bbt.c
定義了NAND 驅動中與壞塊管理有關的函數和結構體。
 
3、  nand_ids.c
定義了兩個全局類型的結構體:struct nand_flash_dev nand_flash_ids[ ]struct nand_manufacturers nand_manuf_ids[ ] 。其中前者定義了一些NAND 芯片的類型,後者定義了NAND 芯片的幾個廠商。NAND 芯片的ID 至少包含兩項內容:廠商ID 和廠商爲自己的NAND 芯片定義的芯片ID 。當NAND 驅動被加載的時候,它會去讀取具體NAND 芯片的ID ,然後根據讀取的內容到上述定義的nand_manuf_ids[ ]nand_flash_ids[ ] 兩個結構體中去查找,以此判斷該NAND 芯片是那個廠商的產品,以及該NAND 芯片的類型。若查找不到,則NAND 驅動就會加載失敗,因此在開發NAND 驅動前必須事先將你的NAND 芯片添加到這兩個結構體中去(其實這兩個結構體中已經定義了市場上絕大多數的NAND 芯片,所以除非你的NAND 芯片實在比較特殊,否則一般不需要額外添加)。值得一提的是,nand_flash_ids[ ] 中有三項屬性比較重要,即pagesizechipsizeerasesize ,驅動就是依據這三項屬性來決定對NAND 芯片進行擦除,讀寫等操作時的大小的。其中pagesizeNAND 芯片的頁大小,一般爲2565122048chipsizeNAND 芯片的容量;erasesize 即每次擦除操作的大小,通常就是NAND 芯片的block 大小。
 
4、  nand_ecc.c
定義了NAND 驅動中與softeware ECC 有關的函數和結構體,若你的系統支持hardware ECC ,且不需要software ECC ,則該文件也不需理會。
 
5、  nandsim.c
定義了Nokia 開發的模擬NAND 設備,默認是Toshiba NAND 8MiB 1,8V 8-bit (根據ManufactureID ),開發普通NAND 驅動時不用理會。
 
6、  diskonchip.c
定義了片上磁盤(DOC) 相關的一些函數,開發普通NAND 驅動時不用理會。
 
除了上述六個文件之外,nand 目錄中其他文件基本都是特定系統的NAND 驅動程序例子,但本人看來真正有參考價值的只有cafe_nand.cs3c2410.c 兩個,而其中又尤以cafe_nand.c 更爲詳細,另外,nand 目錄中也似乎只有cafe_nand.c 中的驅動程序在讀寫NAND 芯片時用到了DMA 操作。
 
綜上所述,若要研究基於MTDNAND 驅動,其實所需閱讀的代碼量也不是很大。
 
另外,在動手寫NAND 驅動之前,也許需要讀一下以下文檔:
1、  Linux MTD 源代碼分析:
該文檔可以讓我們對MTD 有一個直觀而又相對具體的認識,但它似乎主要是針對NOR FLASH 的,對於實際開發NAND 驅動的幫助並不是很大。
2、  MTD NAND Driver Programming Interface
該文檔中關於ECC 的說明很有幫助。
3、  MTD 的官方網站:
 
三、NAND 相關原理
 
在我們開始NAND 驅動編寫之前,至少應該知道:數據在NAND 中是怎樣存儲的,以及以怎樣的方式從NAND 中讀寫數據時。
 
1、  NAND 的存儲結構和操作方式
 
這方面的資料可以從任意一種NANDdatasheet 中得到,因爲基本上每一種NANDdatasheet 都會介紹NAND 的組成結構和操作命令,而且事實上,大多數的NAND datasheet 都大同小異,所不同的大概只是該NAND 芯片的容量大小和讀寫速度等基本特性。
 
這裏以每頁512 字節的NAND FLASH 爲例簡單說明一下:每一塊NAND 芯片由nblock 組成-> 每一個blockmpage 組成-> 每一個page256 字節大小的column1( 也稱1st half page)256 字節大小的column2( 也稱2nd half page)16 字節大小的oob(out-of-band ,也稱spare area) 組成。至於mn 的大小可以查看特定NANDdatasheet 。相應的,若給定NAND 中的一個字節的地址,我們可以根據這個地址算出block 地址( 即第幾個block)page 地址( 即該block 中的第幾個page)column 地址(1st half page ,或2nd half page ,或oob 中的第幾個字節)
 
在擦除NAND 時,必須每次至少擦除1block ;在寫NAND 時,必須每次寫1page( 有些NAND 也支持寫不足一個page 大小的數據) ;在讀NAND 時,分爲三種情況( 對應三種不同的NAND 命令) ,即讀column1 、讀column2 和讀oob ,那麼爲什麼要分這三種情況呢?假如知道NAND 怎樣根據給定的地址確定它的存儲單元,那麼自然也就能明白原因了,其實也並不複雜,主要是因爲給定地址中的A8 並不在NAND 的視野範圍之內( 也許表達並不準確)
 
事實上,在寫基於MTDNAND 驅動時,我們並不需要實現精確到讀寫某一個byte 地址的函數( 除了讀oob 之外) ,這是因爲:
 
基於MTDNAND 驅動在讀寫NAND 時,可以分兩種情況,即:(1) 不進行ECC 檢測時,一次讀寫一整個page 中的MAIN 部分( 也就是那真實存儲數據的512 字節)(2) 進行ECC 檢測時( 不管是hardware ECC 還是software ECC) ,一次讀寫一整個page( 包括16 字節的oob 部分) 。所以部分NAND 所支持的寫不足一個page 大小數據的功能,對MTD 來說是用不着的。
 
那麼,如果只需要讀寫不足一個page 大小的數據怎麼辦?這是MTD 更上層的部分需要處理的事。也就是說,對於NAND 驅動來說,它只會讀寫整整一個page 的數據!
 
最後值得一提的是,NAND 驅動有可能只去讀oob 部分,這是因爲除了ECC 信息之外,壞塊信息也存儲在oob 之中,NAND 驅動需要讀取oob 中描述壞塊的那個字節( 通常是每個block 的第一個pageoob 中的第六個字節) 來判斷該block 是不是一個壞塊。所以,我們只有在讀oob 時,才需要實現精確到讀某一個byte 地址的函數。
 
由此,我們也可以額外知道一件事,那就是NAND 驅動中用到的column 地址只在讀oob 時纔有用,而在其他情況下,column 地址都爲0
 
2、  ECC 相關的結構體
struct nand_ecclayout {
           uint32_t eccbytes;
           uint32_t eccpos[64];
           uint32_t oobavail;
           struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};
這是用來定義ECCoob 中佈局的一個結構體。
 
前面已經提及過,oob 中主要存儲兩種信息:壞塊信息和ECC 數據。對與small pageNAND 芯片來說,其中壞塊信息佔據1 個字節( 一般固定在第六個字節)ECC 數據佔據三個字節。所以sturct nand_ecclayout 這個結構體,也就是用來告訴那些與ECC 操作無關的函數,Nand 芯片的oob 部分中,哪些字節是用來存儲ECC( 即不可用作它用的) ,哪些字節是空閒的,即可用的。
 
其實之所以有這個結構體,主要是因爲硬件ECC 的緣故。以寫數據爲例,在使用硬件ECC 的情況下,那三個字節的ECC 數據是由硬件計算得到,並且寫到NAND 芯片的oob 中去的,同時也是由硬件決定寫到oob 的哪三個字節中去。這些都是由硬件做的,而NAND 驅動並不知道,所以就需要用這個結構體來告訴驅動了。
 
所以,在寫NAND 驅動時,就有可能需要對這個結構體進行賦值。這裏說“有可能”,是因爲MTD 對這個結構體有一個默認的賦值,假如這個賦值所定義的ECC 位置與你的硬件一致的話,那就不必在你的驅動中手動賦值了。其實對大多數硬件( 這裏所說的硬件,不是指NAND 芯片,而是NAND 控制器) 來說,是不必手動賦值的,但也有許多例外。
 
值得一提的是,這個結構體不僅僅用來定義ECC 佈局,也可以用來將你的驅動在oob 中需要額外用到的字節位置保護起來。
 
現在對struct nand_ecclayout 這個結構體進行一下說明。
 
uint32_t eccbytesECC 的字節數,對於512B-per-pageNAND 來說,eccbytes = 3 ,如果你需要額外用到oob 中的數據,那麼也可以大於3.
uint32_t eccpos[64]ECC 數據在oob 中的位置,這裏之所以是個64 字節的數組,是因爲對於2048-per-pageNAND 來說,它的oob64 個字節。而對於512B-per-pageNAND 來說,可以而且只可以定義它的前16 個字節。
uint32_t oobavailoob 中可用的字節數,這個值不用賦值,MTD 會根據其它三個變量自動計算得到。
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES] :顯示定義空閒的oob 字節。
 
完了,似乎有點不想寫下去了:(
發佈了47 篇原創文章 · 獲贊 6 · 訪問量 48萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章