【簡介】如何編寫linux下nand flash驅動
Version: V0.1
Time:10/06/2008
Author:[email][email protected][/email]
【編寫驅動之前要了解的知識】
1.硬件方面:
【Flash的種類】
Flash主要分nand flash和nor flash
除了網上最流行的這個解釋之外:
我再多說幾句,nor的成本相對高,比較適合應用於存儲少量的代碼。
Nand flash相對成本低,因此可以用來存儲大量的數據,其在嵌入式系統中的作用,相當於PC上的硬盤,用於存儲大量數據。
Nor flash,有類似於dram之類的地址總線,因此可以直接和CPU相連,CPU可以直接通過地址總線對nor flash進行訪問,而nand flash沒有這類的總線,只有IO接口,只能通過IO接口發送命令和地址,對nand flash內部數據進行訪問。相比之下,nor flash就像是並行訪問,nand flash就是串行訪問,所以相對來說,前者的速度更快些。
但是由於物理製程/製造方面的原因,導致nor 和nand在一些具體操作方面的特性不同:
Nand 和 Nor Flash 的區別
|
NOR |
NAND |
(備註) |
接口 |
總線 |
I/O接口 |
這個兩者最大的區別 |
單個cell大小 |
大 |
小 |
|
單個Cell成本 |
高 |
低 |
|
讀耗時 |
快 |
慢 |
|
單字節的編程時間 |
快 |
慢 |
|
多字節的編程時間 |
慢 |
快 |
|
擦除時間 |
慢 |
快 |
|
功耗 |
高 |
低,但是需要額外的RAM |
|
是否可以執行代碼 |
是 |
不行, 但是一些新的芯片,可以在第一頁之外執行一些小的loader |
|
位反轉Bit twiddling |
幾乎無限制 |
1-3次,也稱作 “部分頁編程限制” |
也就是數據錯誤了? |
在芯片出廠時候是否允許壞塊 |
不允許 |
允許 |
|
【Nand Flash的種類】
具體再分,又可以分爲
1)Bare NAND chips:裸片,單獨的nand 芯片
2)SmartMediaCards: =裸片+一層薄塑料,常用於數碼相機和MP3播放器中。之所以稱smart,是由於其軟件smart,而不是硬件本身有啥smart之處。^_^
3)DiskOnChip:裸片+glue logic,glue logic=硬件ECC產生器+用於靜態的nand 芯片控制的寄存器+直接訪問一小片地址窗口,那塊地址中包含了引導代碼的stub樁,其可以從nand flash中拷貝真正的引導代碼。
【Nand flash的特點】
Nand flash的操作,和其他一些常見的設備,如硬盤等,不同,其有自己特殊的方式。
其特殊就在於:
1.Nand flash的最小單位是頁page,而不是其他很多設備所說的位bit。
2.寫入數據之前必須先進行擦除erase操作
3.寫的時候,最小單位是頁page,對也進行寫操作,也稱作“頁編程”,page programming
4.擦除的最小單位是塊block
5.由於物理特性,容易出錯,所以無論是讀還是寫,都要採取檢測和校驗,即EDC。
6.nand flash出廠時候,就有一定壞的塊block,成爲換塊,並且做了一定標記。
7.nand flash中有個額外的空間,叫做spare area/oob
【spare area/oob】
Nand由於最初硬件設計時候考慮到,額外的錯誤校驗等需要空間,專門對應每個頁,額外設計了叫做spare area空區域,在其他地方,比如jffs2文件系統中,也叫做oob(out of band)數據。
其具體用途,總結起來有:
1. 標記是否是壞快
2. 存儲ECC數據
3. 存儲一些和文件系統相關的數據,如jffs2就會用到這些空間存儲一些特定信息
【常見Nand Flash的大小及參數】
常見的nand flash 的大小,由最開始的小於256M,到現在的常見的1G,2G,甚至更大。
以前的nand flash的
Pagesize頁大小,多爲512B+16B的oob,block大小爲64*(512B+16B)=32KB+1KB
現在目前市場上見到的,絕大多數,都是新的nand falsh,其Pagesize頁大小多爲2KB+64B的oob,block大小多爲64pages頁=64*(2K+64B)=128KB+4KB,一個nand flash中的芯片,一般含有4096個塊,比如samsung的K9F4G08U0M,所以這個nand flash大小就是
4096 Blocks = 4096 * 64 *(2K+64B)=512MB
即:
1 Page = (2K + 64)Bytes
1 Block = (2K + 64)B x 64 Pages
= (128K + 4K) Bytes
1 Device = (2K+64)B x 64Pages x 4,096 Blocks
= 4,224 Mbits =512MB
【Nand flash工作原理】
所謂工作原理,其實也就是對應對其如何操作的。
還是以上面提到的samsung的K9F4G08U0M的nand flash爲例,簡單描述如下:
1.nand flash定義了一些引腳,使得你可以發送命令過去,實現對nand flash的操控:
上面這些引腳,需要解釋的主要是
CLE,使能,只有使能有效,你才能進行後續的操作。只能選中了這個nand芯片,才能進行後續的讀寫。
ALE,在發送地址時,要鎖住地址總線,纔可以操作。
RE,WE,在讀寫之前,要對應的引腳有效,纔可以進行讀寫的。
其他見解釋皆可。
所有如上的引腳,都是驅動中,通過發送命令,具體內部控制邏輯去實現的。
而驅動開發者要關心的,是何時去發送對應的命令。具體想要實現一操作,要何時發送什麼命令才能實現,要繼續看下面的datasheet中的解釋:
如果想要對nand flash操作,就要根據datasheet中的規範進行,比如對頁的寫操作/寫編程要根據這個順序去操作:
其具體的細節,包含了哪些操作,可以看這個時序圖:
從上圖可以看出來,要先發80h的命令,再繼續後面的操作。
這裏的80h命令的含義,參考這個圖表:
從這個圖表,可以看到,80h就是page program的第一個命令。
而時序圖中的先兩個col列地址,後三個row行地址,是根據下面這個圖得來的:
此時才能定位到你所要操作的頁,才能進行寫操作。
再多說幾句,上圖表示了,如果想要對這個nand flash操作,讀或寫,需要進行3次列地址和三次的行地址,才能定位到你所要操作的地方,才能讀寫。同時顯示了,每次每個位,對應代表的地址位。
此處的2個列地址和3個行地址,是由硬件設計決定的。
老的nand flash由於每個頁只有512B,所以,最後的操作,只需要1個列地址和3個行地址就可以定位了。感興趣的可以去網上下載samsung一些老的nand flash,可以對比着看,更明白些。
而每個操作,比如上面的寫操作,都是需要一定時間的,其具體硬件操作所需的時間,見這個圖:
此處可以看到,寫一個頁,即頁編程,一般需要200us,最大需要700us,所以驅動開發者在開發的時候,要知道這個細節,要等待對應的時間,再去判斷,是否寫操作順利完成了,才能接下來繼續後面其他的操作的。
上述的具體代碼實現,可以去看
drivers\mtd\nand\nand_base.c中的nand_command_lp()函數中的代碼:
/* Command latch cycle */
//對命令鎖存,然後才能發送命令
chip->cmd_ctrl(mtd, command & 0xff,
NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
if (column != -1 || page_addr != -1) {
int ctrl = NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE;
//先發送2次col地址,構成整個列地址
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
if (chip->options & NAND_BUSWIDTH_16)
column >>= 1;
chip->cmd_ctrl(mtd, column, ctrl);
ctrl &= ~NAND_CTRL_CHANGE;
chip->cmd_ctrl(mtd, column >> 8, ctrl);
}
//再發送3次row地址,構成整個行地址
if (page_addr != -1) {
chip->cmd_ctrl(mtd, page_addr, ctrl);
chip->cmd_ctrl(mtd, page_addr >> 8,
NAND_NCE | NAND_ALE);
/* One more address cycle for devices > 128MiB */
if (chip->chipsize > (128 << 20))
chip->cmd_ctrl(mtd, page_addr >> 16,
NAND_NCE | NAND_ALE);
}
}
這樣就可以定位到我們要操作的位置,進行操作了。
2.軟件方面:
Linux驅動原理
具體內部很多實現,已經包含在drivers\mtd\nand\nand_base.c中了
【nand flash驅動加載識別nand類型過程】
在驅動加載的時候,會去調用:
nand_get_flash_type()
其中,就會對nand的類型和其他相關參數進行檢查。
1) 選中對應設備,如果此時只有一個nand 芯片,則此步可以省略
/* Select the device */
chip->select_chip(mtd, 0);
x
2) 發讀命令,去讀取設備類型代碼
/* Send the command for reading device ID */
chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
3) 判斷是哪個廠商的,哪個類型的flash
/* Read manufacturer and device IDs */
*maf_id = chip->read_byte(mtd);
dev_id = chip->read_byte(mtd);
4) 在事先已經定義好的nand flash類型中查找屬於何種廠商和型號
/* Lookup the flash id */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (dev_id == nand_flash_ids[i].id) {
type = &nand_flash_ids[i];
break;
}
}
5) 繼續判斷具體nand flash的各個參數,包括
芯片信息,Pagesize頁大小,oobsize即oob的大小,blocksize塊大小,buswidth總線寬度是8位還是16位。
如果頁大小不是之前老的nand的512B,而是新的nand的2K或更大,則後面對應的發送給nand flash命令的的時候,調用的函數就由nand_command()變成nand_command_lp()了。
後者主要比前者多一發個命令:
chip->cmd_ctrl(mtd, column >> 8, ctrl);
即,多發一個列地址命令。
因爲大頁面(>2KB)的尋址需要2次column,而小頁面(512B)只需要1次的列地址。
具體可以參考nand flash的datasheet。
6) 接着會做一些其他初始化操作,包括最後調用nand_set_defaults()去實現的默認函數的掛載,如果你的nand flash驅動沒有實現的話,就是掛載默認的了。
【模塊加載原理】
這個內容太大,此處只是簡單說說。
驅動加載的功能主要是probe函數實現的,主要去識別設備的類型和各個參數,並且爲設備的使用進行正常的初始化。
對應卸載時候執行的remove函數,施放對應的,之前申請的一些資源。
【MTD設備】
在Linux下,將nand Flash等設備歸屬到MTD設備下進行統一管理。
Mtd,即memory technology deveice,即將nand看出是存儲設備來管理。
之所以會這麼說和這麼做,是因爲前面提高的nand flash和普通硬盤等設備的特殊性:
IO接口,最小單位是頁,寫前需擦除等,導致了,不能像平常對待硬盤等操作一樣去操作nand flash,只能採取一些特殊方法,這就誕生了mtd設備的統一抽象層,將nand flash,nor flash和其他類型的flash等設備,統一抽象成mtd設備來管理,根據這些設備的特點,上層實現了常見的操作函數封裝,底層具體的內部實現,就需要驅動設計者自己來實現了。
MTD設備和硬盤設備之前的區別
HARD drives |
MTD device |
連續的扇區 |
連續的可擦除塊 |
扇區都很小(512B,1024B) |
可擦除塊比較大 (32KB,128KB) |
主要通過兩個操作對其維護操作:讀扇區,寫扇區 |
主要通過三個操作對其維護操作:從擦除塊中讀,寫入擦除塊,擦寫可擦除塊 |
壞快被重新映射,並且被硬件隱藏起來了(至少是在如今常見的LBA硬盤設備中是如此) |
壞的可擦除塊沒有被隱藏,軟件中要處理對應的壞塊問題。 |
HDD扇區沒有擦寫壽命超出的問題。 |
可擦除塊是有擦除次數限制的,大概是104-105次. |
【Linux下nand flash驅動編寫步驟簡介】
1. 瞭解硬件的nand flash的各個參數和工作原理
具體參考nand flash的datasheet,主要包括,自己nand flash的廠商,型號等。
Nand flash的頁大小,oob大小,塊大小,位寬8bit還是16bit。
工作原理,上面已經做了一定描述,不清楚的,可以參考datasheet,多看看,就會明白很多。
2. 按照linux下驅動編寫規範編寫nand flash驅動,
可以參考其他已經有的驅動,比如內核源碼中已經有的
drivers\mtd\nand\s3c2410.c
就是個很好的例子。
自己以其爲模板,實現自己板子的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,
},
};
中的
XXX_nand_probe函數
XXX_nand_remove函數
XXX_nand_enable_hwecc,如果支持硬件ecc的話。
對nand flash的讀寫,這兩個函數,實現了對nand的具體操作。
【Linux下Nand Flash驅動編寫簡單步驟】
軟件和硬件知識,都已經瞭解的話,由於上層的linux的 mtd框架中,已經完全封裝好了,對nand flash的write page,write oob等相關函數的實現,那麼剩下的只是相對來說已經是很少量的,關於nand 驅動具體內部操作方面的工作:
1.初始化
先是在nand 芯片初始化的時候,對其
XXX_nand_init_chip()
給對應的芯片chip賦給對應的
XXX_nand_read_buf和XXX_nand_write_buf等函數:
chip->cmd_ctrl = XXX_nand_hwcontrol;
chip->dev_ready = XXX_nand_devready;
chip->read_buf = XXX_nand_read_buf;
chip->write_buf = XXX_nand_write_buf;
以實現後續的對nand芯片的操作。
然後根據ecc類型,賦給對應的ecc的校驗與糾錯函數:
chip->ecc.hwctl = XXX_nand_enable_hwecc;
chip->ecc.calculate = XXX _nand_calculate_ecc;
3. 實現上面提到的對應的各個函數,關於如何實現,參考一下其他nand驅動,就會理解很多了。
4. 驅動測試,參考具體的 ldd3(Linux Device Driver version 3)的測試相關部分內容。
說得很亂,希望對大家有些幫助。