天天看點

【詳解三】Nand Flash 編寫驅動之前要了解的知識

1.2.16. Nand Flash的一些進階特性

1.2.16.1. Nand Flash的Unique ID

1.2.16.1.1. 什麼是Unique ID唯一性辨別

Unique ID,翻譯為中文就是,獨一無二的ID,唯一性辨別。

很明顯,這個Unique ID是為了用來識别某些東西的,每一個東西都擁有一個獨一無二的辨別資訊。

在Nand Flash裡面的Unique ID,主要是某個ID資訊,保證每個Nand Flash都是獨一無二的。主要用于其它的使用Nand Flash的使用者,根據此unique id去做加密等應用,實作某些安全方面的應用。

簡而言之,就是用Nand Flash的Unique ID來實作安全相關的應用,比如加密,版權保護等等。

1.2.16.1.2. 不同Nand Flash廠商的對Unique ID的不同的實作方法

此處,繼續解釋之前,還要再次贅述一下:

目前Nand Flash的廠家有samsung,Toshiba,Intel, Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等。

由于前面所說的Nand Flash的規範之争,即Toshiba & Samsung和Intel + 其它廠商(Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等)之争,導緻對于Unique ID這麼個小的功能點(feature),不同的方面,弄出了不同的實作(做法)。

下面就來解釋一下各個廠家關于Unique ID的實作方法,以及如何讀取對應的Unique ID:

1.2.16.1.2.1. Toshiba東芝的Nand的Unique ID

網上找到一個datasheet:

Toshiba TH58NS512DC

http://datasheet.elcodis.com/pdf/11/23/112371/th58ns512dc-to51y.pdf

中有提到:

P1:“The TH58NS512DC is a SmartMediaTM with ID and each device has 128 bit unique ID number embedded in the device. This unique ID number is applicable to image files, music files, electronic books, and so on where copyright protection is required.”即每個Toshiba的TH58NS512DC中,都有一個128 bit=16 byte的Unique ID,可用于圖檔,音樂,電子書等應用中的版權保護。

P24:

圖 1.9. Toshiba的Unique ID

【詳解三】Nand Flash 編寫驅動之前要了解的知識

1.2.16.1.2.2. 讀取Toshiba的Nand的Unique ID

從上面可以看出,Toshiba的Nand中,關于Unique ID,是需要先通過普通的0x90,即Read ID的指令,去讀取Nand的ID,找到第三個位元組(3rd byte),然後判斷其是否是0xA5,如果是0xA5,然後才能确定此Nand裡面是有Unique ID的,然後才有去讀取Unique ID這一說。

而關于如何獨缺Unique ID,則需要和自己去聯系Toshiba,和其簽訂NDA協定後,才可得知讀取Nand的Unique ID的方法。

1.2.16.1.3. Samsung三星的Nand的Unique ID

網上找到的某款三星的Nand的datasheet:

Samsung K9F5608U0B

http://hitmen.c02.at/files/docs/psp/ds_k9f5608u0b_rev13.pdf

6. Unique ID for Copyright Protection is available

- The device includes one block sized OTP (One Time Programmable), which can be used to increase system security or to provide identification capabilities. Detailed information can be obtained by contact with Samsung

即,Samsung的Nand的Unique ID,也和Toshiba的用途類似,也主要是用于版權保護,但是其實作卻不同。

Samsung的Unique ID的實作,是專門在Nand 裡面配備了一個OTP的Block,而此Nand晶片的Block大小是16KB。

而關于如何操作此OTP,即如何寫入資料和讀取資料,此處未說明。

欲知詳情,請聯系三星。

不過個人了解,應該和普通的block的操作類似,即普通的block,包含很多page,每個page的操作,有對應的page read,用對應的page read指令來讀取此特殊的OTP的block裡面的資料。

而此OTP的block裡面的資料是什麼,完全取決于自己最開始往裡面寫入了什麼資料。說白了就是,你根據自己需求,在你的産品出廠的時候,寫入對應的資料,比如該款産品的SN序列号等資料,然後自己在用page read讀取出相應資料後,自己解析,得到自己要的資訊,用于自己的用途,比如版權保護等。

