天天看點

Linux kernel mmc 架構說明,包括mmc_test使用方法

FROM:https://blog.csdn.net/kivy_xian/article/details/53333831

請看原文,排版看得友善,轉載留作記錄,謝謝kivy_xian

1.Linux 總線模型

        Linux下的任何驅動在核心中最終都抽象為bus, driver以及device三者間的互相作用。

        總線是處理器和一個或多個裝置之間的通道,在裝置模型中,所有的裝置都通過總線相連接配接。總線将裝置和驅動綁定,在系統每注冊一個裝置的時候,會周遊該總線上的driver list,通過bus的math函數尋找與之比對的驅動;相反的,在系統每注冊一個驅動的時候,會便利該總線上的device 尋找與之比對的裝置,而比對由總線的match函數完成。一但比對,則會調用總線的probe函數。

        在此模型下,如果存在實際總線當然很好,比如mmc總線,i2c總線和spi總線,相應的device和driver都可以直接注冊在總線上。但是總是有一些裝置和總線無關,為此,linux kernel引入了platform 虛拟總線,

Platform總線是一種虛拟的總線,相應的裝置則為platform_device,通過platform_driver_register;而驅動則為platform_driver,通過platform_driver_register 注冊。核心中該總線定義如下:

struct bus_type platform_bus_type = {

       .name            = "platform",

       .dev_groups= platform_dev_groups,

       .match          = platform_match,

       .uevent         = platform_uevent,

       .pm        = &platform_dev_pm_ops,

};

    platform_總線match采用名稱匹 配的方式,即driver和device兩者的name一樣則認為該device對應該driver,詳見下圖:

Linux kernel mmc 架構說明,包括mmc_test使用方法

2.MMC 簡介

MMC

MMC全稱MultiMedia Card,由西門子公司和SanDisk公司1997年推出的多媒體記憶卡标準。MMC卡尺寸為32mm x24mm x 1.4mm,它将存貯單元和控制器一同做到了卡上,智能的控制器使得MMC保證相容性和靈活性。

MMC卡具有MMC和SPI兩種工作模式,MMC模式是預設工作模式,具有MMC的全部特性。而SPI模式則是MMC協定的一個子集,主要用于低速系統。

SD

        SD卡全稱Secure DigitalMemory Card,由松下、東芝和SanDisk公司于1999年8月共同開發的新一代記憶卡标準,已完全相容MMC标準。SD卡比MMC卡多了一個進行資料著作權保護的暗号認證功能。

        SD卡尺寸為32mm x 24mm x2.1mm,長寬和MMC卡一樣,隻是比MMC卡厚了0.7mm,以容納更大容量的存貯單元。SD卡與MMC卡保持向上相容,也就是說,MMC卡可以被新的設有SD卡插槽的裝置存取,但是SD卡卻不可以被設有MMC插槽的裝置存取。

SDIO

        SDIO全稱Secure DigitalInput and Output Card,SDIO是在SD标準上定義了一種外設接口,它使用SD的I/O接口來連接配接外圍裝置,并通過SD上的I/O資料接口與這些外圍裝置傳輸資料。現在已經有很多手持裝置支援SDIO功能,而且許多SDIO外設也被開發出來,目前常見的SDIO外設有:WIFI Card、GPS Card、 Bluetooth Card等等。

eMMC

        eMMC全稱Embedded MultiMediaCard,是MMC協會所制定的内嵌式存儲器标準規格,主要應用于智能手機和移動嵌入式産品等。eMMC是一種嵌入式非易失性存儲系統,由閃存和閃存控制器兩部分組成,它的一個明顯優勢是在封裝中內建了一個閃存控制器,它采用JEDEC标準BGA封裝,并采用統一閃存接口管理閃存。

        eMMC結構由一個嵌入式存儲解決方案組成,帶有MMC接口、快閃儲存設備及主要制器,所有這些由一個小型BGA封裝。由于采用标準封裝,eMMC也很容易更新,并不用改變硬體結構。

        eMMC的這種将Nand Flash晶片和控制晶片封裝在一起的設計概念,就是為了簡化産品記憶體儲器的使用,客戶隻需要采購eMMC晶片放進産品中,不需要處理其它複雜的Nand Flash相容性和管理問題,減少研發成本和研發周期。

