1. 程式人生 > >Linux核心之mmc子系統-sdio

Linux核心之mmc子系統-sdio

現在的Linux核心中,mmc不僅是一個驅動,而是一個子系統。這裡通過分析Linux3.2.0核心,結合TI的arm335x平臺及omap_hsmmcd host分析下mmc子系統,重點關注sdio及架構在其上的具體sdio IP驅動實現。

1.      General overview

1.1 原始碼概覽

Linux kernel把mmc,sd以及sdio三者的驅動程式碼整合在一起,俗稱mmc子系統。原始碼位於drivers/mmc下。其下有三個子目錄,分別是:

 

其中,card用於構建一個塊裝置作為上層與mmc子系統溝通的橋樑;core抽象了mmc,sd,sdio三者的通用操作;host則是各類平臺上的host驅動程式碼,包括如TI Omap的omap_hsmmc,三星的s3cmci等。

1.2 硬體層IP物件間的聯絡

 

即,cpu要訪問slave必須通過host進行,包括slave的中斷。omap host的具體組成及其與slave之間的連線如圖1.1所示:

 

圖1.1

和別的中斷控制器一樣,host的MPU中斷子系統有一個仲裁機制,分別使用IE和ISE控制是否向CPU報中斷以及是否care slave報出來的中斷。

Host在資料傳輸時支援8bit模式,但slave是sdio時僅支援1-bit & 4-bit模式,當支援4-bit模式時,data1和IRQ線複用。

注意:MMCHS的smart-idle wake up line(SWAKEUP)沒有連出去,在別的一些host中,它是和PRCM子系統連線,用於在MMCHS處於suspend時自動提醒PRCM給MMCHS提供clock。

2.      從bus,driver,device看mmc子系統

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

2.1 mmc下的bus,driver,device模型

Mmc子系統涉及到三條匯流排。

Host驅動相應的driver和device掛載在Linux核心內建的虛擬抽象匯流排platform_bus_type。兩者的匹配採用名稱匹配的方式,即driver和device兩者的name一樣則認為該device對應該driver,這裡是”omap_hsmmc”。

Card驅動相應的driver和device掛載在mmc自己建立的虛擬匯流排mmc_bus_type下,直接匹配。

Sdio驅動相應的driver和device掛載在mmc自己建立的虛擬匯流排sdio_bus_type下,ID匹配。

注意:Linux核心中,匹配函式預設使用bus註冊的匹配函式,如果bus沒有註冊則使用driver註冊的匹配函式。所以,一般自己建立虛擬匯流排時,其匹配函式和driver的匹配函式都是一致的。

2.2 按時間順序觀察mmc中各bus,driver,device物件初始化流程

2.2.1 Host device物件

Host device物件首先被初始化並掛載到platform_bus_type,但是這個過程不在mmc子系統原始碼下,它在平臺初始化過程中init了,具體的流程參考5.1。

2.2.2 mmc_bus,sdio_bus物件

core初始化時,件core.c中的subsys_initcall(mmc_init)建立這兩條mmc自己的虛擬匯流排。

2.2.3 card driver物件

Card初始化時,檔案block.c中module_init(mmc_blk_init);函式建立mmcblk driver物件,並將之掛載到mmc_bus_type總線上。

2.2.4 host driver物件

Host初始化時,檔案omap_hsmmc.c中module_init(omap_hsmmc_init);函式建立”omap_hsmmc”driver物件。

2.2.5 card device物件

到2.2.4為止,host的device和driver都已經建立,匹配後呼叫host對應的probe函式:omap_hsmmc_probe()。該函式將進行檢測slave、初始化slave等操作。

檢測sdio並建立card device物件。換言之,這個device物件並沒有對應的硬體裝置,它只是抽象出來用於和他人互動的一個虛擬device。具體的流程參考5.1。

2.2.6 sdio device物件

緊接著2.2.5後面,在函式sdio_init_func()函式中將建立一個device物件並掛載到sdio_bus_type上。具體的流程參考5.1。

2.2.7 小結

到此為止,mmc下三條匯流排、六個device/driver物件只差一個sdio driver了。這個driver物件對應著具體的sdio IP驅動,比如sdio_wifi, sdio_uart等。這樣來看sdio IP驅動其實是構建在mmc子系統之上的。