1.2.16.1.3.1. 讀取Samsung的Nand的Unique ID

如前所述:

關于如何操作此OTP的block,即如何寫入資料和讀取資料,此處未說明。

即想要知道如何讀取Samsung的Nand的Unique ID,請自己去問三星。

1.2.16.1.4. 遵循ONFI規範的廠商的Nand的Unique ID

主要指的是Intel英特爾,Hynix海力士,Micron美光,Numonyx恒億,Spansion飛索等公司。

對應的Nand 的Unique ID的相關定義,ONFI的規範中都有,現簡要摘錄如下:

ONFI 2.2

http://onfi.org/wp-content/uploads/2009/02/ONFI%202_2%20Gold.pdf

ONFI規範中,在“5.7.1. Parameter Page Data Structure Definition”中,如圖:

圖 1.10. ONFI的參數頁資料結構定義

【詳解三】Nand Flash 編寫驅動之前要了解的知識

定義了一個page的資料,用于存儲對應的Nand的各種參數,其中,有一個第8個位元組的bit5==1的時候,表示支援“Read Unique ID”的指令,即說明此Nand晶片支援此指令,如果byte8的bit5==0,那麼說明不支援,也就沒法去讀Unique ID了。

1.2.16.1.4.1. 讀取遵循ONFI的廠商的Nand的Unique ID

如果經過上述判斷,此符合ONFI的Nand Flash支援Read Unique ID指令,次此時就可以通過該指令來讀取對應的Nand Flash的Unique ID了。

此Read Unique ID的詳細解釋為:

“ 5.8. Read Unique ID Definition

The Read Unique ID function is used to retrieve the 16 byte unique ID (UID) for the device. The unique ID when combined with the device manufacturer shall be unique.

The UID data may be stored within the Flash array. To allow the host to determine if the UID is without bit errors, the UID is returned with its complement, as shown in Table 47. If the XOR of the UID and its bit-wise complement is all ones, then the UID is valid.

即用Read Unique ID指令來讀取128bit=16位元組的Unique ID,但是呢,為了用于防止寫入的Unique ID有誤,是以在16位元組後面又添了個對應的補碼,即每位取反的結果,這樣前16位元組的Unique ID和後16位元組的Unique ID的補碼,構成了32位元組,算作一組,如下圖所示:

圖 1.11. ONFI中Unique ID的結構

【詳解三】Nand Flash 編寫驅動之前要了解的知識

To accommodate robust retrieval of the UID in the case of bit errors, sixteen copies of the UID and the corresponding complement shall be stored by the target. For example, reading bytes 32-63 returns to the host another copy of the UID and its complement. Read Status Enhanced shall not be used during execution of the Read Unique ID command

Figure 57 defines the Read Unique ID behavior. The host may use any timing mode supported by the target in order to retrieve the UID data.

而為了進一步防止出錯,将上面32位元組算一組,重複了16次,将這16個32位元組的資料,存在Nand Flash裡面,然後用Read Unique ID指令去讀取出來,取得其中某個32位元組即可,然後判斷前16位元組和後16位元組取反,如果結果所有位都是1,那麼結果即為16個0xFF,那麼說明Unique ID是正确的。否則說明有誤。

Read Unique ID的指令的詳細格式如下圖所示:

圖 1.12. ONFI中Read Unique ID指令的時序圖

【詳解三】Nand Flash 編寫驅動之前要了解的知識

即先發送0xED指令,再發送0x00位址,然後等待Nand Flash的busy狀态結束,就可以讀取出來的那16組的32個位元組了,然後用上面辦法去判斷,找到前16位元組和後16位元組異或得到結果全部16位元組都為0xFF,即說明得到了正确的Unique ID。至此,符合ONFI規範的Unique ID,就讀取出來了。

1.2.16.2. 片選無關(CE don’t-care)技術

很多Nand flash支援一個叫做CE don’t-care的技術,字面意思就是,不關心是否片選。

