2. 軟件方面html
若是想要在Linux下編寫Nand Flash驅動,那麼就先要搞清楚Linux下,關於此部分的整個框架。弄明白,系統是如何管理你的nand flash的,以及,系統都幫你作了那些準備工做,而剩下的,驅動底層實現部分,你要去實現哪些功能,才能使得硬件正常工做起來。linux
【內存技術設備,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設備來管理,根據這些設備的特色,上層實現了常見的操做函數封裝,底層具體的內部實現,就須要驅動設計者本身來實現了。具體的內部硬件設備的讀/寫/擦除函數,那就是你必須實現的了。ide
HARD drives函數 |
MTD device |
連續的扇區 |
連續的可擦除塊 |
扇區都很小(512B,1024B) |
可擦除塊比較大 (32KB,128KB) |
主要經過兩個操做對其維護操做:讀扇區,寫扇區 |
主要經過三個操做對其維護操做:從擦除塊中讀,寫入擦除塊,擦寫可擦除塊 |
壞快被從新映射,而且被硬件隱藏起來了(至少是在現在常見的LBA硬盤設備中是如此) |
壞的可擦除塊沒有被隱藏,軟件中要處理對應的壞塊問題。 |
HDD扇區沒有擦寫壽命超出的問題。 |
可擦除塊是有擦除次數限制的,大概是104-105次. |
表4.MTD設備和硬盤設備之間的區別
多說一句,關於MTD更多的內容,感興趣的,去附錄中的MTD的主頁去看。
關於mtd設備驅動,感興趣的能夠去參考
那裏,算是比較詳細地介紹了整個流程,方便你們理解整個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->s3c24xx_nand_probe,
在probe過程當中,去用clk_enable打開nand flash控制器的clock時鐘,用request_mem_region去申請驅動所須要的一些內存等相關資源。而後,在s3c2410_nand_inithw中,去初始化硬件相關的部分,主要是關於時鐘頻率的計算,以及啓用nand flash控制器,使得硬件初始化好了,後面才能正常工做。
3. 須要多解釋一下的,是這部分代碼:
for (setno = 0; setno < 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->scan_res = nand_scan_ident(&nmtd->mtd,
(sets) ? sets->nr_chips : 1);
if (nmtd->scan_res == 0) {
s3c2410_nand_update_chip(info, nmtd);
/* scan tail,從名字就能夠看出來,是掃描的後一階段,此時,通過前面的scan_ident,咱們已經得到對應nand flash的硬件的各個參數,而後就能夠在scan tail中,根據這些參數,去設置其餘一些重要參數,尤爲是ecc的layout,即ecc是如何在oob中擺放的,最後,再去進行一些初始化操做,主要是根據你的驅動,若是沒有實現一些函數的話,那麼就用系統默認的。 */
nand_scan_tail(&nmtd->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 = &nmtd->chip;
void __iomem *regs = info->regs;
chip->write_buf = s3c2410_nand_write_buf;
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
chip->chip_delay = 50;
chip->priv = nmtd;
chip->options = 0;
chip->controller = &info->controller;
switch (info->cpu_type) {
case TYPE_S3C2410:
/* nand flash控制器中,通常都有對應的數據寄存器,用於給你往裏面寫數據,表示將要讀取或寫入多少個字節(byte,u8)/字(word,u32) ,因此,此處,你要給出地址,以便後面的操做所使用 */
chip->IO_ADDR_W = regs + S3C2410_NFDATA;
info->sel_reg = regs + S3C2410_NFCONF;
info->sel_bit = S3C2410_NFCONF_nFCE;
chip->cmd_ctrl = s3c2410_nand_hwcontrol;
chip->dev_ready = s3c2410_nand_devready;
break;
。。。。。。
}
chip->IO_ADDR_R = chip->IO_ADDR_W;
nmtd->info = info;
nmtd->mtd.priv = chip;
nmtd->mtd.owner = THIS_MODULE;
nmtd->set = set;
if (hardware_ecc) {
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
chip->ecc.correct = s3c2410_nand_correct_data;
/* 此處,多數狀況下,你所用的Nand Flash的控制器,都是支持硬件ECC的,因此,此處設置硬件ECC(HW_ECC) ,也是充分利用硬件的特性,而若是此處不用硬件去作的ECC的話,那麼下面也會去設置成NAND_ECC_SOFT,系統會用默認的軟件去作ECC校驗,相比之下,比硬件ECC的效率就低不少,而你的nand flash的讀寫,也會相應地要慢很多*/
chip->ecc.mode = NAND_ECC_HW;
switch (info->cpu_type) {
case TYPE_S3C2410:
chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
break;
。。。。。
}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
}
if (set->ecc_layout != NULL)
chip->ecc.layout = set->ecc_layout;
if (set->disable_ecc)
chip->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的總體性能和效率。
【引用文章】
http://hi.baidu.com/serial_story/blog/item/3f1635d1dc041cd7562c84a1.html
2. Samsung的型號爲K9G8G08U0M的Nand Flash的數據手冊
要下載數據手冊,能夠去這裏介紹的網站下載:
samsung 4K pagesize SLC Nand Flash K9F8G08U0M datasheet + 推薦一個datasheet搜索的網站
http://hi.baidu.com/serial_story/blog/item/7f25a03def1de309bba167c8.html
http://hi.baidu.com/serial_story/blog/item/f06db3546eced11a3b29356c.html
4. Memory Technology Device (MTD) Subsystem for Linux.