3.MMC 子產品總線模型

mmc子系統涉及到三條總線,如下:

Host驅動相應的driver和device挂載在Linux核心内置的虛拟抽象總線platform_bus_type。兩者的比對采用名稱比對的方式,即driver和device兩者的name一樣則認為該device對應該driver,這裡是”rda,hsmmc”。

Card驅動相應的driver和device挂載在mmc自己建立的虛拟總線mmc_bus_type下,直接比對。

Sdio驅動相應的driver和device挂載在mmc自己建立的虛拟總線sdio_bus_type下,ID比對。

按照時間先後順序,mmc子產品中bus,device和driver的注冊順序如下:

3.1.host device

linux kernel 通過下圖所示流程,解析dts檔案mmc子產品相關配置,生成名為rda,hsmmc的platformdevice,挂在platform平台總線上。

Linux kernel mmc 架構說明,包括mmc_test使用方法

2.2.mmc_bus, sdio_bus

mmc core初始化時,檔案core.c中的subsys_initcall(mmc_init), 調用mmc_register_bus和sdio_register_bus注冊mmc bus 和sdio bus,具體如圖下圖所示。

Linux kernel mmc 架構說明,包括mmc_test使用方法

由上圖可以看出,任何挂在mmc總線上的device 和driver都會比對,而挂在sdio總線上的裝置和deriver需要通過id進行比對。

2.3.card driver

    card目錄下,block.c中module_init(mmc_blk_init)調用mmc_register_driver函數建立mmcblk driver,并将之挂載到mmc_bus_type總線的driver list連結清單上。

    注意:mmc core提供了mmc_test.c作為mmc driver的測試檔案。mmc_test.c中,module_init(mmc_test_init)函數中,調用mmc_register_driver函數建立了mmc_test driver,并且将之挂載在mmc_bus_type總線的driver list連結清單上。

mmc子產品如果需要使用mmc_test功能,需要把CONFIG_MMC_BLOKC宏關閉,然後把CONFIG_MMC_BLOCK=y。否則,card将會比對blockdreiver,不會再次比對mmc_test  driver,具體見下圖。

Linux kernel mmc 架構說明,包括mmc_test使用方法

2.4.host driver

    host目錄下,rda_sgmmc.c檔案中,module_init(rda_mmc_init),把rda_mmc_init連結到相應的init字段中。在初始化時候,執行rda_mmc_init(),調用platform_driver_register注冊名為rda,hsmmc的host driver,此driver挂在platform虛拟總線上。

2.5. card device

在第四步中,hostdriver注冊成功後,platform總線會把此driver和1中注冊的host dev比對,然後執行host driver的probe 函數rda_mmc_probe。該函數中完成mmc_alloc_host申請mmc host 結構體,然後完成初始化,以及中斷等的申請等,最後調用mmc_add_host完成card 的探測,如果card存在,生成card device。

因為sd卡支援熱插拔,是以在probe階段,sd卡沒有插入的情況下,不會生成card device。而是在後期插入sd卡時候,産生中斷,中斷處理函數中調用mmc_detect_change函數探測sd card是否存在,完成初始化并且生成card device。具體見下圖。

Linux kernel mmc 架構說明,包括mmc_test使用方法

從上圖中,我們并沒有看到card裝置的生成。這是因為我們通過detect work完成了sd,mmc,或sdio裝置的初始化,并且生成相應的block裝置,具體見下圖:

Linux kernel mmc 架構說明,包括mmc_test使用方法

2.6. card driver執行

    在第五步中,card device已經挂到mmc總線上,此時會比對到carddriver,自動執行driver 的probe函數。如果在3中,CONFIG_MMC_BLOCK=y,則會執行mmc_blk_probe,生成block裝置。如果CONFIG_MMC_BLOCK=n, CONFIG_MMC_TEST=y,則會自動執行mmc_test_probe。

