Linux SD/MMC/SDIO驅動分析

1、SD/MMC/SDIO概念區分html

SD(SecureDigital)與 MMC(MultimediaCard)linux

SD 是一種 flash memory card 的標準,也就是通常常見的 SD 記憶卡,而 MMC 則是較早的一種記憶卡標準,目前已經被 SD 標準所取代。在維基百科上有至關詳細的 SD/MMC 規格說明:[http://zh.wikipedia.org/wiki/Secure_Digital]。ios

SDIO(SecureDigital I/O)git

SDIO 是目前咱們比較關心的技術,SDIO 故名思義,就是 SD 的 I/O 接口(interface)的意思,不過這樣解釋可能還有點抽像。更具體的說明,SD 原本是記憶卡的標準,可是如今也能夠把 SD 拿來插上一些外圍接口使用,這樣的技術即是 SDIO。數組

因此 SDIO 自己是一種至關單純的技術,透過 SD 的 I/O 接腳來鏈接外部外圍,而且透過 SD 上的 I/O 數據接位與這些外圍傳輸數據,並且 SD 協會會員也推出很完整的 SDIO stack 驅動程序,使得 SDIO 外圍(咱們稱爲 SDIO 卡)的開發與應用變得至關熱門。網絡

如今已經有很是多的手機或是手持裝置都支持 SDIO 的功能(SD 標準本來就是針對 mobile device 而制定),並且許多 SDIO 外圍也都被開發出來,讓手機外接外圍更加容易,而且開發上更有彈性(不須要內建外圍)。目前常見的 SDIO 外圍(SDIO 卡)有:框架

  · Wi-Fi card(無線網絡卡) ide

  · CMOS sensor card(照相模塊) 函數

  · GPS card 網站

  · GSM/GPRS modem card 

  · Bluetooth card 

  ·  Radio/TV card(很好玩)

SDIO 的應用將是將來嵌入式系統最重要的接口技術之一,而且也會取代目前 GPIO 式的 SPI 接口。

SD/SDIO 的傳輸模式

SD 傳輸模式有如下 3 種:

  · SPI mode(required) 

  · 1-bit mode 

  ·  4-bit mode

SDIO 一樣也支持以上 3 種傳輸模式。依據 SD 標準,全部的 SD(記憶卡)與 SDIO(外圍)都必須支持 SPI mode,所以 SPI mode 是「required」。此外,早期的 MMC 卡(使用 SPI 傳輸)也能接到 SD 插糟(SD slot),而且使用 SPI mode 或 1-bit mode 來讀取。

Secure digital I/Ocard,pin out

SD 的 MMCMode

SD 也能讀取 MMC 內存,雖然 MMC 標準上提到,MMC 內存不見得要支持 SPI mode(可是必定要支持 1-bit mode),可是市面上能看到的 MMC 卡其實都有支持 SPI mode。所以,咱們能夠把 SD 設定成 SPI mode 的傳輸方式來讀取 MMC 記憶卡。

SD 的 MMC Mode 就是用來讀取 MMC 卡的一種傳輸模式。不過,SD 的 MMC Mode 雖然也是使用 SPI mode,但其物理特性還是有差別的:

  · MMC 的 SPI mode 最大傳輸速率爲 20 Mbit/s; 

  · SD 的 SPI mode 最大傳輸速率爲 25 Mbit/s。

爲避免混淆,有時也用 SPI/MMC mode 與 SPI/SD mode 的寫法來作清楚區別

參考網站:https://www.sdcard.org/developers/overview/capacity/

              http://www.interfacebus.com/Secure_Digital_IO_Card_Pinout.html

 

2、MMC子系統介紹

MMC代碼分佈

MMC子系統代碼主要在drivers/mmc目錄下,共有三個目錄:

         Card:存放閃存卡(塊設備)的相關驅動,如MMC/SD卡設備驅動,SDIOUART;

         Host:針對不一樣主機端的SDHC、MMC控制器的驅動,這部分須要由驅動工程師來完成;

         Core:整個MMC的核心層,這部分完成不一樣協議和規範的實現,爲host層和設備驅動層提供接口函數。

MMC子系統框架

 

Linux MMC子系統主要分紅三個部分:

  MMC核心層:完成不一樣協議和規範的實現,爲host層和設備驅動層提供接口函數。MMC核心層由三個部分組成:MMC,SD和SDIO,分別爲三類設備驅動提供接口函數;

  Host 驅動層:針對不一樣主機端的SDHC、MMC控制器的驅動;

  Client 驅動層:針對不一樣客戶端的設備驅動程序。如SD卡、T-flash卡、SDIO接口的GPS和wi-fi等設備驅動。

 

3、SD 總線協議

SD總線通訊是基於指令和數據比特流,起始位開始和中止位結束。SD總線通訊有三個元素:

  Command:由host發送到卡設備,使用CMD線發送;

  Response:從card端發送到host端,做爲對前一個CMD的相應,經過CMD線發送;

  Data:即能從host傳輸到card,也能從card傳輸到host,經過data線傳輸。

Commands

如下是四種用於控制卡設備的指令類型,每一個command都是固定的48位長度:

  一、broadcast commands(bc), no response:廣播類型的指令,不須要有響應;

  二、broadcast commands with response(bcr):廣播類型的指令且須要響應;

  三、addressed(point-to-point) commands(ac):由HOST發送到指定的卡設備,沒有數據的傳輸;

  四、address(point-to-point) data transfercommands(adtc):由HOST發送到指定的卡設備且伴隨有數據傳輸。

指令格式:

Card register

幾個主要的寄存器:OCR,CID,CSD,RCA和SCR。

  Operation condition register(OCR):32位的OCR包含卡設備支持的工做電壓表;

  Card identification number register (CID):包含用於在卡識別階段的卡信息,包括製造商ID,產品名等;

  Card specific data register(CSD):CSD寄存器提供瞭如何訪問卡設備的信息,包括定義了數據格式,錯誤校驗類型,最大訪問次數,數據傳輸率等;

  Relative card address register(RCA):存放在卡識別階段分配的相對卡地址,缺省相對卡地址爲0000h;

  SD card configuration register(SCR):SCR是一個配置寄存器,用於配置SD memory card的特殊功能。

Response

全部的response都經過CMD線發送到host端,R4和R5響應類型是SDIO中特有的:

  一、R1(normal response command):用來響應經常使用指令;

  二、R2(CID,CSD register):用來響應CMD2和CMD10或CMD9,並把CID或CSD寄存器做爲響應數據;

  三、R3(OCR register):用來響應ACMD41指令,並把OCR寄存器做爲響應數據;

  四、R6(published RCA response):分配相對卡地址的響應;

  五、R7(card interface condition):響應CMD8,返回卡支持的電壓信息;

  六、R4(CMD5):響應CMD5,並把OCR寄存器做爲響應數據;

  七、R5(CMD52):CMD52是一個讀寫寄存器的指令,R5用於CMD52的響應;

Response 格式:

***詳情請參考spec***

 

 

4、SD初始化流程

 

當host上電後,使全部的卡設備處於卡識別模式,完成設置有效操做電壓範圍,卡識別和請求卡相對地址等操做。

  一、發送指令CMD0使卡設備處於idle狀態;

  二、發送指令CMD8,若是卡設備有response,說明此卡爲SD2.0以上;

  三、發送指令CMD55+ACMD41,該指令是用來探測卡設備的工做電壓是否符合host端的要求;

在發送ACMD41這類指令以前須要先發送CMD55指令,在SDIO中ACMD41指令被CMD5替代。

  四、發送指令CMD11轉換工做電壓到1.8V;

  五、發送指令CMD2獲取CIA;

  六、發送指令CMD3獲取RCA(relative card address)

SD初始化分析

系統上電時,SDI控制器會去掃描總線上的全部設備,而後對掛在總線上卡設備進行初始化。進行掃描和初始化工做都是由mmc_scan函數來完成,如下是Linux驅動中初始化流程圖(感謝同事Linkin的圖)。

SDIO、SD和MMC這三者的初始化流程稍有不一樣,是向下兼容的。

轉載:http://blog.csdn.net/paul_liao/article/details/7685869

 

5、SD卡調試關鍵點:

  1. 上電時要延時足夠長的時間給 SD 卡一個準備過程,在個人程序裏是 5 秒,根據不一樣的卡設置不一樣的延時時間。 SD 卡初始化第一步在發送 CMD 命令以前,在片選有效的狀況下首先要發送至少 74 個時鐘,不然將有可能出現 SD 卡不能初始化的問題。

  2. SD 卡發送復位命令 CMD0 後,要發送版本查詢命令 CMD8 ,返回狀態通常分兩種,若返回 0x01 表示此 SD 卡接受 CMD8, 也就是說此 SD 卡支持版本 2 ;若返回 0x05 則表示此 SD 卡支持版本 1 。由於不一樣版本的 SD 卡操做要求有不同的地方,因此務必查詢 SD 卡的版本號,不然也會出現 SD 卡沒法正常工做的問題。

  3. 理論上要求發送 CMD58 得到 SD 卡電壓參數,但實際過程當中因爲事先都知道了 SD 卡的工做電壓,所以可省略這一步簡化程序。協議書上也建議儘可能不要用這個命令。

  4. SD 卡讀寫超時時間要按照協議說明書書上的給定值 ( 讀超時: 100ms ;寫超時: 250ms) ,這個值要在程序中準確計算出來,不然將會出現不能正常讀寫數據的問題。我本身定義了一個計算公式:超時時間 =( 8/clk )*arg 。

  5. 2GB 之內的 SD 卡 ( 標準卡 ) 和 2GB 以上的 SD 卡 ( 大容量卡 ) 在地址訪問形式上不一樣,這一點尤爲要注意,不然將會出現沒法讀寫數據的問題。如標準卡在讀寫操做時,對讀或寫命令令牌當中的地址域符初值 0x10 ,表示對第 16 個字節之後的地址單元進行操做 ( 前提是此 SD 卡支持偏移讀寫操做 ) ,而對大容量卡讀或寫命令令牌當中的地址域符初值 0x10 時,則表示對第 16 塊進行讀寫操做,並且大容量卡只支持塊讀寫操做,塊大小固定爲 512 字節,對其進行字節操做將會出錯。

  6. 對某一塊要進行寫操做時最好先執行擦出命令,這樣寫入的速度就能大大提升。進行擦除操做時無論是標準卡仍是大容量卡都按塊操做執行,也就是一次擦除至少 512 字節。

  7. 對標準卡進行字節操做時,起始和終止必須在一個物理扇區內,不然將不能進行讀寫操做。實際操做過程當中建議用塊操做以提升效率。無論是標準卡仍是大容量卡一個讀寫命令只能對一個塊進行操做,不容許跨物理層地址操做。

  8. 在寫數據塊前要先寫入若干個 dummy data 字節,寫完一個塊數據時,主機要監測 MISO 數據線,若是從機處於忙狀態這根數據線會保持低電平,這樣主機就能夠根據這根數據線的狀態以決定是否發送下一個命令,在從機沒有釋放 MISO 數據線以前,主機絕對不能執行其餘命令,不然將會致使寫入的數據出錯,並且從機也不會響應主機的命令。

  9. 在 SPI 模式下, CRC 校驗是被忽略的,但依然要求主從機發送 CRC 碼,只是數值能夠是任意值,通常主機的 CRC 碼一般設爲 0x00 或 0xFF 。

  讀多塊操做和寫多塊操做的傳輸中止形式不同,讀多塊操做時用用命令 CMD12 終止傳輸,而寫多塊操做時用 Stop Tran Token( 中止傳輸令牌,值爲 0xFD) 終止傳輸。

----------------------------------------------------------------------------------------

一、初始化步驟:
  (1) 延時至少 74clock,等待SD卡內部操做完成,在MMC協議中有明確說明。
  (2) CS低電平選中SD卡。
  (3) 發送 CMD0 ,須要返回 0x01 ,進入 Idle 狀態
  (4) 爲了區別SD卡是2.0仍是1.0,或是MMC卡,這裏根據協議向上兼容的原理,首先發送只有SD2.0纔有的命令CMD8,若是CMD8返回無錯誤,則初步判斷爲2.0卡,進一步發送命令循環發送 CMD55+ACMD41 ,直到返回 0x00 ,肯定SD2.0卡初始化成功,進入Ready 狀態,再發送CMD58命令來判斷是HCSD仍是SCSD,到此SD2.0卡初始化成功 。若是CMD8返回錯誤則進一步判斷爲1.0卡仍是MMC卡,循環發送CMD55+ACMD41 ,返回無錯誤,則爲SD1.0卡,到此SD1.0卡初始成功,若是在必定的循環次數下,返回爲錯誤,則進一步發送CMD1進行初始化,若是返回無錯誤,則肯定爲MMC卡,若是在必定的次數下,返回爲錯誤,則不能識別該卡,初始結束。
  (5)CS拉高。
二、讀步驟:
  (1) 發送 CMD17 (單塊)或 CMD18 (多塊)讀命令,返回 0x00
  (2) 接收數據開始令牌 0xfe (或 0xfc ) + 正式數據 512Bytes + CRC 校驗 2Bytes, 默認正式傳輸的數據長度是 512Bytes ,可用 CMD16 設置塊長度。
三、 寫步驟:
  (1) 發送 CMD24 (單塊)或 CMD25 (多塊)寫命令,返回 0x00
  (2) 發送數據開始令牌 0xfe (或 0xfc ) + 正式數據 512Bytes + CRC 校驗 2Bytes
四、 擦除步驟:
  (1) 發送 CMD32 ,跟一個參數來指定首個要擦除的起始地址( SD 手冊上說是塊號)
  (2) 發送 CMD33, ,指定最後的地址
  (3) 發送 CMD38 ,擦除指定區間的內容
  此 3 步順序不能顛倒。

 

6、SD卡的命令格式及解析

1.SD卡命令組成

SD卡的指令由6字節(Byte)組成,以下:

  Byte1:0 1 x x x x x x(命令號,由指令標誌定義,如CMD39爲100111即16進制0x27,那麼完整的CMD39第一字節爲01100111,即0x27+0x40)

  Byte2-5:Command Arguments,命令參數,有些命令沒有參數

  Byte6:前7位爲CRC(Cyclic Redundacy Check,循環冗餘校驗)校驗位,最後一位爲中止位0

2.SD卡的命令

SD卡命令共分爲12類,分別爲class0到class11,不一樣的SDd卡,主控根據其功能,支持不一樣的命令集,以下:

  Class0 :(卡的識別、初始化等基本命令集)

    CMD0:復位SD 卡.

    CMD1:讀OCR寄存器.

    CMD9:讀CSD寄存器.

    CMD10:讀CID寄存器.

    CMD12:中止讀多塊時的數據傳輸

    CMD13:讀 Card_Status 寄存器

  Class2 (讀卡命令集):

    CMD16:設置塊的長度

    CMD17:讀單塊.

    CMD18:讀多塊,直至主機發送CMD12爲止 .

  Class4(寫卡命令集) :

    CMD24:寫單塊.

    CMD25:寫多塊.

    CMD27:寫CSD寄存器 .

  Class5 (擦除卡命令集):

    CMD32:設置擦除塊的起始地址.

    CMD33:設置擦除塊的終止地址.

    CMD38: 擦除所選擇的塊.

  Class6(寫保護命令集):

    CMD28:設置寫保護塊的地址.

    CMD29:擦除寫保護塊的地址.

    CMD30: Ask the card for the status of the write protection bits

  class7:卡的鎖定,解鎖功能命令集

  class8:申請特定命令集 。

  class10 -11 :保留

3.有關sd卡驅動和fat fs的實現用了3個文件來實現。

sdboot.c爲sd的驅動(可理解爲pdd)層,主要實現一些對sd控制器的配置以及一些基本sd命令的實現和對sd 卡的操做。

sdmmc.c實現了從sd卡讀取nk並跳到內存去運行的代碼(基本能夠理解爲sd驅動的mdd層)。

sdfat.c文件就是實現fat fs的。mdd層經過fatfs來對pdd層操做以實現讀取文件。

在整個過程當中遇到了不少問題,如今列舉以下:

  1)sd卡初始化問題

      配置gpio有關sd的功能:SDCMD, SDDAT[3:0]。

      使能CLKCON中的SDI位。

      時鐘以及計算公式:SDIPRE   = PCLK/(CLK)-1;INICLK=300000;SDCLK=24000000; MMCCLK= 15000000

      cmd0-cmd55-cmd41-cmd2-cmd3-cmd7-cmd6-cmd17

  2)對sd卡操做問題

      SD卡包括:一個標識寄存器CID,一個相應地址寄存器RCA,一個其餘參數寄存器CSD。

      對sd卡的操做是驅動經過sd controller來發相應的命令以達到讀寫等操做的:發送命令經過SDICmdCon[7:0]的除了開始2bit:CmdIndex放置要發送的命令號;SDICmdCon[8]開始發送命令來完成的。

      檢測卡的插入,直接用中斷引腳的電平來判斷。

      判斷插入的卡是不是sd卡,用命令cmd55和cmd41,由於mmc卡對cmd55不作迴應。

      命令9 就是獲取sd卡中csd寄存器的值的,該值包括不少sd卡的信息,其中就有sd卡的容量。這個值在sd卡接收到cmd9以後會以response的形式存放在sd控制器的SDI Response Register[0,1,2,3]中。在執行cmd9,cmd10等這樣的命令的時候,卡的狀態應該是不選中的,或直接在執行它們以前發送 cmd7(0)不選中卡,否則的話會timeout。

      用cmd17 來讀取單個block的數據,該命令要帶地址參數(該參數經過cmd3命令來獲取),而後根據SDIDSTA和SDIFSTA狀態值來從sd 控制器的SDIDAT寄存器中讀出要讀的數據。該命令與cmd9相反,在執行它以前要選中卡。讀完一個block以後要作一些善後工做,爲下次讀取作好準備,否則的話checkcmdend就要一直循環了。由於用的是每次都讀一個block,並地址要以block對齊,這樣就要考慮要讀取的地址是不是 block對齊的,長度是否夠一個block。

      SDIDCON這個數據控制寄存器也很重要,一些對數據的操做形式就是在這裏設置的。

  3)fat文件系統問題

      根據MBR找到分區表,根據分區表找到該分區MBR[446B+4個分區表(每一個16B)+2B結束符)

      分區表中的第9-12字節爲該分區的啓始地址(單位沒sector),第13-16字節爲分區的長度(單位也是sector)

    http://hjx5548.blog.163.com/blog/static/563676392009111704249875/