對此也許有人會問了,如果不片選,那還能對其操作嗎?答案就是,這個技術,主要用在當時是不需要選中晶片,但是晶片内部卻仍可以繼續操作的這些情況:在某些應用,比如錄音,音頻播放等應用中,外部使用的微秒(us)級的時鐘周期,此處假設是比較少的2us,在進行讀取一頁或者對頁程式設計時,是對Nand Flash操作,這樣的串行(Serial Access)通路的周期都是20/30/50ns,都是納秒(ns)級的,此處假設是50ns,當你已經發了對應的讀或寫的指令之後,接下來隻是需要Nand Flash内部去自己操作,将資料讀取除了或寫入進去到内部的資料寄存器中而已,此處,如果可以把片選取消,CE#是低電平有效,取消片選就是拉高電平,這樣會在下一個外部指令發送過來之前,即微秒量級的時間裡面,即2us-50ns≈2us,這段時間的取消片選,可以降低很少的系統功耗,但是多次的操作,就可以在很大程度上降低整體的功耗了。

總的來說就是:由于某些外部應用所需要的通路Nand Flash的頻率比較低,而Nand Flash内部操作速度比較快,是以在針對Nand Flash的讀或寫操作的大部分時間裡面,都是在等待外部指令的輸入,同時卻選中晶片,産生了多餘的功耗,此“不關心片選”技術,就是在Nand Flash的内部的相對快速的操作(讀或寫)完成之後,就取消片選,以節省系統功耗。待下次外部指令/資料/位址輸入來的時候,再選中晶片,即可正常繼續操作了。這樣,整體上,就可以大大降低系統功耗了。

【詳解三】Nand Flash 編寫驅動之前要了解的知識
提示
  1. 如果想要操作硬體Nand Flash晶片,先要将對應的CE#(低有效)片選信号拉低,選中該晶片,然後才能做接下來的讀寫操作所要做的發指令,發資料等動作。
  2. Nand Flash的片選與否,功耗差别會有很大。如果資料沒有記錯的話,我之前遇到我們系統裡面的Nand Flash的片選,大概有5個mA的電流輸出呢,也許你對5mA沒太多概念,給你說個資料你就知道了:當時為了針對MP3播放功耗進行優化,整個系統優化之後的待機功耗,也才10個mA左右的,是以節省5mA已經算是很不錯的功耗優化了。

1.2.16.3. 帶EDC的拷回操作以及Sector的定義(Copy-Back Operation with EDC & Sector Definition for EDC)

Copy-Back功能,簡單的說就是,将一個頁的資料,拷貝到另一個頁。

如果沒有Copy-Back功能,那麼正常的做法就是,先要将那個頁的資料拷貝出來放到記憶體的資料buffer中,讀出來之後,再用寫指令将這頁的資料,寫到新的頁裡面。

而Copy-Back功能的好處在于,不需要用到外部的存儲空間,不需要讀出來放到外部的buffer裡面,而是可以直接讀取資料到内部的頁寄存器(page register)然後寫到新的頁裡面去。

而且,為了保證資料的正确,要硬體支援EDC(Error Detection Code)的,否則,在資料的拷貝過程中,可能會出現錯誤,并且拷貝次數多了,可能會累積更多錯誤。

而對于錯誤檢測來說,硬體一般支援的是512位元組資料,對應有16位元組用來存放校驗産生的ECC數值,而這512位元組一般叫做一個扇區。對于2K+64位元組大小的頁來說,按照512位元組分,分别叫做A,B,C,D區,而後面的64位元組的oob區域,按照16位元組一個區,分别叫做E,F,G,H區,對應存放A,B,C,D資料區的ECC的值。

Copy-Back程式設計的主要作用在于,去掉了資料串行讀取出來,再串行寫入進去的時間,是以,而這部分操作,是比較耗時的,是以此技術可以提高程式設計效率,提高系統整體性能。

1.2.16.4. 多片同時程式設計(Simultaneously Program Multi Plane)

對于有些新出的Nand Flash,支援同時對多個片進行程式設計,比如上面提到的三星的K9K8G08U0A,内部包含4片(Plane),分别叫做Plane0,Plane1,Plane2,Plane3。.由于硬體上,對于每一個Plane,都有對應的大小是2048+64=2112位元組的頁寄存器(Page Register),使得同時支援多個Plane程式設計成為可能。 K9K8G08U0A支援同時對2個Plane進行程式設計。