3.      上層與mmc以及mmc內部模組之間的互動方式

從軟體層面看mmc內部模組互動以及外部訪問mmc的方式,如圖3.1所示,。

 

圖3.1

如之前所言,mmc內部的core模組是各類host、sd及sdio操作的抽象,訪問host的通用行為都放在其中,而各類host將其各自獨特的行為註冊到host規定的介面上。Mmc提供了兩種方式供外部訪問,mmc子系統本身對外呈現的是一個塊裝置,應用層通過VFS到具體的FS再到塊裝置逐層訪問,塊裝置通過core讓host和slave進行通訊,sd卡的訪問就是這種典型方式。另外一種方式是不通過塊裝置訪問core,而是在驅動層新建一個驅動,這個驅動構建在mmc之上,讓它有許可權訪問core,具有sdio介面的IP驅動就可以這麼做。至於這個sdio的IP驅動對外以何種方式呈現由它自己決定,抽象為一個塊裝置或者字元裝置都可以。

需要注意的是:core內部訪問host的操作有睡眠動作,一般的訪問行為是這樣的:

1)   獲取host資源(mmc_claim_host),可能sleep;

2)   傳送訪問請求(如讀/寫);

3)   等待(block)本次訪問結果(無論slave是否response,host都會通過中斷給出返回值);

4)   釋放host資源(mmc_release_host)。

4.      sdio

4.1 sdio和sd/mmc的差異

sdio事件通過card interrupt通知host,在host已有的中斷處理過程中進行,如圖4.1所示。

 

圖4.1

但是sdio的中斷事件(以CIRQ bit位表示)的處理和別的事件有點差異。Sdio中斷事件的處理可能需要sleep。

比如讀操作,它的流程是:1)來一個CIRQ表明sdio中的FIFO已經達到閾值;2)host告訴上層這個事件;3)sdio中斷處理例程將去sdio FIFO中讀取資料,它是通過host的標準操作進行的,如之前所言,這個過程會阻塞。而同樣的讀操作對sd卡則不需要,它的流程是:1)host發出讀操作;2)slave把準備好的資料放到host的FIFO中並assert中斷;3)host處理自己的FIFO並通知上層讀操作結束。

所以對於sdio而言,host只是充當sdio和上層的溝通媒介,它不是sdio slave的controller,sdio IP有自己的controller,有自己的FIFO。

4.2 TI Omap平臺下的sdio中斷處理實現

截至3.12核心版本,Sdio在TI的Omap Soc下仍然沒有實現以中斷方式處理sdio事件。具體點說,即mmc子系統軟體框架已經考慮了sdio中斷,但是針對omap平臺的host驅動(omap_hsmmc.c)沒有支援,它直接忽略了中斷暫存器SD_STAT中的CIRQ。

4.2.1為什麼host不支援sdio中斷?

究其原因,應該是因為某些平臺的host沒有swakeup line(smart-idle-wakeup),比如arm335x。看一下圖4.1所示的mmc子系統的大致硬體架構和host的硬體組成就大致明白了。

Host包括hsmmc和PRCM等子模組,其中PRCM提供clock,電源管理時host可能會進入suspend模式,該模式下PRCM不提供clock,所以此時host將無法處理CIRQ中斷,除非上層強制host wake up,否則host不會智慧恢復正常工作。而如果有swakeup line的時候,當有CIRQ時,通過swakeup line,PRCM將自動供clock。這一點在圖1.1中可以看的更清楚。

4.2.2 mmc子系統如何實現sdio中斷方式

假設不考慮上述沒有swakeup line,進入suspend狀態的hsmmc無法在有卡中斷時自動開始恢復工作的情況,或者說我們已經找到了某種解決辦法後,考慮下host,sdio等mmc子系統模組如何配合實現使用中斷方式處理sdio事件。

圖4.2表明了在當前mmc子系統軟體架構下可行的中斷處理方式。

 

圖4.2

請注意圖中給出的注意事項。

5.      Appendix

5.1  平臺初始化流程

5.1.1 Linux核心平臺初始化

