天天看點

如何編寫linux下nand flash驅動-4

2.       軟體方面

如果想要在Linux下編寫Nand Flash驅動,那麼就先要搞清楚Linux下,關于此部分的整個架構。弄明白,系統是如何管理你的nand flash的,以及,系統都幫你做了那些準備工作,而剩下的,驅動底層實作部分,你要去實作哪些功能,才能使得硬體正常工作起來。

【記憶體技術裝置,MTD(Memory Technology Device)】

MTD,是Linux的儲存設備中的一個子系統。其設計此系統的目的是,對于記憶體類的裝置,提供一個抽象層,一個接口,使得對于硬體驅動設計者來說,可以盡量少的去關心存儲格式,比如FTL,FFS2等,而隻需要去提供最簡單的底層硬體裝置的讀/寫/擦除函數就可以了。而對于資料對于上層使用者來說是如何表示的,硬體驅動設計者可以不關心,而MTD儲存設備子系統都幫你做好了。

對于MTD字系統的好處,簡單解釋就是,他幫助你實作了,很多對于以前或者其他系統來說,本來也是你驅動設計者要去實作的很多功能。換句話說,有了MTD,使得你設計Nand Flash的驅動,所要做的事情,要少很多很多,因為大部分工作,都由MTD幫你做好了。

當然,這個好處的一個“副作用”就是,使得我們不了解的人去了解整個Linux驅動架構,以及MTD,變得更加複雜。但是,總的說,覺得是利遠遠大于弊,否則,就不僅需要你了解,而且還是做更多的工作,實作更多的功能了。

此外,還有一個重要的原因,那就是,前面提到的nand flash和普通硬碟等裝置的特殊性:

有限的通過出複用來實作輸入輸出指令和位址/資料等的IO接口,最小機關是頁而不是常見的bit,寫前需擦除等,導緻了這類裝置,不能像平常對待硬碟等操作一樣去操作,隻能采取一些特殊方法,這就誕生了MTD裝置的統一抽象層。

MTD,将nand flash,nor flash和其他類型的flash等裝置,統一抽象成MTD裝置來管理,根據這些裝置的特點,上層實作了常見的操作函數封裝,底層具體的内部實作,就需要驅動設計者自己來實作了。具體的内部硬體裝置的讀/寫/擦除函數,那就是你必須實作的了。

HARD drives

MTD device

連續的扇區

連續的可擦除塊

扇區都很小(512B,1024B)

可擦除塊比較大 (32KB,128KB)

主要通過兩個操作對其維護操作:讀扇區,寫扇區

主要通過三個操作對其維護操作:從擦除塊中讀,寫入擦除塊,擦寫可擦除塊

壞快被重新映射,并且被硬體隐藏起來了(至少是在如今常見的LBA硬碟裝置中是如此)

壞的可擦除塊沒有被隐藏,軟體中要處理對應的壞塊問題。

HDD扇區沒有擦寫壽命超出的問題。

可擦除塊是有擦除次數限制的,大概是104-105次.

表4.MTD裝置和硬碟裝置之間的差別

多說一句,關于MTD更多的内容,感興趣的,去附錄中的MTD的首頁去看。

關于mtd裝置驅動,感興趣的可以去參考

<a href="http://www.cnitblog.com/luofuchong/archive/2007/08/31/32682.html" target="_blank">MTD原始裝置與FLASH硬體驅動的對話</a>

<a href="http://www.cnitblog.com/luofuchong/archive/2007/09/04/32939.html" target="_blank">MTD原始裝置與FLASH硬體驅動的對話-續</a>

那裡,算是比較詳細地介紹了整個流程,友善大家了解整個mtd架構和nand flash驅動。

【Nand flash驅動工作原理】

在介紹具體如何寫Nand Flash驅動之前,我們先要了解,大概的,整個系統,和Nand Flash相關的部分的驅動工作流程,這樣,對于後面的驅動實作,才能更加清楚機制,才更容易實作,否則就是,即使寫完了代碼,也還是沒搞懂系統是如何工作的了。