不過要注意的是,隻能對Plane0和Plane1或者Plane2和Plane3,同時程式設計,而不支援Plane0和Plane2同時程式設計。

1.2.16.5. 交錯頁程式設計(Interleave Page Program)

多片同時程式設計,是針對一個chip裡面的多個Plane來說的,

而此處的交錯頁程式設計,是指對多個chip而言的。

可以先對一個chip,假設叫chip1,裡面的一頁進行程式設計,然後此時,chip1内部就開始将資料一點點寫到頁裡面,就出于忙的狀态了,而此時可以利用這個時間,對出于就緒狀态的chip2,也進行頁程式設計,發送對應的指令後,chip2内部也就開始慢慢的寫資料到存儲單元裡面去了,也出于忙的狀态了。此時,再去檢查chip1,如果程式設計完成了,就可以開始下一頁的程式設計了,然後發完指令後,就讓其内部慢慢的程式設計吧,再去檢查chip2,如果也是程式設計完了,也就可以進行接下來的其他頁的程式設計了。如此,互動操作chip1和chip2,就可以有效地利用時間,使得整體程式設計效率提高近2倍,大大提高Nand Flash的程式設計/擦寫速度了。

1.2.16.6. 随機輸出頁内資料(Random Data Output In a Page)

在介紹此特性之前,先要說說,與Random Data Output In a Page相對應的是,普通的,正常的,sequential data output in a page。

正常情況下,我們讀取資料,都是先發讀指令,然後等待資料從存儲單元到内部的頁資料寄存器中後,我們通過不斷地将RE#(Read Enale,低電平有效)置低,然後從我們開始傳入的列的起始位址,一點點讀出我們要的資料,直到頁的末尾,當然有可能還沒到頁位址的末尾,就不再讀了。所謂的順序(sequential)讀取也就是,根據你之前發送的列位址的起始位址開始,每讀一個位元組的資料出來,内部的資料指針就加1,移到下個位元組的位址,然後你再讀下一個位元組資料,就可以讀出來你要的資料了,直到讀取全部的資料出來為止。

而此處的随機(random)讀取,就是在你正常的順序讀取的過程中,先發一個随機讀取的開始指令0x05指令,再傳入你要将内部那個資料指針定位到具體什麼位址,也就是2個cycle的列位址,然後再發随機讀取結束指令0xE0,然後,内部那個資料位址指針,就會移動到你所制定的位置了,你接下來再讀取的資料,就是從那個制定位址開始的資料了。

而Nand Flash資料手冊裡面也說了,這樣的随機讀取,你可以多次操作,沒限制的。

請注意,上面你所傳入的位址,都是列位址,也就是頁内位址,也就是說,對于頁大小為2K的Nand Flash來說,所傳入的位址,應該是小于2048+64=2112的。

不過,實際在Nand Flash的使用中,好像這種用法很少的。絕大多數,都是順序讀取資料。

1.3. 軟體方面

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

1.3.1. Nand Flash相關規範 – ONFI和LBA

在介紹Nand Flash的軟體細節方面之前,先來介紹一下Nand Flash的兩個相關的規範:ONFI和LBA。

1.3.1.1. ONFI是什麼

ONFI規範,即Open Nand Flash Interface specification。

ONFI是Intel主導的,其他一些廠家(Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等)參與制定的,統一了Nand Flash的操作接口。

所謂操作接口,就是那些對Nand Flash操作的指令等内容。

而所謂統一,意思是之前那些Nand Flash的操作指令等,都是各自為政,雖然大多數常見的Nand Flash的操作,比如page read的指令是0x00,0x30,page write的指令是0x80,0x10等,但是有些指令相關的内容,很特别且很重要的一個例子就是,每個廠家的Nand Flash的read id的指令,雖然都是0x90,但是讀取出來的幾個位元組的含義,每個廠家定義的都不太一樣。

是以,才有統一Nand Flash的操作接口這一說。

ONFI規範,官網可以下載下傳的到:

http://onfi.org/specifications/

比如:

ONFI 2.2 Spec

http://onfi.org/wp-content/uploads/2009/02/ONFI%202_2%20Gold.pdf

ONFI規範中定義的Nand Flash的指令集合為:

圖 1.13. ONFI中的Nand Flash的指令集合

【詳解三】Nand Flash 編寫驅動之前要了解的知識

可以看到,其中常見的一些指令,比如

  1. page read(0x00,0x30)
  2. page write(0x80,0x10)
  3. block erase(0x60,0xD0)
  4. Reset(0xFF)

等等指令,都是和普通的Nand Flash的指令是一樣的,而額外多出一些指令,比如Read Unique ID(0xED)等指令,是之前某些Nand Flash指令所不具有的。

如此,定義了Nand Flash的操作的指令的集合以及發送對應指令所遵循的時序等内容。

1.3.1.1.1. ONFI Block Abstracted NAND

ONFI還定義了另外一個規範:

ONFI Block Abstracted Nand Specification

http://onfi.org/wp-content/uploads/2009/02/BA_NAND_rev_1_1_Gold.pdf

即ONFI LBA Nand,簡單說就是,邏輯塊尋址的Nand。其含義和Toshiba的LBA,基本沒有太多差別。

1.3.1.1.2. ONFI的好處

ONFI規範定義了之後,每家廠商的Nand Flash,隻要符合這個ONFI規範,然後上層Nand Flash的軟體,就可以統一隻用一種了,換句話說,我寫了一份Nand Flash的驅動後,就可以操作所有和ONFI相容的Nand Flash了,整個Nand Flash的相容性,上層軟體的相容性,互操作性,就大大提高了。

而且,同樣的,由于任何規範在定義的時候,都會考慮到相容性和擴充性,ONFI也不例外。針對符合ONFI規範的,寫好的軟體,除了可以操作多家與ONFI相容的Nand Flash之外,而對于以後出現的新的技術,新制程的Nand Flash,隻要符合ONFI規範,也同樣可以支援,可以在舊的軟體下工作,而不需要由于Nand Flash的更新換代,而更改上層軟體和驅動,這個優勢,由于對于将Nand Flash晶片內建到自己系統中的相關開發人員來說,是個好消息。

1.3.1.2. LBA規範是什麼

LBA Nand Flash,Logical Block Address,邏輯塊尋址的Nand Flash,是Nand Flash大廠之一的Toshiba,自己獨立設計出來的新一代的Nand Flash的規範。

之是以叫做邏輯塊尋址,是相對于之前常見的,普通的Nand Flash的實體塊的尋址來說的。常見的Nand Flash,如果要讀取和寫入資料,所用的對應的位址是對應的:block位址+block内的Page位址+Page内的偏移量 = 絕對的實體位址,

此實體塊尋址,相對來說有個缺點,那就是,由于之前提到的Nand Flash會出現使用過程中出現壞塊,是以,遇到這樣的壞塊,首先壞塊管理要去将此壞塊标記,然後将壞塊的資料拷貝到另一個好的block中,再繼續通路新的block。

而且資料讀寫過程中,還要有對應的ECC校驗,很多情況下,也都是軟體來實作這部分的工作,即使是硬體的ECC校驗,也要寫少量的軟體,去操作對應寄存器,讀取ECC校驗的結果,當然别忘了,還有對應的負載平衡等工作。

如此的這類的壞塊管理工作,對于軟體來說,很是繁重,而且整個系統實作起來也不是很容易,是以,才催生了一個想法,是否可以把ECC校驗,負載平衡,壞塊管理,全部都放到硬體實作上,而對于軟體來說,我都不關心,隻關心有多少個Block供我使用,用于資料讀寫。

針對于此需求,Toshiba推出了LBA邏輯塊尋址的Nand Flash,在Nand Flash存儲晶片之外,加了對應一個硬體控制權Controller,實作了上述的壞塊管理,ECC校驗,負載平衡等工作,這樣使得人家想要用你LBA的Nand Flash的人,去開發對應的軟體來驅動LBA Nand Flash工作,相對要做的事情,就少了很多,相對來說就是減輕了軟體系統內建方面的工作,提高了開發效率,縮短了産品上市周期。

LBA Nand,最早放出對應的樣片(sample)是在2006年8月。

網上找到一個LBA Nand Flash的簡介:

http://www.toshiba-components.com/prpdf/5678E.pdf