綜上所述,裝置通過dts檔案把mmc host dev(platform裝置)挂在虛拟平台總線上,然後注冊sdio和mmc總線,緊接着注冊card driver,挂在mmc總線上。其後,在host目錄下,我們自己的driver中,調用platform_driver_register注冊platform平台驅動,即host driver。此時,platform會比對host dev 和driver,執行driver的probe函數,通過mmc_add_host注冊card

device,挂載到mmc總線上。最後,mmc總線會比對card dev 和driver,執行card driver的probe函數,生成block裝置或者mmc_test相關的屬性檔案。

4.MMC 協定實作

到此為止,mmc 的相關架構已經介紹清楚了。因為在架構明了的情況下,驅動已經很簡單了,是以後面僅僅會簡單介紹一下mmc驅動。

sd,sdio或者mmc的協定實作在哪裡呢?可以在圖5中看到,初始化就是在mmc_attach_sdio, mmc_attach_sd或者mmc_attach_mmc函數中。

1.初始化時候,首先發送cmd0使卡進入idle狀态;

2.接着發送cmd8,檢測卡是否SD2.0。SD1.1不支援cmd8,是以如果發送cmd8無回應,則卡為SD1.1,否則就是SD2.0;

3. mmc_attach_sdio發送cmd5讀取OCR 寄存器,判斷是否為sdio,如果是就綁定sdio總線;

4. mmc_attach_sd發送指令acmd55、cmd41,使卡進入工作狀态。如果通過,則認為是sd卡,綁定sd總線。mmc卡不支援acmd55,和cmd41,是以如果無回應,則認為卡是mmc卡;

5. mmc_attach_mmc中發送指令1,判斷是否為mmc卡,如果回應,則綁定mmc總線。如果cmd1無回應,則不是mmc卡。

具體實作和協定強相關,隻要對照協定來看,很簡單,就不再多述了。

    ls kernel/drivers/mmc,可以看到如下三個目錄:

card core host

card目錄下主要是完成了上章中所述的mmc總線driver,也就是2.3所述的card driver,

core目錄主要是完成了上章中sdio bus和mmc bus注冊,card device的添加,host目錄就是具體的host driver的添加了。

在mmc driver中,需要做的很重要的一部就是實作mmc_host_ops,在probe函數中把其指派給mmc_host 結構體的ops變量,rda_sgmmc.c的mmc_host_ops實作如下:

static const struct mmc_host_ops rda_mmc_ops= {

       .request        = rda_mmc_request,

       .get_ro         = rda_mmc_get_ro,

       .get_cd         = rda_mmc_get_cd,

       .set_ios        = rda_mmc_set_ios,

        .enable_sdio_irq =rda_mmc_enable_sdio_irq,

};

其中,request函數,主要實作指令發送,資料的讀寫;set_ios主要用來設定資料速度,mmc相位,power mode 和data bus width;get_cd用來檢測裝置是否存在;get_ro用來判斷mmc是否為read-only card;enable_sdio_irq是用來使能或者關閉sdio中斷。

mmc dev正常讀寫的時候調用流程是怎麼樣呢?怎麼和上面注冊的mmc_host_ops關聯起來呢?塊裝置的讀寫會被放入request_queue。見下圖:

Linux kernel mmc 架構說明,包括mmc_test使用方法

其中,blk_fetch_request等涉及到block檔案的讀寫實作方式,這裡不再叙述,有興趣的話大家可以看看代碼。

由此,mmc card dev就和上面注冊的mmc_host_ops關聯起來了。

5.MMC test driver 使用

在第二章介紹card driver時候已經介紹到了mmc test driver了。如果要使用mmc_test function,可以按照如下步驟:

1.CONFIG_MMC_BLOCK=n,CONFIG_MMC_TEST=y。或者CONFIG_MMC_BLOCK=y, CONFIG_MMC_TEST=y。如果選用後一種配置,需要再系統起來後,在總線driver中手動bind和unbind,見後面;

2.CONFIG_DEBUG_FS=y,CONFIG_DEBUG_KERNEL=y,這兩項在我們項目的kernel的defconfig中已經配置,是以不需要進行改動;

