基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析.doc_第1頁
基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析.doc_第2頁
基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析.doc_第3頁
基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析.doc_第4頁
基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析.doc_第5頁
已閱讀5頁,還剩56頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

基于MTD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析(一) 經(jīng)過UBOOT初步的移植,Linux內(nèi)核初步的移植,Linux內(nèi)核總線設(shè)備模型的分析,等一系列痛苦的折騰,目的就是想更好的來分析下NANDFLASH的驅(qū)動(dòng)。大概一共歷經(jīng)了半個(gè)月的時(shí)間,慢慢的對(duì)NANDFLASH驅(qū)動(dòng)程序有感覺了。一、MTD體系結(jié)構(gòu):Linux內(nèi)核提供MTD子系統(tǒng)來建立FLASH針對(duì)Linux的統(tǒng)一、抽象接口。MTD將文件系統(tǒng)與底層的FLASH存儲(chǔ)器進(jìn)行隔離。 引入MTD后Linux系統(tǒng)中對(duì)FLASH的設(shè)備驅(qū)動(dòng)分為4層 設(shè)備節(jié)點(diǎn):用戶在/dev目錄下使用mknod命令建立MTD字符設(shè)備節(jié)點(diǎn)(主設(shè)備號(hào)為90),或者M(jìn)TD塊設(shè)備節(jié)點(diǎn)(主設(shè)備號(hào)為31),使用該設(shè)備節(jié)點(diǎn)即可訪問MTD設(shè)備。MTD設(shè)備層:基于MTD原始設(shè)備層,系統(tǒng)將MTD設(shè)備可以定義為MTD字符(在/mtd/mtdchar.c中實(shí)現(xiàn),設(shè)備號(hào)90)和MTD塊設(shè)備(在/mtd/mtdblock.c中實(shí)現(xiàn),設(shè)備號(hào)31)。MTD原始設(shè)備層:MTD原始設(shè)備層由兩部分構(gòu)成,一部分是MTD原始設(shè)備的通用代碼,另一部分是各個(gè)特定Flash的數(shù)據(jù),如分區(qū)。 主要構(gòu)成的文件有: drivers/mtd/mtdcore.c支持mtd字符設(shè)備 driver/mtd/mtdpart.c支持mtd塊設(shè)備Flash硬件驅(qū)動(dòng)層:Flash硬件驅(qū)動(dòng)層負(fù)責(zé)對(duì)Flash硬件的讀、寫和擦除操作。MTD設(shè)備的Nor Flash芯片驅(qū)動(dòng)位于drivers/mtd/chips/子目錄下,Nand Flash芯片的驅(qū)動(dòng)則位于drivers/mtd/nand/子目錄下。 二、Linux內(nèi)核中基于MTD的NANDFLASH驅(qū)動(dòng)代碼布局:在Linux2.6.35內(nèi)核中,MTD源代碼放在driver/mtd目錄中,該目錄中包含chips、devices、maps、nand、onenand、lpdrr、tests和ubi八個(gè)子目錄。其中只有nand和onenand目錄中的代碼才與NAND驅(qū)動(dòng)相關(guān),不過nand目錄中的代碼比較通用,而onenand目錄中的代碼相對(duì)于nand中的代碼而言則簡化了很多,它是針對(duì)三星公司開發(fā)的另一類Flash芯片,即OneNAND Flash。本文我們需要關(guān)注的代碼是linux-2.6.35/drivers/mtd/nand目錄中,在該目錄中我們關(guān)心的文件如下:1、 nand_base.c:定義了NAND驅(qū)動(dòng)中對(duì)NAND芯片最基本的操作函數(shù)和操作流程,如擦除、讀寫page、讀寫oob等。當(dāng)然這些函數(shù)都只是進(jìn)行一些default的操作,若你的系統(tǒng)在對(duì)NAND操作時(shí)有一些特殊的動(dòng)作,則需要在你自己的驅(qū)動(dòng)代碼中進(jìn)行定義,然后Replace這些default的函數(shù)。2、 nand_bbt.c:定義了NAND驅(qū)動(dòng)中與壞塊管理有關(guān)的函數(shù)和結(jié)構(gòu)體。3、 nand_ids.c:定義了兩個(gè)全局類型的結(jié)構(gòu)體:struct nand_flash_dev nand_flash_ids 和struct nand_manufacturers nand_manuf_ids 。其中前者定義了一些NAND芯片的類型,后者定義了NAND芯片的幾個(gè)廠商。NAND芯片的ID至少包含兩項(xiàng)內(nèi)容:廠商ID和廠商為自己的NAND芯片定義的芯片ID。當(dāng)NAND驅(qū)動(dòng)被加載的時(shí)候,它會(huì)去讀取具體NAND芯片的ID,然后根據(jù)讀取的內(nèi)容到上述定義的nand_manuf_ids 和nand_flash_ids 兩個(gè)結(jié)構(gòu)體中去查找,以此判斷該NAND芯片是那個(gè)廠商的產(chǎn)品,以及該NAND芯片的類型。若查找不到,則NAND驅(qū)動(dòng)就會(huì)加載失敗,因此在開發(fā)NAND驅(qū)動(dòng)前必須事先將你的NAND芯片添加到這兩個(gè)結(jié)構(gòu)體中去(其實(shí)這兩個(gè)結(jié)構(gòu)體中已經(jīng)定義了市場(chǎng)上絕大多數(shù)的NAND芯片,所以除非你的NAND芯片實(shí)在比較特殊,否則一般不需要額外添加)。值得一提的是,nand_flash_ids 中有三項(xiàng)屬性比較重要,即pagesize、chipsize和erasesize,驅(qū)動(dòng)就是依據(jù)這三項(xiàng)屬性來決定對(duì)NAND芯片進(jìn)行擦除,讀寫等操作時(shí)的大小的。其中pagesize即NAND芯片的頁大小,一般為256、512或2048;chipsize即NAND芯片的容量;erasesize即每次擦除操作的大小,通常就是NAND芯片的block大小。4、 nand_ecc.c:定義了NAND驅(qū)動(dòng)中與softeware ECC有關(guān)的函數(shù)和結(jié)構(gòu)體,若你的系統(tǒng)支持hardware ECC,且不需要software ECC,則該文件也不需理會(huì)。上面這些內(nèi)容我是Copy別人的我覺得寫得太好了,因?yàn)橐婚_始我真的很迷茫,在nand目錄下有那么多的文件,到底哪個(gè)是值得我讀的.我真的不值得,讀了這個(gè)大神的博客后對(duì)NANDDLASH的驅(qū)動(dòng)我不再是那么的迷茫。 三、NANDFLASH的硬件特性要想讀懂后面Linux系統(tǒng)中對(duì)NANDFLASH硬件驅(qū)動(dòng)代碼,了解NANDFLASH的硬件特性這是再好不過的。1、NANDFLASH的內(nèi)部布局 2、Nand Flash的物理存儲(chǔ)單元的陣列組織結(jié)構(gòu)(以開發(fā)板上的K9F2G08為例) K9F2G08的大小是256M a)block:Block是Nand Flash的擦除操作的基本/最小單位,一片NANDFLASH(chip)由很多塊(block)組成,塊的大小一般是 128KB, 256KB,512KB,此處是 128KB。其他的小于 128KB 的,比如 64KB稱之為small block的Nand Flash。 b)page:page是讀寫操作的最小單位,每一個(gè)block里面包又含了許多page(頁),每個(gè)頁的大小,對(duì)于現(xiàn)在常見的Nand Flash多數(shù)是2KB,最新的Nand Flash的是4KB、8KB等,這類的頁大小大于2KB的NandFlash,被稱作 big block的 Nand Flash,對(duì)應(yīng)的發(fā)讀寫命令地址,一共 5個(gè)周期(cycle),而老的 Nand Flash,頁大小是 256B,512B,這類的 Nand Flash被稱作 small block的nandflash地址周期只有4個(gè)。 c)oob:每一個(gè)頁,對(duì)應(yīng)還有一塊區(qū)域,叫做空閑區(qū)域(spare area)/冗余區(qū)域(redundant area)而Linux 系統(tǒng)中,一般叫做 OOB(Out Of Band),這個(gè)區(qū)域,是最初基于Nand Flash的硬件特性:數(shù)據(jù)在讀寫時(shí)候相對(duì)容易錯(cuò)誤,所以為了保證數(shù)據(jù)的正確性,必須要有對(duì)應(yīng)的檢測(cè)和糾錯(cuò)機(jī)制,此機(jī)制被叫做 EDC(Error Detection Code)/ECC(Error Code Correction, 或者 Error Checking and Correcting),所以設(shè)計(jì)了多余的區(qū)域,用于放置數(shù)據(jù)的校驗(yàn)值。 Oob 的讀寫操作,一般是隨著頁的操作一起完成的,即讀寫頁的時(shí)候,對(duì)應(yīng)地就讀寫了 oob。 關(guān)于 oob具體用途,總結(jié)起來有: 1、 標(biāo)記是否是壞快 2、存儲(chǔ)ECC數(shù)據(jù) 3、存儲(chǔ)一些和文件系統(tǒng)相關(guān)的數(shù)據(jù)。如 jffs2 就會(huì)用到這些空間存儲(chǔ)一些特定信息, 4、而yaffs2 文件系統(tǒng),會(huì)在 oob中,存放很多和自己文件系統(tǒng)相關(guān)的信息。 3、K9F2G08的引腳定義 IO7IO0:用于輸入地址/數(shù)據(jù)/命令,輸出數(shù)據(jù)。 CLE:命令鎖存使能位,在發(fā)送命令之前要先將模式寄存器中設(shè)置CLE使能(高電平有效)。 ALE:地址鎖存使能位,在發(fā)送地址之前,要先將模式寄存器中設(shè)置ALE使能(高電平有效)。 CE:(nFCE)芯片的片選信號(hào),操作nandflash前應(yīng)該拉低該位使之選中該芯片。 RE:(nFRE)讀使能,低電平有效,讀之前使CE有效。 WE:(nFWE)寫使能,低電平有效,寫之前必須使WE有效。 WP:寫保護(hù)低電平有效 R/B:(R/nB)Ready/Busy Output,就緒/忙,主要用于在發(fā)送完編程/擦除命令后,檢測(cè)這些操作是否完成,忙,表示編程/擦除操作仍在進(jìn)行中,就緒表示操作完成。(其中就緒:高電平,忙:低電平)?;贛TD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析(二) 四、常見的NANDFLASH的操作 1、要實(shí)現(xiàn)對(duì) Nand Flash 的操作,比如讀取一頁的數(shù)據(jù),寫入一頁的數(shù)據(jù)等,都要發(fā)送對(duì)應(yīng)的命令,而且要符合硬件的規(guī)定,如圖: 比如說要實(shí)現(xiàn)讀一頁的數(shù)據(jù),就要發(fā)送Read命令,而且分兩個(gè)周期發(fā)送,即分兩次發(fā)送對(duì)應(yīng)的命令,第一次是 0x00h,第二次是 0x30h,而兩次命令中間,需要發(fā)送對(duì)應(yīng)的你所要讀取的頁的地址,對(duì)應(yīng)地,其他常見的一些操作,比如寫一個(gè)頁的數(shù)據(jù)(Page Program),就是先發(fā)送 0x80h,然后發(fā)送要寫入的地址,再發(fā)送0x10h。 2、讀(Read)nandflash操作過程分析 1)紅色豎線穿過的第一行,是 CLE。前面介紹命令所存使能(CLE)的那個(gè)引腳將CLE 置 1,就說明你將要通過 I/O 復(fù)用端口發(fā)送進(jìn)入Nand Flash的,是命令,而不是地址或者其他類型的數(shù)據(jù)。只有這樣將 CLE 置 1,使其有效,才能去通知了內(nèi)部硬件邏輯,你接下來將收到的是命令,內(nèi)部硬件邏輯才會(huì)直到收到的是命令,放到命令寄存器中,才能實(shí)現(xiàn)后面正確的操作,否則,不去將 CLE 置 1使其有效硬件會(huì)無所適從,不知道你傳入的到底是數(shù)據(jù)還是命令了。 2)而第二行,是 CE,那一刻的值是 0。這個(gè)道理很簡單,你既然要向Nand Flash發(fā)命令,那么先要選中它,所以,要保證 CE為低電平,使其有效,也就是片選有效。 3)第三行是 WE,意思是寫使能。因?yàn)榻酉聛硎峭?Nand Flash里面寫命令,所以,要使得 WE有效所以設(shè)為低電平。 4)第四行,是 ALE 是低電平,而 ALE 是高電平有效,此時(shí)意思就是使其無效。而對(duì)應(yīng)地,前面介紹的使 CLE 有效,因?yàn)閷⒁獢?shù)據(jù)的是命令(此時(shí)是發(fā)送圖示所示的讀命令第二周期的 0x30) ,而不是地址。如果在其他某些場(chǎng)合,比如接下來的要輸入地址的時(shí)候,就要使其有效,而使 CLE 無效了。 5)第五行,RE,此時(shí)是高電平,無效??梢钥吹剑篮竺娴?6 階段,才變成低電平,才有效,因?yàn)槟菚r(shí)候要發(fā)生讀取命令,去讀取數(shù)據(jù)。 6)第六行,就是我們重點(diǎn)要介紹的,復(fù)用的輸入輸出 I/O 端口了,此刻,還沒有輸入數(shù)據(jù),接下來,在不同的階段,會(huì)輸入或輸出不同的數(shù)據(jù)/地址。 7)第七行,R/B,高電平,表示 R(Ready)/就緒,因?yàn)榈搅撕竺娴牡?5階段,硬件內(nèi)部,在第四階段,接受了外界的讀取命令后,把該頁的數(shù)據(jù)一點(diǎn)點(diǎn)送到頁寄存器中,這段時(shí)間,屬于系統(tǒng)在忙著干活,屬于忙的階段所以,R/B才變成低,表示 Busy忙的狀態(tài)的。 其他的時(shí)序的就類似的理解。3、計(jì)算我們要讀取或者寫入的行地址和例地址 以mini2440開發(fā)板上的K9F2G08為例,此Nand Flash,一共有 2048 個(gè)塊,每個(gè)塊內(nèi)有 64 頁,每個(gè)頁是 2K+64 Bytes。 假設(shè),我們要訪問其中的第 1000個(gè)塊中的第 25 頁中的 1208字節(jié)處的地址,此時(shí),我們就要先把具體的地址算出來: 物理地址 =塊大小*塊號(hào) 頁大小*頁號(hào) 頁內(nèi)地址 從上圖可以看出,該FLASH的地址周期一共有5個(gè),2個(gè)列地址(Column)周期,3個(gè)行地址(Row)周期 a)對(duì)應(yīng)的列地址就是頁內(nèi)地址,該flash一個(gè)頁的大小是2K即2048個(gè)字節(jié),所以它的地址范圍是02047,對(duì)應(yīng)的上圖的列地址A0-A10就是頁內(nèi)地址。你可能會(huì)發(fā)現(xiàn)多出了一個(gè)A11,從A0-A11,這樣一共就有了12位,那它的地址范圍就是0212,即04096了,實(shí)際上,由于我們?cè)L問頁內(nèi)地址,可能會(huì)訪問到 oob 的位置,即 2048-2111 這 64 個(gè)字節(jié)的范圍內(nèi),所以,此處實(shí)際上只用到了 20482111,用于表示頁內(nèi)的 oob 區(qū)域,其大小是 64字節(jié)。 b)對(duì)應(yīng)地,A12A28,稱作頁號(hào),頁的號(hào)碼,可以定位到具體是哪一個(gè)頁,該FLASH一共是64頁一共需要6位即A12A17,而其中A18A28表示對(duì)應(yīng)的塊號(hào),即屬于哪個(gè)塊。這里有一個(gè)很重要的地方就是我們要傳入的地址的每一位,就是對(duì)應(yīng)著上表中的 A0 到 A28 ,實(shí)際上上表中的 A11是比較特殊的,只有當(dāng)我們?cè)L問頁內(nèi)地址處于 oob的位置,即屬于 20482111 的時(shí)候,A11才會(huì)其效果,才會(huì)用 A0-A11用來表示對(duì)應(yīng)的某個(gè)屬于 20482111 的某個(gè)值,屬于 oob 的某個(gè)位置,而我們此處的頁內(nèi)地址為 1208,還沒有超過 2047 呢,所以 A11肯定是 0然后我們?cè)賮硭闵厦嫖覀円L問的地址:第 1000個(gè)塊中的第 25 頁中的 1208字節(jié)處的地址第 1000個(gè)塊中的第 25 頁中的 1208字節(jié)處的地址它對(duì)應(yīng)著的頁內(nèi)地址為:頁內(nèi)地址 =1208Bytes =0x4B8頁 號(hào) =塊數(shù)*每塊多少頁 + 塊內(nèi)的頁號(hào) =1000*64 + 25 =0xFA19也就是我們要訪問0xFA19頁內(nèi)的0x4B8地址,再把這個(gè)地址轉(zhuǎn)換成列和行地址A0A10是用來表示頁內(nèi)地址的所以把0x4B8拆分成兩列列地址1=0xB8,列地址2=0x04;再把頁號(hào)0xFA19拆分成3行行地址1=0x19,行地址2=0xFA,行地址3=0x0; 對(duì)應(yīng)的看看linux2.6.35/driver/mtd/nand/nand_base.c中地址的發(fā)送 上面的column即對(duì)應(yīng)著頁內(nèi)地址,通常情況是0,如果不是0則通過傳入進(jìn)來的地址除于頁地址就可得到相應(yīng)的列地址了。而 page_addr 即頁號(hào),就是通過要訪問的地址,除于頁大小,即可得到。對(duì)于其他操作還正在研究中。雖然說上面這些東東大部分都是來自別人的東西,但是我相信現(xiàn)在它已經(jīng)變成我自己的東西了。我不記得那個(gè)大帥的博客了,因?yàn)槲沂侵苯影阉牟┛徒o保存到本地了?;贛TD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析(三) 非常的說:我突然發(fā)現(xiàn)在寫這些關(guān)于NAND驅(qū)動(dòng)的文章的時(shí)候,原來我一直是在改寫別人的博客。其實(shí)這并不要緊的,我也覺得這不僅僅是一種比較好的學(xué)習(xí)方法了,為什么呢,因?yàn)楫?dāng)我在看他的博客的時(shí)候,我明白了一點(diǎn),然后當(dāng)我自己要寫的時(shí)候。對(duì)這個(gè)東東又進(jìn)一步了解一點(diǎn)了。呵呵Copy也分檔次了五、硬件時(shí)序到軟件代碼的演變過程對(duì)nand_base.c部分代碼的分析該文件位于還是把那個(gè)讀NAND的硬件時(shí)序圖給貼上,如下圖:此階段,是讀命令第一個(gè)周期,發(fā)送的命令為0x00。:此階段,依次發(fā)送列地址,關(guān)于這些行地址,列地址等是如何計(jì)算出來的,后面的內(nèi)容會(huì)有詳細(xì)解釋。 :此階段是發(fā)送對(duì)應(yīng)的行地址 :此階段是發(fā)送讀命令第二周期 2nd cycle所對(duì)應(yīng)的命令,0x30 :此階段是等待時(shí)間,等待 Nand Flash硬件上準(zhǔn)備好對(duì)應(yīng)的數(shù)據(jù),以便后續(xù)讀出。 :此階段,就是一點(diǎn)點(diǎn)地把所需要的數(shù)據(jù)讀出來。 MTD 讀取數(shù)據(jù)的入口是 nand_read,然后調(diào)用 nand_do_read_ops,此函數(shù)主體如下:static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) /*此處省略部分代碼*/ 。 while(1) /*省略*/ .。 if (likely(sndcmd) /*#define NAND_CMD_READ0 0*/ /*1)*讀取數(shù)據(jù)前肯定要先發(fā)送對(duì)應(yīng)的讀頁命令*/ chip-cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); sndcmd = 0; /* Now read the page into the buffer */ if (unlikely(ops-mode = MTD_OOB_RAW) ret = chip-ecc.read_page_raw(mtd, chip, bufpoi, page); else if (!aligned & NAND_SUBPAGE_READ(chip) & !oob) ret = chip-ecc.read_subpage(mtd, chip, col, bytes, bufpoi); else /*執(zhí)行到這里read_page函數(shù)讀取對(duì)應(yīng)的數(shù)據(jù)了*/ ret = chip-ecc.read_page(mtd, chip, bufpoi, page); if (ret pagebuf = realpage; memcpy(buf, chip-buffers-databuf + col, bytes); buf += bytes; 。 if (mtd-ecc_stats.failed - stats.failed) return -EBADMSG; return mtd-ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0;上面這些代碼都不需要我們?nèi)?shí)現(xiàn)的,使用MTD層的自定義代碼就行。nand_command_lp的分析static void nand_command_lp(struct mtd_info *mtd, unsigned int command, int column, int page_addr) register struct nand_chip *chip = mtd-priv; /* Emulate NAND_CMD_READOOB */ if (command = NAND_CMD_READOOB) column += mtd-writesize; command = NAND_CMD_READ0; /* Command latch cycle */ /* 此處就是就是發(fā)送讀命令的第一個(gè)周期1st Cycle的命令,即0x00,對(duì)應(yīng)著上述步驟中的 */ 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; /* Serially input address */ if (column != -1) /* Adjust columns for 16 bit buswidth */ if (chip-options & NAND_BUSWIDTH_16) column = 1; /* 發(fā)送兩個(gè)column列地址,對(duì)應(yīng)著上述步驟中的 */ chip-cmd_ctrl(mtd, column, ctrl);/*發(fā)送列地址1*/ ctrl &= NAND_CTRL_CHANGE; chip-cmd_ctrl(mtd, column 8, ctrl);/*發(fā)送列地址2*/ if (page_addr != -1) /* 接下來是發(fā)送三個(gè)Row,行地址,對(duì)應(yīng)著上述步驟中的 */ chip-cmd_ctrl(mtd, page_addr, ctrl);/*發(fā)送行地址1*/ chip-cmd_ctrl(mtd, page_addr 8,/*發(fā)送行地址2*/ NAND_NCE | NAND_ALE); /* One more address cycle for devices 128MiB */ if (chip-chipsize (128 cmd_ctrl(mtd, page_addr 16,/*發(fā)送行地址3*/ NAND_NCE | NAND_ALE); chip-cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* * program and erase have their own busy handlers * status, sequential in, and deplete1 need no delay */ switch (command) 。 return; /*復(fù)位*/ case NAND_CMD_RESET: if (chip-dev_ready) break; udelay(chip-chip_delay); chip-cmd_ctrl(mtd, NAND_CMD_STATUS, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip-cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); while (!(chip-read_byte(mtd) & NAND_STATUS_READY) ; return; /*讀忙信號(hào)*/ case NAND_CMD_RNDOUT: /* No ready / busy check necessary */ chip-cmd_ctrl(mtd, NAND_CMD_RNDOUTSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip-cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); return;/* 接下來發(fā)送讀命令的第二個(gè)周期2nd Cycle的命令,即0x30,對(duì)應(yīng)著上述步驟中的 */ case NAND_CMD_READ0: chip-cmd_ctrl(mtd, NAND_CMD_READSTART, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE); chip-cmd_ctrl(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE); /* This applies to read commands */ default: /* * If we dont have access to the busy pin, we apply the given * command delay */ if (!chip-dev_ready) udelay(chip-chip_delay); return; /* Apply this short delay always to ensure that we do wait tWB in * any case on any machine. */* 此處是對(duì)應(yīng)著中的tWB的等待時(shí)間*/ ndelay(100);/* 接下來就是要等待一定的時(shí)間,使得Nand Flash硬件上準(zhǔn)備好數(shù)據(jù),以供你之后讀取,即對(duì)應(yīng)著步驟 */ nand_wait_ready(mtd);還有一個(gè)步驟沒有實(shí)現(xiàn)那就是步驟了一點(diǎn)一點(diǎn)的把數(shù)據(jù)讀出來nand_read_page_hwecc分析static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) 。 for (i = 0; eccsteps; eccsteps-, i += eccbytes, p += eccsize) chip-ecc.hwctl(mtd, NAND_ECC_READ); /*這個(gè)最重要了這才是真正的從NAND的緩沖區(qū)中把數(shù)據(jù)給讀出來*/ chip-read_buf(mtd, p, eccsize); chip-ecc.calculate(mtd, p, &ecc_calci); 。 return 0;上面的 read_buf,就是真正的去讀取數(shù)據(jù)的函數(shù)了,由于不同的Nand Flash controller 控制器所實(shí)現(xiàn)的方式不同,所以這個(gè)函數(shù)必須在你的 Nand Flash驅(qū)動(dòng)中實(shí)現(xiàn),即MTD 層,能幫我們實(shí)現(xiàn)的都實(shí)現(xiàn)了,不能實(shí)現(xiàn)的,那肯定是我們自己的事情了。接下來的工作是什么?MTD原始設(shè)備和硬件驅(qū)動(dòng)層的交互了.這個(gè)才是我們要去真正實(shí)現(xiàn)的?;贛TD的NANDFLASH設(shè)備驅(qū)動(dòng)底層實(shí)現(xiàn)原理分析(四) 進(jìn)過前面3篇文章對(duì)NANDFLASH的一些硬件特性以及MTD的上層操作已經(jīng)有了一個(gè)大體概念,這些東西的重要性就像你要吃飯那么你首先得學(xué)會(huì)拿筷子道理一樣吧,應(yīng)該一樣的。五、MTD原始設(shè)備層和硬件驅(qū)動(dòng)層的橋梁: 熟悉這幾個(gè)重要的結(jié)構(gòu)體:linux/mtd/mtd.hstruct mtd_info u_char type; /*內(nèi)存技術(shù)類型(包括MTD_RAM,MTD_ROM,MTD_NANDFLASH等)*/ uint32_t flags; /*MTD設(shè)備屬性標(biāo)志*/ uint64_t size; / Total size of the MTD uint32_t erasesize;/MTD設(shè)備的擦除單元大小,對(duì)于NandFlash來說就是Block的大小 uint32_t writesize;/最小的可寫單元的字節(jié)數(shù) uint32_t oobsize; / Amount of OOB data per block (e.g. 16) uint32_t oobavail; / Available OOB bytes per block unsigned int erasesize_shift; unsigned int writesize_shift; /* Masks based on erasesize_shift and writesize_shift */ unsigned int erasesize_mask; unsigned int writesize_mask; / Kernel-only stuff starts here. const char *name; int index; /* ecc layout structure pointer - read only ! */ struct nand_ecclayout *ecclayout; /* Data for variable erase regions. If numeraseregions is zero, * it means that the whole device has erasesize as given above. *一般為1吧 */ int numeraseregions; /擦除區(qū)域的指針 struct mtd_erase_region_info *eraseregions; /擦除函數(shù)將一個(gè)erase_info結(jié)構(gòu)放入擦除隊(duì)列中 int (*erase) (struct mtd_info *mtd, struct erase_info *instr); /* This stuff for eXecute-In-Place */ /* phys is optional and may be set to NULL */ int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void *virt, resource_size_t *phys); /* We probably shouldnt allow XIP if the unpoint isnt a NULL */ void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len); /* Allow NOMMU mmap() to directly map the device (if not NULL) * - return the address to which the offset maps * - return -ENOSYS to indicate refusal to do the mapping */ unsigned long (*get_unmapped_area) (struct mtd_info *mtd, unsigned long len, unsigned long offset, unsigned long flags); /* Backing device capabilities for this device * - provides mmap capabilities */ struct backing_dev_info *backing_dev_info;/read和write分別用于MTD設(shè)備的讀和寫 int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int (*panic_write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); /讀寫MTD設(shè)備的OOB區(qū)域的數(shù)據(jù) int (*read_oob) (struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); int (*write_oob) (struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops); /訪問一些受保護(hù)的寄存器 int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); /* kvec-based read/write methods. NB: The count parameter is the number of _vectors_, each of which contains an (ofs, len) tuple. */ int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); /* Sync */*同步*/ void (*sync) (struct mtd_info *mtd); /* Chip-supported device locking */ int (*lock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, uint64_t len); /* Power Management functions */ int (*suspend) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd); /* Bad block management functions */ int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); struct notifier_block reboot_notifier; /* default mode before reboot */ /* ECC status information */ struct mtd_ecc_stats ecc_stats; /* Subpage shift (NAND) */ int subpage_sft; /私有數(shù)據(jù) 指向map_info結(jié)構(gòu) void *priv; struct module *owner; struct device dev; int usecount; /設(shè)備驅(qū)動(dòng)回調(diào)函數(shù) int (*get_device) (struct mtd_info *mtd); void (*put_device) (struct mtd_info *mtd);上面的read()、write()、read_oob()、等都是MTD設(shè)備驅(qū)動(dòng)要實(shí)現(xiàn)的主要函數(shù),不過這些函數(shù)都是透明的不需要我們自己去實(shí)現(xiàn),因?yàn)長inux在MTD的下層實(shí)現(xiàn)了針對(duì)NORFLASH和NANDFLASH的通用mtd_info成員函數(shù)。感覺沒什么可寫的了,因?yàn)檫@些都不是我要關(guān)注的東西,但是又不能不知道有這么回事這些結(jié)構(gòu)體還是得了解了解driver/mtd/mtdpart.c/* Our partition node structure */struct mtd_part struct mtd_info mtd; /分區(qū)信息 struct mtd_info *master; /該分區(qū)的主分區(qū) uint64_t offset; /該分區(qū)的偏移量 struct list_head list;mtd_partition會(huì)在MTD原始設(shè)備調(diào)用add_mtd_partitions()的時(shí)候傳遞分區(qū)參數(shù)/linux/mtd/partition.hstruct mtd_partition char *name; /* identifier string */ uint64_t size; /* partition size */ uint64_t offset; /* offset within the master MTD space */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/;一個(gè)MTD原始設(shè)備可以通過mtd_part分割成數(shù)個(gè)MTD原始設(shè)備注冊(cè)進(jìn)mtd_table,mtd_table中的每個(gè)MTD原始設(shè)備都可以被注冊(cè)成一個(gè)MTD設(shè)備,有兩個(gè)函數(shù)可以完成這個(gè)工作,即add_mt

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論