現早已經量産,偶在之前開發過程中,就用過其某款LBA的Nand Flash。

目前網上還找不到免費的LBA的規範。除非你搞開發,和Toshiba簽訂NDA協定後,才可以拿到對應的specification。

關于Toshiba LBA Nand規範,在此多說一點(參考附錄中:lba-core.c):

LBA Nand分為PNP,VFP和MDP三種分區:

  1. PNP主要用于存放Uboot等啟動代碼
  2. VFP主要用于存放uImage等核心代碼
  3. MDP主要用于存放使用者的資料,以及rootfs等内容

1.3.1.3. 為何會有ONFI和LBA

在解釋為何會有ONFI和LBA之前,先來個背景介紹:

目前Nand Flash的廠家有Samsung,Toshiba,Intel, Hynix,Micron,Numonyx,Phison ,SanDisk,Sony,Spansion等。

1.3.1.3.1. 技術層面的解釋

ONFI的出現,上面已經解釋過了,就是為了統一Nand Flash的接口,使得軟體相容性更好;

而LBA的出現,是為了減輕軟體方面對Nand Flash的各種管理工作。上面這些解釋,其實隻是技術上解釋。

1.3.1.3.2. 現實層面的解釋

而現實方面是,ONFI是Intel主導的,其他一些Nand Flash廠商參與制定出來的一套Nand Flash的規範,但是卻沒有得到Nand Flash的兩個大廠家的認可,一個是以第一廠商自居samsung,另一個是Nand Flash技術引導者的Toshiba。是以,可以算是在Nand Flash領域裡,老三帶着一幫小的,定了一個規範,但是老大和老二卻不買賬。是以,技術上的Nand的老大Toshiba聯手産量上的老大,自己去推出了另外一套規範LBA。

這可以稱得上是典型的規範之争吧。

1.3.1.4. ONFI和LBA的差別和聯系

總的來說,ONFI在對于舊的Nand Flash的相容上,都是相對類似的。

1.3.1.4.1. ONFI和LBA的差別

總的來說:

ONFI規範,更注重對于Nand Flash的操作接口方面的定義 + ONFI LBA Nand的定義,而Toshiba LBA規範,主要側重于LBA的Nand的定義。

1.3.1.4.2. ONFI和LBA的聯系

ONFI Block Abstracted NAND Specification,基本上和Toshiba的LBA,沒太多差別,隻是Toshiba的LBA規範中,又多了些其他模式和應用類别。

總的來說,可以這麼劃分:

ONFI = Nand Flash操作接口的統一 + ONFI的LBA Nand

Toshiba LBA = 等價于ONFI的LBA Nand + 多種模式和對應的不同應用

1.3.2. 記憶體技術裝置,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裝置來管理,根據這些裝置的特點,上層實作了常見的操作函數封裝,底層具體的内部實作,就需要驅動設計者自己來實作了。具體的内部硬體裝置的讀/寫/擦除函數,那就是你必須實作的了。

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

HARD drives MTD device
連續的扇區 連續的可擦除塊
扇區都很小(512B,1024B) 可擦除塊比較大 (32KB,128KB)
主要通過兩個操作對其維護操作:讀扇區,寫扇區 主要通過三個操作對其維護操作:從擦除塊中讀,寫入擦除塊,擦寫可擦除塊
壞快被重新映射,并且被硬體隐藏起來了(至少是在如今常見的LBA硬碟裝置中是如此) 壞的可擦除塊沒有被隐藏,軟體中要處理對應的壞塊問題
HDD扇區沒有擦寫壽命超出的問題 可擦除塊是有擦除次數限制的,大概是104-105次

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

關于mtd裝置驅動,感興趣的可以去參考附錄中MTD裝置的文章,該文章是比較詳細地介紹了整個MTD架構和流程,友善大家了解整個mtd架構和Nand Flash驅動。

1.3.2.1. Linux MTD中檢測不同類型Nand Flash的ID部分的代碼

關于nand flash,由于各個廠家的read id讀出的内容的定義,都不同,導緻,對于讀出的id,分别要用不同的解析方法,下面這段代碼,是我之前寫的,本來打算自己寫信去推薦到Linux MTD核心源碼的,不過後來由于沒搞懂具體申請流程,就放棄了。不過,後來,看到Linux的MTD部分更新了,加了和下面類似的做法。