Linux核心將平臺相關的具體資料和模組的特定初始化函式分別放在兩個平臺相關的檔案中。核心提供一個框架,並呼叫各平臺提供的註冊函式。

以omap arm33xx為例,板級初始化相關函式放在檔案board-am335xevm.c中,由kernel框架呼叫;板級各模組特定資訊放在omap_hwmod_33xx_data.c中,並由omap_hwmod.c中提供的函式進行呼叫。

注意:現在Linux下已經不用這種方式儲存板級資訊,取而代之的是device tree,它用一種更簡潔的方式來描述這些特定平臺資訊,從而讓kernel程式碼更加清晰,具體的可以參考http://blog.csdn.net/21cnbao/article/details/8457546

5.1.2 平臺環境檔案

平臺環境檔案board-am335xevm.c定義了各模組的初始化函式,以及提供核心初始化必要的平臺初始化函式:

複製程式碼
 1 MACHINE_START(AM335XEVM, "xxxx")
 2 
 3 /* Maintainer: Texas Instruments */
 4 
 5 .atag_offset         = 0x100,
 6 
 7 .map_io               = am335x_evm_map_io,
 8 
 9 .init_early  = am33xx_init_early,
10 
11 .init_irq      = ti81xx_init_irq,
12 
13 .handle_irq     = omap3_intc_handle_irq,
14 
15 .timer                  = &omap3_am33xx_timer,
16 
17 .init_machine      = am335x_evm_init,
18 
19 MACHINE_END
複製程式碼

上面這段程式碼註冊了核心在start_kernel()中會呼叫的函式,如map_io提供將各IP暫存器地址到核心虛擬地址的轉換,init_early()函式由start_kernel()--->setup_arch()--->mdesc->init_early()呼叫,其餘的init_irq,handle_irq()等會在start_kernel()中分別呼叫。其中init_machine()函式用於初始化各IP模組的device抽象物件,熟悉Linux驅動架構的應該就清楚了,這就是bus,driver,device三劍客之一的device初始化的地方。

舉例來說,init_machine()這個函式被呼叫流程是:start_kernel()--->rest_init()--->kernel_init()-àdo_basic_setup()-àdo_initcalls()--->customize_machine()--->init_machine()。

 

5.1.3 平臺數據檔案及其呼叫者

可以看到init_machine()函式在do_initcalls()中處於第三優先順序,第一優先順序的是core_init(),它會使用omap_hwmod.c提供的相關函式在device初始化前作一些操作。

omap_hwmod_33xx_data.c大部分內容是定義板子各模組的資源資訊如clock、irq號、模組暫存器相應的實體地址等。

do_initcalls()時,core_initcall(omap_hwmod_setup_all)---> _setup()(對每個模組依次作此操作)。

所以平臺初始化操作除MACHINE_START提供的幾個函式外,還有一個地方就是這裡的omap_hwmod_setup_all。

以各模組的sysconfig暫存器初始配置為例:

core_initcall(omap_hwmod_setup_all);

  omap_hwmod_for_each(_setup, NULL);//列舉平臺omap_hwmod_33xx_data.c中定義的omap_hwmod(每個模組的暫存器設定值)

_setup

   ++++++++++++ 以下是clock相關 +++++++++

         _enable

                   _enable_sysc

                            _set_clockactivity

                            _write_sysconfig

                                     omap_hwmod_write(v, oh, oh->class->sysc->sysc_offs);

注意:omap相關模組暫存器預設值一般情況下並非都為0,以mmc的sysconfig為例,其預設值是0x2015。

5.2 slave檢測流程

如2.2.5所言,host的device和driver建立並匹配後呼叫host對應的probe函式:omap_hsmmc_probe()。其中一個重要的步驟是啟用一個工作佇列進行slave檢測並建立後續相關device/driver物件。

Host驅動為了相容多種host controller,有些操作對於SDIO是不需要的(下面用刪除線mark了),但是給SDIO這樣的操作,SDIO不應報錯。具體流程如下所示:

mmc_alloc_host

INIT_DELAYED_WORK(&host->detect, mmc_rescan);

mmc_add_host(mmc);

mmc_start_host(host);

mmc_power_off(host);

          mmc_detect_change(host, 0);

             mmc_schedule_delayed_work(&host->detect, delay);