6、實例

1、概述

  最近在研究WIFI驅動,驅動模塊爲broamd4330,基於SDIO接口,因此趁機研究了一下內核中對於SDIO設備的註冊。

  (我使用的linux內核版本爲3.2.0    硬件爲samsung 4412)

  在介紹內核以前,有必要先了解一下MMC  SD  SDIO三種卡,從發展歷程來看,是先有MMC卡,後來有SD卡,這兩種都是純粹的存儲卡,而SDIO是什麼呢,從字面意思理解,應該是SD+IO,也就是既有存儲功能,又有IO控制功能,不過也有純IO功能的SDIO設備(本人用到的WIFI模塊就是這種)。而且,這三種卡可使用同一個插槽,系統還能正確的識別!!,多是因爲歷史緣由,在開始有Linux的時候,還只存在mmc卡(不存在SD和SDIO卡),因此在linux系統裏面關於這三種卡的名稱通通用「mmc「來命名。

        下面來看一下CPU與WIFI模塊的物理鏈接圖

                           

      從圖上能夠看出,咱們的WIFI模塊接的是CPU上的mmc3,數據線,時鐘線以及命令線都一一對應。

  固然在CPU一端,對於mmc3模塊,還有一個很重要的引腳--「xmmc3CDn」腳,CPU就是根據該引腳的電平高低來判斷mmc3模塊上是否有卡接入,若是電平爲低,表示有卡,若是爲高,表示無卡,筆者這裏將該引腳固定拉低。

  同時在WIFI模塊一端,也有一個很重要的引腳--「WL_SDIO_SPI_HSCI_SEL」引腳   ,它是用來選擇模塊是工做在SD模式(低電平),仍是SPI模式(高電平),筆者這裏也將該引腳固定拉低。

  好了,簡單的介紹了一些概念以及硬件後,仍是要回歸到程序上,從大的方面來說,MMC/SD/SDIO的驅動程序主要分爲兩大塊,主設備驅動和從設備驅動。對於上面的例子來講,CPU上的MMC3模塊就是主設備,而WIFI模塊就是從設備。該系列的博文就是分析MMC主設備在內核中的註冊,以及對於同一個mmc插槽,系統是如何區分出MMC SD 以及SDIO設備的。

 