3. mount -t debugfs none /sys/kernel/debug,這個我們的project中已經挂載了,不需要進行改動;

完成上面三項後,啟動系統,如果在步驟1中CONFIG_MMC_BLOCK=y,那麼需要先執行執行如下操作:

etau:/ # ls sys/bus/mmc/devices/                                              

mmc0:aaaa

etau:/sys/bus/mmc/drivers # ls

mmc_test/ mmcblk/

etau:/sys/bus/mmc/drivers # cd mmcblk/                                         

etau:/sys/bus/mmc/drivers/mmcblk # ls -al

total 0

drwxr-xr-x 2 root root    0 2000-01-01 01:27 .

drwxr-xr-x 4 root root    0 2000-01-01 01:27 ..

--w------- 1 root root 4096 2000-01-01 01:28bind

lrwxrwxrwx 1 root root    0 2000-01-01 01:28 mmc0:aaaa ->../../../../devices/soc0/20a50000.rda-mmc0/mmc_host/mmc0/mmc0:aaaa

--w------- 1 root root 4096 2000-01-01 01:28uevent

--w------- 1 root root 4096 2000-01-01 01:28unbind

etau:/sys/bus/mmc/drivers/mmcblk #echo –n mmc0:aaaa> unbind

etau:/sys/bus/mmc/drivers # cdmmc_test/                                      

etau:/sys/bus/mmc/drivers/mmc_test # ls

bind uevent unbind

etau:/sys/bus/mmc/drivers/mmc_test #echo –n mmc0:aaaa> bind

完成上述操作後,完成了mmc0:aaaa和mmc_test driver的綁定。後續步驟完全一緻,如下:

etau:/sys/kernel/debug/mmc0/mmc0:aaaa# cat testlist                          

1:     Basic write (no data verification)

2:     Basic read (no data verification)

3:     Basic write (with data verification)

4:     Basic read (with data verification)

5:     Multi-block write

6:     Multi-block read

7:     Power of two block writes

8:     Power of two block reads

9:     Weird sized block writes

10:    Weird sized block reads

11:    Badly aligned write

12:    Badly aligned read

13:    Badly aligned multi-block write

14:    Badly aligned multi-block read

15:    Correct xfer_size at write (start failure)

16:    Correct xfer_size at read (start failure)

17:    Correct xfer_size at write (midway failure)

18:    Correct xfer_size at read (midway failure)

19:    Highmem write

20:    Highmem read

21:    Multi-block highmem write

22:    Multi-block highmem read

23:    Best-case read performance

24:    Best-case write performance

25:    Best-case read performance into scattered pages

26:     Best-case write performance from scatteredpages

27:    Single read performance by transfer size

28:    Single write performance by transfer size

29:    Single trim performance by transfer size

30:    Consecutive read performance by transfer size

31:    Consecutive write performance by transfer size

32:    Consecutive trim performance by transfer size

33:    Random read performance by transfer size

34:    Random write performance by transfer size

35:    Large sequential read into scattered pages

36:    Large sequential write from scattered pages

37:    Write performance with blocking req 4k to 4MB

38:    Write performance with non-blocking req 4k to 4MB

39:    Read performance with blocking req 4k to 4MB

40:    Read performance with non-blocking req 4k to 4MB

41:    Write performance blocking req 1 to 512 sg elems

42:    Write performance non-blocking req 1 to 512 sg elems

43:    Read performance blocking req 1 to 512 sg elems

44:    Read performance non-blocking req 1 to 512 sg elems

45:    Reset test

然後執行etau:/sys/kernel/debug/mmc0/mmc0:aaaa# echo 1 > test可以看到測試結果:

[314034.644348] mmc0: Starting tests of cardmmc0:aaaa...

[314034.645080] mmc0: Test case 1. Basicwrite (no data verification)...

[314034.647583] mmc0: Result: OK

[314034.647827] mmc0: Tests completed.

至此,mmc子產品就告一段落了。

————————————————

版權聲明:本文為CSDN部落客「kivy_xian」的原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結及本聲明。

原文連結:https://blog.csdn.net/kivy_xian/article/details/53333831