此處隻是為了記錄下來,也算給感興趣的人一個參考吧。

檔案:

\linux-2.6.28.4\drivers\mtd\nand\nand_base.c

/*
 * Get the flash and manufacturer id and lookup if the type is supported
 */
static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
                          struct nand_chip *chip,
                          int busw, int *maf_id)
{
... ...
    chip->chipsize = (uint64_t)type->chipsize << 20;

    /* 針對不同的MLC和SLC的nand flash,添加了不同的解析其ID的方法 */
    /* Newer devices have all the information in additional id bytes */
    if (!type->pagesize) {
        int erase_bits, page_base, block_base, old_50nm, new_40nm;
        uint8_t id3rd, id4th, id5th, id6th, id7th;

        /* The 3rd id byte holds MLC / multichip data */
        chip->cellinfo = id3rd = chip->read_byte(mtd);
        /* The 4th id byte is the important one */
        id4th = chip->read_byte(mtd);
        id5th = chip->read_byte(mtd);
        id6th = chip->read_byte(mtd);
        id7th = chip->read_byte(mtd);
        /* printk(KERN_INFO " (ID:%02x %02x %02x %02x %02x %02x %02x) ",
            id1st, id2nd, id3rd, id4th, id5th, id6th, id7th); */

        if (nand_is_mlc(chip->cellinfo)) {
            /*
             * MLC:
             * 50nm has 5 bytes ID, further read ID will periodically output
             * 40nm has 6 bytes ID
             */

            /*
             * the 4th byte is not the same meaning for different manufature
            */
            if (NAND_MFR_SAMSUNG == *maf_id) {
                /* samsung MLC chip has several type ID meanings:
                (1)50nm serials, such as K9GAG08U0M
                (2)40nm serials, such as K9LBG08UXD
                */

                /* old 50nm chip will periodically output if read further ID */
                old_50nm = (id1st == id6th) && (id2nd == id7th);
                /* is 40nm or newer */
                new_40nm = id6th & 0x07;
                if ((!old_50nm) && new_40nm) {
                    /*
                     * Samsang
                     * follow algorithm accordding to datasheets of:
                     * K9LBG08UXD_1.3 (40nm), 
                     * ID(hex): EC D7 D5 29 38 41
                     * this algorithm is suitable for new chip than 50nm
                     * such as K9GAG08u0D, 
                     * ID(hex): EC D5 94 29 B4 41
                     */

                    int bit236;

                    /* Calc pagesize, bit0,bit1: page size */
                    page_base = (1 << 11);  /* 2KB */
                    mtd->writesize = page_base * (1 << (id4th & BIT01));
                    block_base = (1 << 17); /* 128 KB */
                    /* Calc block size, bit4,bit5,bit7: block size */
                    erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                    erase_bits |= (id4th >> 5) & BIT(2); /* get bit7 and combine them */
                    mtd->erasesize = block_base * (1 << erase_bits);
                    /* Calc oobsize, bit2,bit3,bit6: oob size */
                    bit236 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
                    bit236 |= (id4th >> 4) & BIT(2); /* get bit6 and combine them */
                    switch (bit236) {
                    case 0x01:
                        mtd->oobsize = 128;
                        break;
                    case 0x02:
                        mtd->oobsize = 218;
                        break;
                    default:
                        /* others reserved */
                        break;
                    }
                }
                else {
                    /*
                     * Samsang
                     * follow algorithm accordding to datasheets of:
                     * K9GAG08U0M (50nm)
                     * this algorithm is suitable for old 50nm chip
                     */

                    goto slc_algorithm;
                }
            }
            else if (NAND_MFR_TOSHIBA == *maf_id) {
                /*
                 * Toshiba
                 * follow algorithm guess from ID of TC58NVG3D1DTG00:
                 * Toshiba MLC TC58NVG3D1DTG00 1GB 8bit 1chip
                 * 4K+218 512K+27K 3.3V, (ID:98 D3 94 BA 64 13 42)
                 */
                int bit23;

                /* Calc pagesize, bit0,bit1: page size */
                page_base = (1 << 10);  /* 1KB */
                mtd->writesize = page_base * (1 << (id4th & BIT01));
                block_base = (1 << 16); /* 64 KB */
                /* Calc block size, bit4,bit5: block size */
                erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                mtd->erasesize = block_base * (1 << erase_bits);
                /* Calc oobsize, use spare/redundant area bit */
                bit23 = (id4th >> 2) & BIT01; /* get bit2,bit3 */
                switch (bit23) {
                case 0x01:
                    mtd->oobsize = 128;
                    break;
                case 0x02:
                    mtd->oobsize = 218;
                    break;
                default:
                    /* others reserved */
                    break;
                }
                /* Get buswidth information: x8 or x16 */
                busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
            }
            else if (NAND_MFR_MICRON == *maf_id) {
                /*
                 * Micron
                 * follow algorithm accordding to datasheets of:
                 * 29F32G08CBAAA
                 */
                int spare_area_size_bit;

                /* Calc pagesize, bit0,bit1: page size */
                page_base = (1 << 10);  /* 1KB */
                mtd->writesize = page_base * (1 << (id4th & 0x03));
                block_base = (1 << 16); /* 64 KB */
                /* Calc block size, bit4,bit5: block size */
                erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
                mtd->erasesize = block_base * (1 << erase_bits);
                /* Calc oobsize, use spare/redundant area bit */
                spare_area_size_bit = (id4th >> 2) & BIT(0);
                if (spare_area_size_bit) /* special oob */
                    mtd->oobsize = 218;
                else /* normal */
                    mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
                /* Get buswidth information: x8 or x16 */
                busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
            }
            else {
                /*
                 * Others
                 * FIXME: update follow algrithm,
                 * according to different manufacture's chip's datasheet
                 */

                goto slc_algorithm;
            }       
        }
        else {
            /*
             * SLC, only has 4 bytes ID, further read will output periodically, such as:
             * Hynix : HY27UG084G2M, only has 4 byte ID,
             * following read ID is periodically same as the 1st ~ 4th byte,
             * for HY27UG084G2M is : 0xAD 0xDC 0x80 0x15 0xAD 0xDC 0x80 0x15 ..... 
            */
slc_algorithm:
            /* Calc pagesize, bit0,bit1: page size */
            page_base = (1 << 10);  /* 1KB */
            mtd->writesize = page_base * (1 << (id4th & BIT01));
            block_base = (1 << 16); /* 64 KB */
            /* Calc block size, bit4,bit5: block size */
            erase_bits = (id4th >> 4) & BIT01; /* get bit4,bit5 */
            mtd->erasesize = block_base * (1 << erase_bits);
            /* Calc oobsize, use fixed ratio */
            mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
            /* Get buswidth information: x8 or x16 */
            busw = ((id4th >> 6) & BIT(0)) ? NAND_BUSWIDTH_16 : 0;
        }
    } else {
        /*
         * Old devices have chip data hardcoded in the device id table
         */
        mtd->erasesize = type->erasesize;
        mtd->writesize = type->pagesize;
        mtd->oobsize = mtd->writesize / NAND_PAGE_OOB_RATIO;
        busw = type->options & NAND_BUSWIDTH_16;
    }
    
    /*
    * 以上内容,主要是更加不同廠家的nand flash的datasheet,一點點總結出來的算法。
    * 最新的Linux的MTD部分,已經添加了類似如上部分的代碼。此處貼出來,僅供參考。
    */
    
    /*
     * Check, if buswidth is correct. Hardware drivers should set
     * chip correct !
     */
    if (busw != (chip->options & NAND_BUSWIDTH_16)) {
        printk(KERN_INFO "NAND device: Manufacturer ID:"
               " 0x%02x, Chip ID: 0x%02x (%s %s)\n", *maf_id,
               dev_id, nand_manuf_ids[maf_idx].name, mtd->name);
        printk(KERN_WARNING "NAND bus width %d instead %d bit\n",
               (chip->options & NAND_BUSWIDTH_16) ? 16 : 8,
               busw ? 16 : 8);
        return ERR_PTR(-EINVAL);
    }
... ...
}