mmc_rescan

mmc_rescan_try_freq

sdio_reset(host); //使用CMD52設定slave暫存器6(CCCR)的RES bit

                    mmc_go_idle(host);

                   mmc_send_if_cond(host, host->ocr_avail);

mmc_attach_sdio(host)

         mmc_send_io_op_cond(host, 0, &ocr);//使用CMD5命令,獲取OCR值(slave支援的電壓範圍)

         mmc_attach_bus(host, &mmc_sdio_ops);//設定host的bus操作函式是mmc_sdio_ops

         mmc_select_voltage(host, ocr);//設定電壓為選取電壓(slave本身支援的電壓範圍 & 平臺環境選取的電壓ocr_avail==ocr_mask)的最小值

mmc_sdio_init_card(host, host->ocr, NULL, 0);

                   mmc_alloc_card(host, NULL);//device_init,建立card device,指定為mmc_bus_type

                   host->ops->init_card == omap_hsmmc_init_card //沒設定,為空

                   mmc_send_relative_addr(host, &card->rca);//CMD3,獲取slave的rca

                   mmc_select_card(card);//CMD7,選中rca對應的slave

                   sdio_read_cccr(card);//read CCCR

                   sdio_read_common_cis(card);// read CIS

                   // set high speed, set bus width and so on...

sdio_init_func

         sdio_alloc_func//建立device,指定為sdio_bus_type

         sdio_read_fbr

sdio_read_func_cis

mmc_add_card//把card device掛載到mmc_bus_type匯流排

                             sdio_add_func//把sdio device掛載到sdio_bus_type匯流排

6.      Reference

  1. Linux kernel source code(version3.2.0)
  2. Linux kernel 最新patch
  3. AM335x ARM® Cortex™-A8 Microprocessors(MPUs) Technical Reference Manual
  4. SDIO Simplified Specification
  5. SD Host Controller Simplified Specification
轉自:http://www.cnblogs.com/RandyQ/p/3607107.html

相關推薦

Linux核心mmc子系統-sdio

現在的Linux核心中,mmc不僅是一個驅動,而是一個子系統。這裡通過分析Linux3.2.0核心,結合TI的arm335x平臺及omap_hsmmcd host分析下mmc子系統,重點關注sdio及架構在其上的具體sdio IP驅動實現。 1.      Genera

⑳tiny4412 Linux驅動開發MMC子系統驅動程式

本次我們來說一下SDIO子系統的控制器的開發部分,這部分也是和硬體平臺相關的,在說這個之前,我們先來了解一下相關硬體的基礎知識和概念. MMC MMC全稱MultiMedia Card,由西門子公司和SanDisk公司1997年推出的多媒體記憶卡標準。MMC卡尺寸為32mm

Linux驅動輸入子系統簡析

ans 沒有 procfs 通過 sel spa 函數 minor ifdef 輸入子系統由驅動層、輸入子系統核心、事件處理層三部分組成。一個輸入事件,如鼠標移動、鍵盤按下等通過Driver->Inputcore->Event handler->users

Linux核心vmlinux與vmlinuz

因為是初次系統的學習Linux核心,過程中遇到了一些常常出現的名詞。似曾相識,但對他們的含義又不是非常清楚。因此,將搜尋到的內容進行一下彙總。 1.vmlinux   vmlinux是一個包括linux kernel的靜態連結的可執行檔案。檔案型別是l

linux核心class介紹(三)

前兩節介紹了class,device結構體的成員,在這一節主要介紹class和device的關鍵函式. 1.classes_init() 系統呼叫路徑: start_kernel()->>rest_init()->>kernel_init()->>do_b

linux核心class介紹(二)

在最底層,在linux系統裡每個裝置由一個device結構體表示.device結構體包含了裝置模型核心需要的資訊 大部分subsystem,還會增加額外的資訊.因此很少單獨由一個device表示一個裝置;而是像kobject一樣嵌入到 一個更大的結構體來表示一個裝置. device結構體定義

linux核心class介紹(一)