2、host註冊過程

  上面說到了MMC/SD/SDIO(如下簡稱MMC)的驅動從大的方面來講分爲主設備驅動和從設備驅動,那本文就來詳細的講述主設備驅動註冊的過程。

  MMC主設備(也就是host)指的是集成於CPU內部的MMC controller,筆者用的是4412芯片,從datasheet能夠看出,裏面集成了四個MMC controller,分別是mmc0,mmc1,mmc2,mmc3。 而且從上一篇文章咱們知道,WIFI模塊是接在mmc3 這個host上面。

  在linux系統中,將每一個host設備封裝成platform_device來逐一進行註冊。

  對於筆者所使用的內核(3.2.0版本)來講,每個host設備所對應的platform_device文件位於目錄($KERNEL_SOURCE)/arch/arm/plat-samsung下,分別爲dev-hsmmc.c,dev-hsmmc1.c,dev-hsmmc2.c,dev-hsmmc3.c,爲了與實際WIFI模塊對應,咱們重點進入dev-hsmmc3.c文件看一看:

 

  從上圖能夠看出,該文件裏面定義了一個名爲s3c_device_hsmmc3的platform_device,可是定義好了的platform_device還須要有一個註冊的過程,該過程就發生在文件($KERNEL_SOURCE)/arch/arm/mach-exynos/mach-$(BOARD).c中,其中有以下的一個函數調用:
                                           
      
  它的行爲就是將數組skd4x12_devices裏面的每個platform_device項一一註冊進系統,而且這個數組裏面就包含了上面所定義的s3c_device_hsmmc3:

                                      

        

  因此總結來講,系統化在初始化的時候,就已經將s3c_device_hsmmc3(也就是那個host  mmc3)註冊進了platform總線(其餘的mmc0,mmc1,mmc2都是一個道理)。

  固然,對於熟悉platform機制的朋友來講,此時僅僅只是註冊了platform_device ,而對應的platform_driver尚未註冊。

  下面就來講說這個platform_driver的註冊,它是在$(KERNEL_SOURCE)/drivers/mmc/host目錄下的sdhci-s3c.c文件中進行的,該文件中有以下的一個註冊函數調用:

     

  其中的參數sdhci_s3c_driver就是上面所說的platform_driver,它也是定義在sdhci-s3c.c文件中,來看一下:

      

  在對sdhci_s3c_driver進行註冊的過程當中,系統會根據sdhci_s3c_driver->driver.name成員變量(此處是「s3c-sdhci」)在platform_bus 總線上尋找同名字的platform_dvice(這個過程稱之爲「探測」),經過上面對s3c_device_hsmmc3的註冊分析,發現s3c_device_mmc3.name也恰好是「s3c-sdhci」,因此他倆恰好能夠配對,探測成功,同時當你們查閱s3c_device_hsmmc,s3c_device_hsmmc1以及s3c_device_hsmmc2的時候發現他們的name成員變量都是「s3c-sdhci」,,因此會有四次成功的探測,每一次探測成功,就會調用sdhci_s3c_driver.probe函數---sdhci_s3c_probe,這個函數相當重要,在整個驅動註冊過程當中起着核心做用。

  

  上面文章說到了探測函數sdhci_s3c_probe,如今就來仔細分析這個函數的做用:

      在分析代碼以前,先簡要的歸納一下這個函數的功能:

  一、既然是講host的註冊,那麼首先必須構造出一個host,這個host就是經過sdhci_alloc_host函數來構造出來的,它是一個struct sdhci_host類型的結構體。同時,也經過mmc_alloc_host函數構造了一個struct mmc_host的結構體變量mmc。

  二、初始化host的時鐘,設置host的gpio等等其餘一些「亂七八糟」的參數初始化(須要的時候再詳細分析)。

  三、經過sdhci_add_host函數來註冊host。

  下面重點來看sdhci_add_host函數

 

  該函數主要是對mmc的註冊,一樣mmc也有不少的參數,先來看看他的操做函數集mmc->ops = &sdhci_ops

        

  其中,request函數指針指向的函數用來處理host向從設備發送命令的請求,

              set_ios用來設置電源、時鐘等等之類(須要重點關注),

              get_ro用來判斷是否寫保護

 

    再來看該函數裏面的中斷註冊部分

            

  咱們先看一下mmc_add_host這個函數,它的功能就是經過device_add函數將設備註冊進linux設備模型,最終的結果就是在sys/bus/platform/devices目錄下能見到s3c-sdhci.1,s3c-sdhci.2,s3c-sdhci.3設備節點。

  中斷註冊函_irq的第一個參數中斷號就取自於s3c_device_hsmmc3.resource裏面的irq參數,sdhci_irq就是中斷服務程序,該中斷函數通常在插卡、拔卡或者從設備反饋給host信息時會被調用數request

 

         中斷服務程序

         

  程序首先讀取寄存器NORINTSTSn的值,該寄存器中有兩個bit分別來表示卡的插入與拔出過程(注意,必須是動態變化過程,纔會讓相應的兩個bit置1),那麼接下來的if語句就是從該寄存器的那兩個bit來判斷是否有卡的插入或拔出,並同時清除這兩個bit,準備下一次的檢測,緊接着就調用中斷的下半部分函數 sdhci_tasklet_card,其實這個函數也沒作什麼事情,就是判讀若是此時有卡的話就經過mmc_detect_chang函數調用mmc_rescan函數。從這個函數的名字均可以猜出個八九不離十,它的功能就是掃描所插入的卡

 

    掃描卡的程序

      

  這個函數咱們重點關注上述兩個地方,其實真正的掃描動做發生在函數mmc_rescan_try_freq函數裏面,該函數的第二個參數表示以什麼樣的頻率去進行掃描,那麼可選的頻率值在那個數組freqs裏面,通常當用某個頻率值掃描成功後,就直接退出了,不然,會如下一個更低的頻率值來掃描,筆者所使用的WIFI模塊就是以400KHz的頻率掃描成功的。

 

       掃描過程

      

  該函數首先發送復位命令(不過該命令只有SDIO類型的卡纔可以識別),而後發送CMD0,讓設備進入IDLE模式,緊接着發送CMD8,獲取該卡所支持的電壓值,最後就是重點了(從1998-2003行),從所調用的各個函數名字能夠看出,它是在試探該卡是否爲SDIO? SD? MMC?

 

     那麼接下來的文章就是要分析上面的三個函數,看它是如何識別SDIO、SD、MMC的。

 