讓我們以最常見的,Linux核心中已經有的三星的Nand Flash驅動,來解釋Nand Flash驅動具體流程和原理。

此處是參考2.6.29版本的Linux源碼中的\drivers\mtd\nand\s3c2410.c,以2410為例。

1.       在nand flash驅動加載後,第一步,就是去調用對應的init函數,s3c2410_nand_init,去将在nand flash驅動注冊到Linux驅動架構中。

2.       驅動本身,真正開始,是從probe函數,s3c2410_nand_probe-&gt;s3c24xx_nand_probe,

在probe過程中,去用clk_enable打開nand flash控制器的clock時鐘,用request_mem_region去申請驅動所需要的一些記憶體等相關資源。然後,在s3c2410_nand_inithw中,去初始化硬體相關的部分,主要是關于時鐘頻率的計算,以及啟用nand flash控制器,使得硬體初始化好了,後面才能正常工作。

3.       需要多解釋一下的,是這部分代碼:

       for (setno = 0; setno &lt; nr_sets; setno++, nmtd++) {

              pr_debug("initialising set %d (%p, info %p)\n", setno, nmtd, info);

/* 調用init chip去挂載你的nand 驅動的底層函數到nand flash的結構體中,以及設定對應的ecc mode,挂載ecc相關的函數 */

              s3c2410_nand_init_chip(info, nmtd, sets);

/* scan_ident,掃描nand 裝置,設定nand flash的預設函數,獲得實體裝置的具體型号以及對應各個特性參數,這部分算出來的一些值,對于nand flash來說,是最主要的參數,比如nand falsh的晶片的大小,塊大小,頁大小等。 */

              nmtd-&gt;scan_res = nand_scan_ident(&amp;nmtd-&gt;mtd,

                                           (sets) ? sets-&gt;nr_chips : 1);

              if (nmtd-&gt;scan_res == 0) {

                     s3c2410_nand_update_chip(info, nmtd);

/* scan tail,從名字就可以看出來,是掃描的後一階段,此時,經過前面的scan_ident,我們已經獲得對應nand flash的硬體的各個參數,然後就可以在scan tail中,根據這些參數,去設定其他一些重要參數,尤其是ecc的layout,即ecc是如何在oob中擺放的,最後,再去進行一些初始化操作,主要是根據你的驅動,如果沒有實作一些函數的話,那麼就用系統預設的。 */

                     nand_scan_tail(&amp;nmtd-&gt;mtd);

/* add partion,根據你的nand flash的分區設定,去分區 */

                     s3c2410_nand_add_partition(info, nmtd, sets);

              }

              if (sets != NULL)

                     sets++;

       }

4.       等所有的參數都計算好了,函數都挂載完畢,系統就可以正常工作了。

上層通路你的nand falsh中的資料的時候,通過MTD層,一層層調用,最後調用到你所實作的那些底層通路硬體資料/緩存的函數中。

【Linux下nand flash驅動編寫步驟簡介】

關于上面提到的,在nand_scan_tail的時候,系統會根據你的驅動,如果沒有實作一些函數的話,那麼就用系統預設的。如果實作了自己的函數,就用你的。

估計很多人就會問了,那麼到底我要實作哪些函數呢,而又有哪些是可以不實作,用系統預設的就可以了呢。

此問題的,就是我們下面要介紹的,也就是,你要實作的,你的驅動最少要做哪些工作,才能使整個nand flash工作起來。

1.       對于驅動架構部分

其實,要了解,關于驅動架構部分,你所要做的事情的話,隻要看看三星的整個nand flash驅動中的這個結構體,就差不多了:

static struct platform_driver s3c2410_nand_driver = {

       .probe            = s3c2410_nand_probe,

       .remove         = s3c2410_nand_remove,

       .suspend = s3c24xx_nand_suspend,

       .resume         = s3c24xx_nand_resume,

       .driver           = {

              .name     = "s3c2410-nand",

              .owner    = THIS_MODULE,

       },

};

