如何編寫linux下nandflash驅動-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裝置驅動,感興趣的可以去參考
那裡,算是比較詳細地介紹了整個流程,方便大家理解整個mtd框架和nand flash驅動。
【Nand flash驅動工作原理】
在介紹具體如何寫Nand Flash驅動之前,我們先要了解,大概的,整個系統,和Nand Flash相關的部分的驅動工作流程,這樣,對於後面的驅動實現,才能更加清楚機制,才更容易實現,否則就是,即使寫完了程式碼,也還是沒搞懂系統是如何工作的了。
讓我們以最常見的,Linux核心中已經有的三星的Nand Flash驅動,來解釋Nand Flash驅動具體流程和原理。
此處是參考2.6.29版本的Linux原始碼中的driversmtd
ands3c2410.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)
“, 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.
http://www.linux-mtd.infradead.org/index.html
相關文章
- Linux驅動實踐:如何編寫【 GPIO 】裝置的驅動程式?Linux
- Linux驅動開發筆記(一):helloworld驅動原始碼編寫、makefile編寫以及驅動編譯Linux筆記原始碼編譯
- 如何編寫一個簡單的Linux驅動(三)——完善裝置驅動Linux
- Linux RN6752 驅動編寫Linux
- linux裝置驅動編寫入門Linux
- linux裝置驅動編寫基礎Linux
- linux 觸控式螢幕驅動編寫Linux
- Linux網路驅動程式編寫(四)(轉)Linux
- Linux網路驅動程式編寫(三)(轉)Linux
- Linux網路驅動程式編寫(二)(轉)Linux
- Linux網路驅動程式編寫(一)(轉)Linux
- 【ARM-WINCE 】 WinCE中nandflash驅動開發介紹NaN
- usb驅動程式初步編寫
- Linux系統核心模組和驅動的編寫(轉)Linux
- 04_Linux下把驅動編譯進核心Linux編譯
- Linux驅動開發筆記(三):基於ubuntu的驅動、makefile編寫以及編譯載入流程Linux筆記Ubuntu編譯
- NandFlash驅動除錯筆記(K9F1G08NaN除錯筆記
- 入門文章:教你學會編寫Linux裝置驅動(轉)Linux
- 如何編寫一個簡單的Linux驅動(二)——裝置操作集file_operationsLinux
- Window下 Qt 編譯MySQL驅動QT編譯MySql
- 編寫軟體動態載入NT式驅動
- Linux下如何用GCC編譯動態庫LinuxGC編譯
- Linux MIPI 攝像頭驅動框架編寫(RN6752解碼晶片)Linux框架晶片
- 1.7.5_NandFlash的擦除與燒寫_PNaN
- 淺談 Linux 下 Makefile 編寫Linux
- Linux下的硬體驅動——USB裝置(下)(驅動開發部分)(轉)Linux
- 如何向 Linux 核心提交驅動Linux
- 用 Delphi 編寫 VxD 裝置驅動程式(轉) (轉)
- linux下靜態庫、動態庫編譯及makefile書寫Linux編譯
- Asahi Lina講述了使用Rust編寫Linux驅動程式的經驗RustLinux
- linux驅動之LED驅動Linux
- 【linux】驅動-4-LED晶片手冊分析Linux晶片
- LINUX下的裝置驅動程式 (轉)Linux
- Windows95的裝置驅動程式的編寫 (轉)Windows
- Linux下的硬體驅動——USB裝置(上)(驅動配置部分)(轉)Linux
- windows下如何使用QT編寫dll程式WindowsQT
- 在LINUX下編寫程式並執行Linux
- Linux驅動模組載入方法和如何判斷Linux驅動是否載入成功Linux