class的概念 class是裝置的更高層檢視,抽象出了底層的實現細節.驅動程式會區分SCSI硬碟和ATA硬碟,但在class層他們都是 硬碟.classes幫助使用者空間只需要知道這些是什麼裝置,而不需要關心他們是怎麼連線和工作的.(A class is a higher-level vi

linux核心klist連結串列

klist介面提供了兩個封裝了list_head的結構體,連結串列頭klist和連結串列節點klist_node 對於結構體klist包含一個spinlock來提供訪問保護.klist_node包含一個klist指標指向 歸屬的klist和kref引用計數器指示該節點的引用次數. 連結串列頭

linux核心的I2C子系統

1、I2C匯流排彙總概念   (1)三根訊號線:SCL、SDA、GND   (2)同步、序列、電平、低速、近距離   (3)匯流排式結構,支援多個裝置掛接在同一條總線上   (4)主從式結構,通訊雙方必須一個為主(master)一個為從(slave),主裝置掌握每次通訊的主動權,從裝置按照主裝置的節奏

linux核心spin lock

轉自:http://www.wowotech.net/kernel_synchronization/spinlock.html 一、前言 在linux kernel的實現中,經常會遇到這樣的場景:共享資料被中斷上下文和程序上下文訪問,該如何保護呢?如果只有程序上下文的訪問,那麼可以考慮使用s

linux 核心completion

    如果核心中一個任務需要發出訊號通知另外一個任務發生了某個特定事件,使用完成量completion是兩個任得以同步的最簡單方法。當任務1需要執行某些工作時,需要等待任務2完成特定操作才能繼續執行,那麼任務1就會阻塞等待,當任務2的特定操作執行完成之後,通過complet

linux核心USB驅動分析

第一部分  USB驅動程式框架   app:   -------------------------------------------   USB裝置驅動程式    // 知道資料含義  核心 --------------------------------------   USB匯流

Linux核心GDB基本除錯方法

Oops[#1]:Cpu 0$ 0   : 00000000 10008d00 00000000 ffffffea$ 4   : fffffdfd 10008d01 00000001 00000000$ 8   : 00000000 7fed2e40 00001cb2 00000b3b$12   : 0003

Linux核心禁止中斷和禁止核心搶佔

禁止中斷指的是Linux核心停工了一組介面用於操作機器上的中斷狀態。這些介面為我們提供了能夠禁止當前處理器的中斷系統,或者遮蔽掉整個機器的一條中斷線的能力。通過禁止中斷,可以確保某個中斷處理程式不會搶佔當前的程式碼。控制中斷系統在Linux的實現有很多,以local_irq

linux核心offset_of和container_of理解

無意間在騰訊課堂上看到有個老師講解linux核心連結串列,開始就講這兩個巨集。 這篇文章主要是為了記錄對這兩個巨集的使用和理解。 測試環境: win10 64bit 家庭版 gcc version 6.3.0 (MinGW.org GCC-6.3.0-1)

Linux核心時間管理子系統——時鐘源

struct clocksource { /* * Hotpath data, fits in a single cache line when the * clocksource itself is cacheline aligned.

linux核心排程演算法(二)

  上層排程,linux排程的核心函式為schedule,schedule函式封裝了核心排程的框架。細節實現上呼叫具體的排程類中的函式實現。schedule函式主要流程為: 1,將當前程序從相應的執行佇列中刪除; 2,計算和更新排程實體和程序的相關排程資訊; 3,將當前進重

linux核心程序管理詳解

1、程序描述符 (1)程序與執行緒          程序是處於執行期的程式以及相關資源的總稱。執行緒在linux上稱為輕量級程序,沒有獨立的地址空間,一個程序下的所有執行緒共享地址空間、檔案系統資源、檔案描述符、訊號處理程式等。 (2)程序描述符task_struct

LINUX驅動SPI子系統三基本的呼叫流程

這裡有一處說明,因為SPI用的是spi_device這個裝置結構,其中與platform_device不同的是沒有.resource這個成員,所以就不用再考慮它了。它通過在s3c24xx_spi_probe函式裡: res = platform_get_r

linux核心chdir分析

今天我們看一些在linux系統裡邊經常使用的cd命令對應的核心實現,就是sys_chdir函式的實現。 sys_chdir函式在fs/open.c裡,定義如下asmlinkage long sys_c