對于上面這個結構體,沒多少要解釋的。從名字,就能看出來:

(1)probe就是系統“探測”,就是前面解釋的整個過程,這個過程中的多數步驟,都是和你自己的nand flash相關的,尤其是那些硬體初始化部分,是你必須要自己實作的。

(2)remove,就是和probe對應的,“反初始化”相關的動作。主要是釋放系統相關資源和關閉硬體的時鐘等常見操作了。

(3)suspend和resume,對于很多沒用到電源管理的情況下,至少對于我們剛開始寫基本的驅動的時候,可以不用關心,放個空函數即可。

2.       對于nand flash底層操作實作部分

而對于底層硬體操作的有些函數,總體上說,都可以在上面提到的s3c2410_nand_init_chip中找到:

static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,

                               struct s3c2410_nand_mtd *nmtd,

                               struct s3c2410_nand_set *set)

{

       struct nand_chip *chip = &amp;nmtd-&gt;chip;

       void __iomem *regs = info-&gt;regs;

       chip-&gt;write_buf    = s3c2410_nand_write_buf;

       chip-&gt;read_buf     = s3c2410_nand_read_buf;

       chip-&gt;select_chip  = s3c2410_nand_select_chip;

       chip-&gt;chip_delay   = 50;

       chip-&gt;priv         = nmtd;

       chip-&gt;options    = 0;

       chip-&gt;controller   = &amp;info-&gt;controller;

       switch (info-&gt;cpu_type) {

       case TYPE_S3C2410:

/* nand flash控制器中,一般都有對應的資料寄存器,用于給你往裡面寫資料,表示将要讀取或寫入多少個位元組(byte,u8)/字(word,u32) ,是以,此處,你要給出位址,以便後面的操作所使用 */

              chip-&gt;IO_ADDR_W = regs + S3C2410_NFDATA;

              info-&gt;sel_reg   = regs + S3C2410_NFCONF;

              info-&gt;sel_bit  = S3C2410_NFCONF_nFCE;

              chip-&gt;cmd_ctrl  = s3c2410_nand_hwcontrol;

              chip-&gt;dev_ready = s3c2410_nand_devready;

              break;

。。。。。。

      }

       chip-&gt;IO_ADDR_R = chip-&gt;IO_ADDR_W;

       nmtd-&gt;info       = info;

       nmtd-&gt;mtd.priv       = chip;

       nmtd-&gt;mtd.owner    = THIS_MODULE;

       nmtd-&gt;set        = set;