3、SDIO的識別和操做

  從上面文章的最後,咱們知道host在掃描卡的過程當中,其識別的順序爲SDIO  SD MMC,而且從它的註釋能夠看出,這個順序是很重要的。那這篇文章,咱們就看看SDIO的識別過程,它對應的函數就是mmc_attach_sdio(host) (函數位於文件drivers/mmc/core/sdio.c)

  這個函數大概來講作了以下的工做

    一、向卡發送CMD5命令,該命令有兩個做用:

      第一,經過判斷卡是否有反饋信息來判斷是否爲SDIO設備(只有SDIO設備纔對CMD5命令有反饋,其餘卡是沒有回饋的);

      第二,若是是SDIO設備,就會給host反饋電壓信息,就是說告訴host,本卡所能支持的電壓是多少多少。

    二、host根據SDIO卡反饋回來的電壓要求,給其提供合適的電壓。

    三、初始化該SDIO卡

    四、註冊SDIO的各個功能模塊

    五、註冊SDIO卡

  對於以上功能的具體解釋,下面將結合程序娓娓道來

  一、CMD5命令的發送

              

  第789行的函數就是發送的CMD5命令,若是卡對該命令有回饋的話,err就是0,不然,err爲非0,直接退出了;而且須要重點說明的一點就是,該函數的最後一個參數ocr,它是存儲反饋命令的,SDIO設備對CMD5的反饋命令爲R4,下面來仔細分析一下這個R4,由於後面要用到這個R4命令。從SDIO spec文檔裏面,咱們能獲得R4命令的格式

            

  從上圖能夠看出,該命令有48位,但咱們的ocr變量是32位的,那怎麼存儲呢?系統就去掉原命令的開頭8位以及結尾的8位,只保留中間的32爲,也就是截短後的命令格式是以下:

            

        

  具體各位的描述以下:

    C --   我還不知道

    Number 0f IO functions   -- 每一個SDIO設備都有功能塊,這三位就記錄了該設備有多少個功能塊,最多7個

    Memory Present – 指明該設備是純粹只有功能塊的設備,仍是同時包含了存儲空間,若是爲0就是前者,若是是1就是後者

    Stuff Bits  -- 沒有實際用途通常爲0

    I/O OCR – 該設備所能支持的電壓範圍(具體描述見sdio spec)

 

  二、配置電壓

         

  ocr就是咱們上面講的反饋命令R4(截短以後的32位),那麼ocr&0x7f的意義是什麼呢?從R4的格式就能夠看出來,其低24位就表明了所能支持的電壓範圍,咱們再來詳細的看一下這24位的OCR格式

                

  如今應該能夠知道ocr&0x7f的意義了吧,就是擯棄那些保留的電壓範圍。

  重點關注mmc_select_voltage

                  

  第1080行的相與 過程就是判斷host實際所支持的電壓與card所須要的電壓是否匹配,若是匹配,那麼ocr的值就非0,不然就爲0

  簡單介紹下第1082行的ffs函數,它的做用就是返回參數中第一個爲1的bit的位置(ffs(0)=0,ffs(1)=1,ffs(8)=4),那麼該函數用在這裏的做用就是取出card須要的實際電壓是多少;

  第1090行的mmc_set_ios函數裏面經過調用sdhci_set_power將host->ios.vdd所表明的電壓寫入寄存器PWRCONn中 完成那個對電壓的從新配置(想要了解更詳細的過程,請跟蹤源代碼)

 

  三、初始化SDIO卡

               

  第821行就是初始化SDIO卡的函數  這個函數很長,也很重要,這裏筆者就不列出其程序代碼了,只是列出其中最重要的幾條:

    一、經過函數mmc_alloc_card分配一個mmc_card的變量card

    二、經過讀取R4命令中的bit27(也就是Memory Present)來判斷此卡是純IO卡 ,仍是同時包含存儲功能。筆者使用的WIFI模塊爲純IO功能,因此card->type = MMC_TYPE_SDIO(這個很重要,之後會用到) (接下來重點分析MMC_TYPE_SDIO的狀況)

    三、經過發送CMD3命令獲取設備的從地址(relative addr),而且存放在變量card->rca中。筆者使用的WIFI模塊的card->rca = 1

    四、經過發送CMD7,選中相應從地址的卡

    五、經過調用函數mmc_set_clock設置卡工做的時鐘頻率

    六、經過發送CMD52命令,設置4位數據傳輸模式

 

  四、註冊SDIO功能模塊

             

  847行的變量funcs存儲該SDIO卡所包含的IO功能塊的個數,851行到857行就是逐一初始化各個IO功能塊,下面來重點看一下該函數的內容:

              

  第71行就是分配sdio_func結構體變量,該結構體存儲了功能塊的參數。

  第75行就是給功能塊編號,編號是從1到7(由於一個SDIO設備最多隻有7個功能塊),存儲在變量func->num中

  第78行就是讀取SDIO卡中的FBR寄存器中關於該卡的功能類型的數據,存儲在func->class變量中(具體關於FBR寄存器內容,能夠參考SDIO spec文檔)

  第82行就是讀取SDIO卡中的CIS寄存器的內容

 

           

  上面的程序就是將功能模塊逐個的註冊進設備模型,這裏想重點說明一下注冊的名稱(name),它是由三部分組成的,每部分之間用冒號隔開,(即 host的名稱:rca:功能塊編號)。

  具體到筆者使用的WIFI模塊,由於其host名稱是mmc2  ,rca = 1,而且有兩個功能模塊(功能模塊編號分別是1和2),因此在/sys/bus/sdio/devices目錄下能見到以下兩個設備名

    mmc2:0001:1

    mmc2:0001:2

 

  五、註冊SDIO卡

       

  上面的mmc_add_card函數就是註冊card了(這個card是在第3部分,初始化SDIO卡 裏面分配和定義的)

     

        

  第259行就是給card命名,格式爲host名字:從地址,對於筆者的WIFI模塊 就是mmc2:0001

  第261到273行就是根據card->type來分辨出card的類型,給賦予相應的字符串,筆者的WIFI模塊就是"SDIO"

  第275行就是打印信息,具體不解釋 筆者的打印信息爲  mmc2:new high speed SDIO card at address 0001(一般能夠經過查看內核啓動信息中是否有該語句來判斷card是否被正確識別)

  第283行 就是將card註冊進linux設備模型  註冊結果就是能夠在/sys/bus/mmc/devices目錄下見到card 的名字,筆者的就是mmc2:0001

相關文章
相關標籤/搜索