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,詳見下圖:
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平台總線上。
3.2.mmc_bus, sdio_bus
mmc core初始化時,文件core.c中的subsys_initcall(mmc_init), 調用mmc_register_bus和sdio_register_bus注冊mmc bus 和sdio bus,具體如圖下圖所示。
由上圖可以看出,任何掛在mmc總線上的device 和driver都會匹配,而掛在sdio總線上的設備和deriver需要通過id進行匹配。
3.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,具體見下圖。
3.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虛擬總線上。
3.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。具體見下圖。
從上圖中,我們並沒有看到card設備的生成。這是因為我們通過detect work完成了sd,mmc,或sdio設備的初始化,並且生成相應的block設備,具體見下圖:
3.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。見下圖:
其中,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