       if (hardware_ecc) {

              chip-&gt;ecc.calculate = s3c2410_nand_calculate_ecc;

              chip-&gt;ecc.correct   = s3c2410_nand_correct_data;

/* 此處,多數情況下,你所用的Nand Flash的控制器,都是支援硬體ECC的,是以,此處設定硬體ECC(HW_ECC) ,也是充分利用硬體的特性,而如果此處不用硬體去做的ECC的話,那麼下面也會去設定成NAND_ECC_SOFT,系統會用預設的軟體去做ECC校驗,相比之下,比硬體ECC的效率就低很多,而你的nand flash的讀寫,也會相應地要慢不少*/

              chip-&gt;ecc.mode         = NAND_ECC_HW;

              switch (info-&gt;cpu_type) {

              case TYPE_S3C2410:

                     chip-&gt;ecc.hwctl         = s3c2410_nand_enable_hwecc;

                     chip-&gt;ecc.calculate = s3c2410_nand_calculate_ecc;

                     break;

。。。。。

       } else {

              chip-&gt;ecc.mode         = NAND_ECC_SOFT;

       if (set-&gt;ecc_layout != NULL)

              chip-&gt;ecc.layout = set-&gt;ecc_layout;

       if (set-&gt;disable_ecc)

              chip-&gt;ecc.mode     = NAND_ECC_NONE;

}

而我們要實作的底層函數,也就是上面藍色标出來的一些函數而已:

(1)s3c2410_nand_write_buf 和 s3c2410_nand_read_buf:這是兩個最基本的操作函數,其功能,就是往你的nand flash的控制器中的FIFO讀寫資料。一般情況下,是MTD上層的操作,比如要讀取一頁的資料,那麼在發送完相關的讀指令和等待時間之後,就會調用到你底層的read_buf,去nand Flash的FIFO中,一點點把我們要的資料,讀取出來,放到我們制定的記憶體的緩存中去。寫操作也是類似,将我們記憶體中的資料,寫到Nand Flash的FIFO中去。具體的資料流向,參考上面的圖4。

(2)s3c2410_nand_select_chip : 實作Nand Flash的片選。

(3)s3c2410_nand_hwcontrol:給底層發送指令或位址,或者設定具體操作的模式,都是通過此函數。

(4)s3c2410_nand_devready:Nand Flash的一些操作,比如讀一頁資料,寫入(程式設計)一頁資料,擦除一個塊,都是需要一定時間的,在命發送完成後,就是硬體開始忙着工作的時候了,而硬體什麼時候完成這些操作,什麼時候不忙了,變就緒了,就是通過這個函數去檢查狀态的。一般具體實作都是去讀硬體的一個狀态寄存器,其中某一位是否是1,對應着是出于“就緒/不忙”還是“忙”的狀态。這個寄存器,也就是我們前面分析時序圖中的R/B#。

(5)s3c2410_nand_enable_hwecc: 在硬體支援的前提下,前面設定了硬體ECC的話,要實作這個函數,用于每次在讀寫操作前,通過設定對應的硬體寄存器的某些位,使得啟用硬體ECC,這樣在讀寫操作完成後,就可以去讀取硬體校驗産生出來的ECC數值了。

(6)s3c2410_nand_calculate_ecc:如果是上面提到的硬體ECC的話,就不用我們用軟體去實作校驗算法了,而是直接去讀取硬體産生的ECC數值就可以了。

(7)s3c2410_nand_correct_data:當實際操作過程中,讀取出來的資料所對應的硬體或軟體計算出來的ECC,和從oob中讀出來的ECC不一樣的時候,就是說明資料有誤了,就需要調用此函數去糾正錯誤。對于現在SLC常見的ECC算法來說,可以發現2位,糾正1位。如果錯誤大于1位,那麼就無法糾正回來了。一般情況下,出錯超過1位的,好像幾率不大。至少我看到的不是很大。更複雜的情況和更加注重資料安全的情況下,一般是需要另外實作更高效和檢錯和糾錯能力更強的ECC算法的。

當然,除了這些你必須實作的函數之外,在你更加熟悉整個架構之後,你可以根據你自己的nand flash的特點,去實作其他一些原先用系統預設但是效率不高的函數,而用自己的更高效率的函數替代他們,以提升你的nand flash的整體性能和效率。

【引用文章】

<a href="http://hi.baidu.com/serial_story/blog/item/3f1635d1dc041cd7562c84a1.html" target="_blank">http://hi.baidu.com/serial_story/blog/item/3f1635d1dc041cd7562c84a1.html</a>

2. Samsung的型号為K9G8G08U0M的Nand Flash的資料手冊

要下載下傳資料手冊,可以去這裡介紹的網站下載下傳:

samsung 4K pagesize SLC Nand Flash K9F8G08U0M datasheet + 推薦一個datasheet搜尋的網站

<a href="http://hi.baidu.com/serial_story/blog/item/7f25a03def1de309bba167c8.html" target="_blank">http://hi.baidu.com/serial_story/blog/item/7f25a03def1de309bba167c8.html</a>

<a href="http://hi.baidu.com/serial_story/blog/item/f06db3546eced11a3b29356c.html" target="_blank">http://hi.baidu.com/serial_story/blog/item/f06db3546eced11a3b29356c.html</a>

4. Memory Technology Device (MTD) Subsystem for Linux.

<a href="http://www.linux-mtd.infradead.org/index.html" target="_blank">http://www.linux-mtd.infradead.org/index.html</a>

繼續閱讀