1. 前言
本文是Linux MMC framework的第二篇,將從驅(qū)動(dòng)工程師的角度,介紹MMC host controller driver有關(guān)的知識,學(xué)習(xí)并掌握如何在MMC framework的框架下,編寫MMC控制器的驅(qū)動(dòng)程序。同時(shí),通過本篇文章,我們會(huì)進(jìn)一步的理解MMC、SD、SDIO等有關(guān)的基礎(chǔ)知識。
2. MMC host驅(qū)動(dòng)介紹
MMC的host driver,是用于驅(qū)動(dòng)MMC host控制器的程序,位于“drivers/mmc/host”目錄。從大的流程上看,編寫一個(gè)這樣的驅(qū)動(dòng)非常簡單,只需要三步:
1)調(diào)用mmc_alloc_host,分配一個(gè)struct mmc_host類型的變量,用于描述某一個(gè)具體的MMC host控制器。
2)根據(jù)MMC host控制器的硬件特性,填充struct mmc_host變量的各個(gè)字段,例如MMC類型、電壓范圍、操作函數(shù)集等等。
3)調(diào)用mmc_add_host接口,將正確填充的MMC host注冊到MMC core中。
當(dāng)然,看著簡單,一牽涉到實(shí)現(xiàn)細(xì)節(jié),還是很麻煩的,后面我們會(huì)慢慢分析。
注1:分析MMC host driver的時(shí)候,Linux kernel中有大把大把的例子(例如drivers/mmc/host/pxamci.c),大家可盡情參考、學(xué)習(xí),不必謙虛(這是學(xué)習(xí)Linux的最佳方法)。
注2:由于MMC host driver牽涉到具體的硬件controller,分析的過程中需要一些具體的硬件輔助理解,本文將以“X Project”所使用Bubblegum-96平臺為例,具體的硬件spec可參考[1]。
3. 主要數(shù)據(jù)結(jié)構(gòu)
3.1 struct mmc_host
MMC core使用struct mmc_host結(jié)構(gòu)抽象具體的MMC host controller,該結(jié)構(gòu)的定義位于“include/linux/mmc/host.h”中,它既可以用來描述MMC控制器所具有的特性、能力(host driver關(guān)心的內(nèi)容),也保存了host driver運(yùn)行過程中的一些狀態(tài)、參數(shù)(MMC core關(guān)心的內(nèi)容)。需要host driver關(guān)心的部分的具體的介紹如下:
parent,一個(gè)struct device類型的指針,指向該MMC host的父設(shè)備,一般是注冊該host的那個(gè)platform設(shè)備;
class_dev,一個(gè)struct device類型的變量,是該MMC host在設(shè)備模型中作為一個(gè)“設(shè)備”的體現(xiàn)。當(dāng)然,人如其名,該設(shè)備從屬于某一個(gè)class(mmc_host_class);
ops,一個(gè)struct mmc_host_ops類型的指針,保存了該MMC host有關(guān)的操作函數(shù)集,具體可參考3.2小節(jié)的介紹;
pwrseq,一個(gè)struct mmc_pwrseq類型的指針,保存了該MMC host電源管理有關(guān)的操作函數(shù)集,具體可參考3.2小節(jié)的介紹;
f_min、f_max、f_init,該MMC host支持的時(shí)鐘頻率范圍,最小頻率、最大頻率以及初始頻率;
ocr_avail,該MMC host可支持的操作電壓范圍(具體可參考include/linux/mmc/host.h中MMC_VDD_開頭的定義);
注3:OCR(Operating Conditions Register)是MMC/SD/SDIO卡的一個(gè)32-bit的寄存器,其中有些bit指明了該卡的操作電壓。MMC host在驅(qū)動(dòng)這些卡的時(shí)候,需要和Host自身所支持的電壓范圍匹配之后,才能正常操作,這就是ocr_avail的存在意義。
ocr_avail_sdio、ocr_avail_sd、ocr_avail_mmc,如果MMC host針對SDIO、SD、MMC等不同類型的卡,所支持的電壓范圍不同的話,需要通過這幾個(gè)字段特別指定。否則,不需要賦值(初始化為0);
pm_notify,一個(gè)struct notifier_block類型的變量,用于支持power management有關(guān)的notify實(shí)現(xiàn);
max_current_330、max_current_300、max_current_180,當(dāng)工作電壓分別是3.3v、3v以及1.8v的時(shí)候,所支持的最大操作電流(如果MMC host沒有特別的限制,可以不賦值);
caps、caps2,指示該MMC host所支持的功能特性,具體可參考3.4小節(jié)的介紹;
pm_caps,mmc_pm_flag_t類型的變量,指示該MMC host所支持的電源管理特性;
max_seg_size、max_segs、max_req_size、max_blk_size、max_blk_count、max_busy_timeout,和塊設(shè)備(如MMC、SD、eMMC等)有關(guān)的參數(shù),在古老的磁盤時(shí)代,這些參數(shù)比較重要。對基于MMC技術(shù)的塊設(shè)備來說,硬件的性能大大提升,這些參數(shù)就沒有太大的意義了。具體可參考5.2章節(jié)有關(guān)MMC數(shù)據(jù)傳輸?shù)慕榻B;
lock,一個(gè)spin lock,是MMC host driver的私有變量,可用于保護(hù)host driver的臨界資源;
ios,一個(gè)struct mmc_ios類型的變量,用于保存MMC bus的當(dāng)前配置,具體可參考3.5小節(jié)的介紹;
supply,一個(gè)struct mmc_supply類型的變量,用于描述MMC系統(tǒng)中的供電信息,具體可參考3.6小節(jié)的介紹;
……
private,一個(gè)0長度的數(shù)組,可以在mmc_alloc_host時(shí)指定長度,由host controller driver自行支配。
3.2 struct mmc_host_ops
struct mmc_host_ops抽象并集合了MMC host controller所有的操作函數(shù)集,包括:
1)數(shù)據(jù)傳輸有關(guān)的函數(shù)
/*?
* It is optional for the host to implement pre_req and post_req in?
* order to support double buffering of requests (prepare one?
* request while another request is active).?
* pre_req() must always be followed by a post_req().?
* To undo a call made to pre_req(), call post_req() with?
* a nonzero err condition.?
*/?
void??? (*post_req)(struct mmc_host *host, struct mmc_request *req,?
??????????????????? int err);?
void??? (*pre_req)(struct mmc_host *host, struct mmc_request *req,?
?????????????????? bool is_first_req);?
void??? (*request)(struct mmc_host *host, struct mmc_request *req);
pre_req和post_req是非必需的,host driver可以利用它們實(shí)現(xiàn)諸如雙buffer之類的高級功能。
數(shù)據(jù)傳輸?shù)闹黝}是struct mmc_request類型的指針,具體可參考3.7小節(jié)的介紹。
2)總線參數(shù)的配置以及卡狀態(tài)的獲取函數(shù)
/*?
* Avoid calling these three functions too often or in a "fast path",?
* since underlaying controller might implement them in an expensive?
* and/or slow way.?
*?
* Also note that these functions might sleep, so don't call them?
* in the atomic contexts!?
*?
* Return values for the get_ro callback should be:?
*?? 0 for a read/write card?
*?? 1 for a read-only card?
*?? -ENOSYS when not supported (equal to NULL callback)?
*?? or a negative errno value when something bad happened?
*?
* Return values for the get_cd callback should be:?
*?? 0 for a absent card?
*?? 1 for a present card?
*?? -ENOSYS when not supported (equal to NULL callback)?
*?? or a negative errno value when something bad happened?
*/?
void??? (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);?
int???? (*get_ro)(struct mmc_host *host);?
int???? (*get_cd)(struct mmc_host *host);
set_ios用于設(shè)置bus的參數(shù)(ios,可參考3.5小節(jié)的介紹);get_ro可獲取card的讀寫狀態(tài)(具體可參考上面的注釋);get_cd用于檢測卡的存在狀態(tài)。
注4:注釋中特別說明了,這幾個(gè)函數(shù)可以sleep,耗時(shí)較長,沒事別亂用。
3)其它一些非主流函數(shù),都是optional的,用到的時(shí)候再去細(xì)看即可。
3.3 struct mmc_pwrseq
MMC framework的power sequence是一個(gè)比較有意思的功能,它提供一個(gè)名稱為struct mmc_pwrseq_ops的操作函數(shù)集,集合了power on、power off等操作函數(shù),用于控制MMC系統(tǒng)的供電,如下:
struct mmc_pwrseq_ops {?
??????? void (*pre_power_on)(struct mmc_host *host);?
??????? void (*post_power_on)(struct mmc_host *host);?
??????? void (*power_off)(struct mmc_host *host);?
??????? void (*free)(struct mmc_host *host);?
};
struct mmc_pwrseq {?
??????? const struct mmc_pwrseq_ops *ops;?
};
與此同時(shí),MMC core提供了一個(gè)通用的pwrseq的管理模塊(drivers/mmc/core/pwrseq.c),以及一些簡單的pwrseq策略(如drivers/mmc/core/pwrseq_simple.c、drivers/mmc/core/pwrseq_emmc.c),最終的目的是,通過一些簡單的dts配置,即可正確配置MMC的供電,例如:
/* arch/arm/boot/dts/omap3-igep0020.dts */?
mmc2_pwrseq: mmc2_pwrseq {?
??????? compatible = "mmc-pwrseq-simple";?
??????? reset-gpios = <&gpio5 11 GPIO_ACTIVE_LOW>,????? /* gpio_139 - RESET_N_W */
????????????????????? <&gpio5 10 GPIO_ACTIVE_LOW>;????? /* gpio_138 - WIFI_PDN */
};
/* arch/arm/boot/dts/rk3288-veyron.dtsi? */?
emmc_pwrseq: emmc-pwrseq {?
??????? compatible = "mmc-pwrseq-emmc";?
??????? pinctrl-0 = <&emmc_reset>;?
??????? pinctrl-names = "default";?
??????? reset-gpios = <&gpio2 9 GPIO_ACTIVE_HIGH>;?
};
具體的細(xì)節(jié),在需要的時(shí)候,閱讀代碼即可,這里不再贅述。
3.4 Host capabilities
通過的caps和caps2兩個(gè)字段,MMC host driver可以告訴MMC core控制器的一些特性、能力,包括(具體可參考include/linux/mmc/host.h中相關(guān)的宏定義,更為細(xì)致的使用場景和指南,需要結(jié)合實(shí)際的硬件,具體分析):
MMC_CAP_4_BIT_DATA,支持4-bit的總線傳輸;?
MMC_CAP_MMC_HIGHSPEED,支持“高速M(fèi)MC時(shí)序”;?
MMC_CAP_SD_HIGHSPEED,支持“高速SD時(shí)序”;?
MMC_CAP_SDIO_IRQ,可以產(chǎn)生SDIO有關(guān)的中斷;?
MMC_CAP_SPI,僅僅支持SPI協(xié)議(可參考“drivers/mmc/host/mmc_spi.c”中有關(guān)的實(shí)現(xiàn));?
MMC_CAP_NEEDS_POLL,表明需要不停的查詢卡的插入狀態(tài)(如果需要支持熱拔插的卡,則需要設(shè)置該feature);?
MMC_CAP_8_BIT_DATA,支持8-bit的總線傳輸;?
MMC_CAP_AGGRESSIVE_PM,支持比較積極的電源管理策略(kernel的注釋為“Suspend (e)MMC/SD at idle”);?
MMC_CAP_NONREMOVABLE,表明該MMC控制器所連接的卡是不可拆卸的,例如eMMC;?
MMC_CAP_WAIT_WHILE_BUSY,表面Host controller在向卡發(fā)送命令時(shí),如果卡處于busy狀態(tài),是否等待。如果不等待,MMC core在一些流程中(如查詢busy狀態(tài)),需要額外做一些處理;
MMC_CAP_ERASE,表明該MMC控制器允許執(zhí)行擦除命令;?
MMC_CAP_1_8V_DDR,支持工作電壓為1.8v的DDR(Double Data Rate)mode[3];?
MMC_CAP_1_2V_DDR,支持工作電壓為1.2v的DDR(Double Data Rate)mode[3];?
MMC_CAP_POWER_OFF_CARD,可以在啟動(dòng)之后,關(guān)閉卡的供電(一般只有在SDIO的應(yīng)用場景中才可能用到,因?yàn)镾DIO所連接的設(shè)備可能是一個(gè)獨(dú)立的設(shè)備);
MMC_CAP_BUS_WIDTH_TEST,支持通過CMD14和CMD19進(jìn)行總線寬度的測試,以便選擇一個(gè)合適的總線寬度進(jìn)行通信;?
MMC_CAP_UHS_SDR12、MMC_CAP_UHS_SDR25、MMC_CAP_UHS_SDR50、MMC_CAP_UHS_SDR104,它們是SD3.0的速率模式,分別表示支持25MHz、50MHz、100MHz和208MHz的SDR(Single Data Rate,相對[3]中的DDR)模式;
MMC_CAP_UHS_DDR50,它也是SD3.0的速率模式,表示支持50MHz的DDR(Double Data Rate[3])模式;?
MMC_CAP_DRIVER_TYPE_A、MMC_CAP_DRIVER_TYPE_C、MMC_CAP_DRIVER_TYPE_D,分別表示支持A/C/D類型的driver strength(驅(qū)動(dòng)能力,具體可參考相應(yīng)的spec);
MMC_CAP_CMD23,表示該controller支持multiblock transfers(通過CMD23);?
MMC_CAP_HW_RESET,支持硬件reset;
MMC_CAP2_FULL_PWR_CYCLE,表示該controller支持從power off到power on的完整的power cycle;
MMC_CAP2_HS200_1_8V_SDR、MMC_CAP2_HS200_1_2V_SDR,HS200是eMMC5.0支持的一種速率模式,200是200MHz的縮寫,分別表示支持1.8v和1.2v的SDR模式;
MMC_CAP2_HS200,表示同時(shí)支持MMC_CAP2_HS200_1_8V_SDR和MMC_CAP2_HS200_1_2V_SDR;?
MMC_CAP2_HC_ERASE_SZ,支持High-capacity erase size;?
MMC_CAP2_CD_ACTIVE_HIGH,CD(Card-detect)信號高有效;?
MMC_CAP2_RO_ACTIVE_HIGH,RO(Write-protect)信號高有效;?
MMC_CAP2_PACKED_RD、MMC_CAP2_PACKED_WR,允許packed read、packed write;?
MMC_CAP2_PACKED_CMD,同時(shí)支持DMMC_CAP2_PACKED_RD和MMC_CAP2_PACKED_WR;?
MMC_CAP2_NO_PRESCAN_POWERUP,在scan之前不要上電;?
MMC_CAP2_HS400_1_8V、MMC_CAP2_HS400_1_2V,HS400是eMMC5.0支持的一種速率模式,400是400MHz的縮寫,分別表示支持1.8v和1.2v的HS400模式;
MMC_CAP2_HS400,同時(shí)支持MMC_CAP2_HS400_1_8V和MMC_CAP2_HS400_1_2V;?
MMC_CAP2_HSX00_1_2V,同時(shí)支持MMC_CAP2_HS200_1_2V_SDR和MMC_CAP2_HS400_1_2V;?
MMC_CAP2_SDIO_IRQ_NOTHREAD,SDIO的IRQ的處理函數(shù),不能在線程里面執(zhí)行;?
MMC_CAP2_NO_WRITE_PROTECT,沒有物理的RO管腳,意味著任何時(shí)候都是可以讀寫的;?
MMC_CAP2_NO_SDIO,在初始化的時(shí)候,不會(huì)發(fā)送SDIO相關(guān)的命令(也就是說不支持SDIO模式)。
3.5 struct mmc_ios
struct mmc_ios中保存了MMC總線當(dāng)前的配置情況,包括如下信息:
1)clock,時(shí)鐘頻率。
2)vdd,卡的供電電壓,通過“1 << vdd”可以得到MMC_VDD_x_x(具體可參考include/linux/mmc/host.h中MMC_VDD_開頭的定義),進(jìn)而得到電壓信息。
3)bus_mode,兩種信號模式,open-drain(MMC_BUSMODE_OPENDRAIN)和push-pull(MMC_BUSMODE_PUSHPULL),對應(yīng)不同的高低電平(可參考相應(yīng)的spec,例如[2])。
4)chip_select,只針對SPI模式,指定片選信號的有效模式,包括沒有片選信號(MMC_CS_DONTCARE)、高電平有效(MMC_CS_HIGH)、低電平有效(MMC_CS_LOW)。
5)power_mode,當(dāng)前的電源狀態(tài),包括MMC_POWER_OFF、MMC_POWER_UP、MMC_POWER_ON和MMC_POWER_UNDEFINED。
6)bus_width,總線的寬度,包括1-bit(MMC_BUS_WIDTH_1)、4-bit(MMC_BUS_WIDTH_4)和8-bit(MMC_BUS_WIDTH_8)。
7)timing,符合哪一種總線時(shí)序(大多對應(yīng)某一類MMC規(guī)范),包括:
MMC_TIMING_LEGACY,舊的、不再使用的規(guī)范;?
MMC_TIMING_MMC_HS,High speed MMC規(guī)范(具體可參考相應(yīng)的spec,這里不再詳細(xì)介紹,下同);?
MMC_TIMING_SD_HS,High speed SD;?
MMC_TIMING_UHS_SDR12;?
MMC_TIMING_UHS_SDR25?
MMC_TIMING_UHS_SDR50?
MMC_TIMING_UHS_SDR104?
MMC_TIMING_UHS_DDR50?
MMC_TIMING_MMC_DDR52?
MMC_TIMING_MMC_HS200?
MMC_TIMING_MMC_HS400
8)signal_voltage,總線信號使用哪一種電壓,3.3v(MMC_SIGNAL_VOLTAGE_330)、1.8v(MMC_SIGNAL_VOLTAGE_180)或者1.2v(MMC_SIGNAL_VOLTAGE_120)。
9)drv_type,驅(qū)動(dòng)能力,包括:
MMC_SET_DRIVER_TYPE_B?
MMC_SET_DRIVER_TYPE_A?
MMC_SET_DRIVER_TYPE_C?
MMC_SET_DRIVER_TYPE_D
3.6 struct mmc_supply
struct mmc_supply中保存了兩個(gè)struct regulator指針(如下),用于控制MMC子系統(tǒng)有關(guān)的供電(vmmc和vqmmc)。
struct mmc_supply {?
??????? struct regulator *vmmc;???????? /* Card power supply */?
??????? struct regulator *vqmmc;??????? /* Optional Vccq supply */?
};
關(guān)于vmmc和vqmmc,說明如下:
vmmc是卡的供電電壓,一般連接到卡的VDD管腳上。而vqmmc則用于上拉信號線(CMD、CLK和DATA[6])。
通常情況下vqmmc使用和vmmc相同的regulator,同時(shí)供電即可。
后來,一些高速卡(例如UHS SD)要求在高速模式下,vmmc為3.3v,vqmmc為1.8v,這就需要兩個(gè)不同的regulator獨(dú)立控制。
3.7 struct mmc_request
struct mmc_request封裝了一次傳輸請求,定義如下:
/* include/linux/mmc/core.h */
struct mmc_request {?
??????? struct mmc_command????? *sbc;?????????? /* SET_BLOCK_COUNT for multiblock */
??????? struct mmc_command????? *cmd;?
??????? struct mmc_data???????? *data;?
??????? struct mmc_command????? *stop;
struct completion?????? completion;?
??????? void??????????????????? (*done)(struct mmc_request *);/* completion function */
??????? struct mmc_host???????? *host;?
};
要理解這個(gè)數(shù)據(jù)結(jié)構(gòu),需要先了解MMC的總線協(xié)議(bus protocol),這里以eMMC[2]為例進(jìn)行簡單的介紹(更為詳細(xì)的解釋,可參考相應(yīng)的spec以及本站的文章--“eMMC 原理 4 :總線協(xié)議[7]”)。
3.7.1 MMC bus protocol
在eMMC的spec中,稱總線協(xié)議為“message-based MultiMediaCard bus protocol”,這里的message由三種信標(biāo)(token)組成:
Command,用于啟動(dòng)(或者停止)一次傳輸,由Host通過CMD line向Card發(fā)送;
Response,用于應(yīng)答上一次的Command,由Card通過CMD line想Host發(fā)送;
Data,傳輸數(shù)據(jù),由Host(或者Card)通過DATA lines向Card(或者Host發(fā)送)。
以上token除了Command之外,剩余的兩個(gè)(Response和Data)都是非必需的,也就是說,一次傳輸可以是:不需要應(yīng)答、不需要數(shù)據(jù)傳輸?shù)腃ommand;需要應(yīng)答、不需要數(shù)據(jù)傳輸?shù)腃ommand;不需要應(yīng)答、需要數(shù)據(jù)傳輸?shù)腃ommand;不需要應(yīng)答、不需要數(shù)據(jù)傳輸?shù)腃ommand。
Command token的格式只有一種(具體可參考[2]中“Command token format”有關(guān)的表述),長度為48bits,包括Start bit(0)、Transmitter bit(1, host command)、Content(38bits)、CRC checksum(7bits)、Stop bit(1)。
根據(jù)內(nèi)容的不同,Response token的格式有5中,分別簡稱為R1/R3/R4/R5/R2,其中R1/R3/R4/R5的長度為48bits,R2為136bits(具體可參考[2]中“Response token format”有關(guān)的表述)。
對于包含了Data token的Command,有兩種類型:
Sequential commands,發(fā)送Start command之后,數(shù)據(jù)以stream的形式傳輸,直到Stop command為止。這種方式只支持1-bit總線模式,主要為了兼容舊的技術(shù),一般不使用;
Block-oriented commands,發(fā)送Start command之后,數(shù)據(jù)以block的形式傳輸(每個(gè)block的大小是固定的,且都由CRC保護(hù))。
最后,以block為單位的傳輸,大體上也分為兩類:
在傳輸開始的時(shí)候(Start command),沒有指定需要傳輸?shù)腷lock數(shù)目,直到發(fā)送Stop command為止。這種方法在spec中稱作“Open-ended”;
在傳輸開始的時(shí)候(Start command),指定需要傳輸?shù)腷lock數(shù)據(jù),當(dāng)達(dá)到數(shù)據(jù)之后,Card會(huì)自動(dòng)停止傳輸,這樣可以省略Stop command。這種方法在spec中稱作pre-defined block count。
3.7.2 struct mmc_request
了解MMC bus protocol之后,再來看一次MMC傳輸請求(struct mmc_request )所包含的內(nèi)容:
cmd,Start command,為struct mmc_command類型(具體請參考3.7.3中的介紹)的指針,在一次傳輸?shù)倪^程中是必須的;
data,傳輸?shù)臄?shù)據(jù),為struct mmc_data類型(具體請參考3.7.4中的介紹)的指針,不是必須要的;
stop、sbc,如果需要進(jìn)行數(shù)據(jù)傳輸,根據(jù)數(shù)據(jù)傳輸?shù)姆绞剑▍⒖?.7.1中的介紹):如果是“Open-ended”,則需要stop命令(stop指針,或者data->stop指針);如果是pre-defined block count,則需要sbc指針(用于發(fā)送SET_BLOCK_COUNT--CMD23命令);
completion,一個(gè)struct completion變量,用于等待此次傳輸完成,host controller driver可以根據(jù)需要使用;
done,傳輸完成時(shí)的回調(diào),用于通知傳輸請求的發(fā)起者;
host,對應(yīng)的mmc host controller指針。
3.7.3 struct mmc_command
struct mmc_command結(jié)構(gòu)抽象了一個(gè)MMC command,包括如下內(nèi)容:
/* include/linux/mmc/core.h */
opcode,Command的操作碼,用于標(biāo)識該命令是哪一個(gè)命令,具體可參考相應(yīng)的spec(例如[2]);
arg,一個(gè)Command可能會(huì)攜帶參數(shù),具體可參考相應(yīng)的spec(例如[2]);
resp[4],Command發(fā)出后,如果需要應(yīng)答,結(jié)果保存在resp數(shù)組中,該數(shù)組是32-bit的,因此最多可以保存128bits的應(yīng)答;
flags,是一個(gè)bitmap,保存該命令所期望的應(yīng)答類型,例如:?
MMC_RSP_PRESENT(1 << 0),是否需要應(yīng)答,如果該bit為0,則表示該命令不需要應(yīng)答,否則,需要應(yīng)答;?
MMC_RSP_136(1 << 1),如果為1,表示需要136bits的應(yīng)答;?
MMC_RSP_CRC(1 << 2),如果為1,表示需要對該命令進(jìn)行CRC校驗(yàn);?
等等,具體可參考include/linux/mmc/core.h中“MMC_RSP_”開頭的定義;
retries,如果命令發(fā)送出錯(cuò),可以重新發(fā)送,該字段向host driver指明最多可重發(fā)的次數(shù);
error,如果最終還是出錯(cuò),host driver需要通過該字段返回錯(cuò)誤的原因,kernel定義了一些標(biāo)準(zhǔn)錯(cuò)誤,例如ETIMEDOUT、EILSEQ、EINVAL、ENOMEDIUM等,具體含義可參考include/linux/mmc/core.h中注釋;
busy_timeout,如果card具有busy檢測的功能,該字段指定等待card返回busy狀態(tài)的超時(shí)時(shí)間,單位為ms;
data,和該命令對應(yīng)的struct mmc_data指針;
mrq,和該命令對應(yīng)的struct mmc_request指針。
3.7.4 struct mmc_data
struct mmc_data結(jié)構(gòu)包含了數(shù)據(jù)傳輸有關(guān)的內(nèi)容:
/* include/linux/mmc/core.h */
timeout_ns、timeout_clks,這一筆數(shù)據(jù)傳輸?shù)某瑫r(shí)時(shí)間(單位分別為ns和clks),如果超過這個(gè)時(shí)間host driver還無法成功發(fā)送,則要將狀態(tài)返回給mmc core;
blksz、blocks,該筆數(shù)據(jù)包含多少block(blocks),每個(gè)block的size多大(blksz),這兩個(gè)值不會(huì)大于struct mmc_host中上報(bào)的max_blk_size和max_blk_count;
error,如果數(shù)據(jù)傳輸出錯(cuò),錯(cuò)誤值保存在該字段,具體意義和struct mmc_command中的一致;
flags,一個(gè)bitmap,指明該筆傳說的方向(MMC_DATA_WRITE或者M(jìn)MC_DATA_READ);
sg,一個(gè)struct scatterlist類型的數(shù)組,保存了需要傳輸?shù)臄?shù)據(jù)(可以通過dma_相關(guān)的接口,獲得相應(yīng)的物理地址);
sg_len,sg數(shù)組的size;?
sg_count,通過sg map出來的實(shí)際的entry的個(gè)數(shù)(可能由于物理地址的連續(xù)、IOMMU的干涉等,map出來的entry的個(gè)數(shù),可能會(huì)小于sg的size);
注5:有關(guān)scatterlist的介紹,可參考本站另外的文章(TODO)。有關(guān)struct mmc_data的使用場景,可參考5.2小節(jié)的介紹;
host_cookie,host driver的私有數(shù)據(jù),怎么用由host driver自行決定。
4. 主要API
第3章花了很大的篇幅介紹了用于抽象MMC host的數(shù)據(jù)結(jié)構(gòu)----struct mmc_host,并詳細(xì)說明了和mmc_host相關(guān)的mmc request、mmc command、mmc data等結(jié)構(gòu)。基于這些知識,本章將介紹MMC core提供的和struct mmc_host有關(guān)的操作函數(shù),主要包括如下幾類。
4.1 向MMC host controller driver提供的用于操作struct mmc_host的API
包括:
struct mmc_host *mmc_alloc_host(int extra, struct device *);
int mmc_add_host(struct mmc_host *);?
void mmc_remove_host(struct mmc_host *);?
void mmc_free_host(struct mmc_host *);?
int mmc_of_parse(struct mmc_host *host);?
static inline void *mmc_priv(struct mmc_host *host) {?
??????? return (void *)host->private;?
}
mmc_alloc_host,動(dòng)態(tài)分配一個(gè)struct mmc_host變量。extra是私有數(shù)據(jù)的大小,可通過host->private指針訪問(也可通過mmc_priv接口直接獲?。?。mmc_free_host執(zhí)行相反動(dòng)作。
mmc_add_host,將已初始化好的host變量注冊到kernel中。mmc_remove_host執(zhí)行相反動(dòng)作。
為了方便,host controller driver可以在dts中定義host的各種特性,然后在代碼中調(diào)用mmc_of_parse解析并填充到struct mmc_host變量中。dts屬性關(guān)鍵字可參考mmc_of_parse的source code(drivers/mmc/core/host.c),并結(jié)合第三章的內(nèi)容自行理解。
int mmc_power_save_host(struct mmc_host *host);?
int mmc_power_restore_host(struct mmc_host *host);
從mmc host的角度進(jìn)行電源管理,進(jìn)入/退出power save狀態(tài)。
void mmc_detect_change(struct mmc_host *, unsigned long delay);
當(dāng)host driver檢測到總線上的設(shè)備有變動(dòng)的話(例如卡的插入和拔出等),需要調(diào)用這個(gè)接口,讓MMC core幫忙做后續(xù)的工作,例如檢測新插入的卡到底是個(gè)什么東東……
另外,可以通過delay參數(shù)告訴MMC core延時(shí)多久(單位為jiffies)開始處理,通常可以用來對卡的拔插進(jìn)行去抖動(dòng)。
void mmc_request_done(struct mmc_host *, struct mmc_request *);
當(dāng)host driver處理完成一個(gè)mmc request之后,需要調(diào)用該函數(shù)通知MMC core,MMC core會(huì)進(jìn)行一些善后的操作,例如校驗(yàn)結(jié)果、調(diào)用mmc request的.done回調(diào)等等。
static inline void mmc_signal_sdio_irq(struct mmc_host *host)
void sdio_run_irqs(struct mmc_host *host);
對于SDIO類型的總線,這兩個(gè)函數(shù)用于操作SDIO irqs,后面用到的時(shí)候再分析。
int mmc_regulator_get_ocrmask(struct regulator *supply);
int mmc_regulator_set_ocr(struct mmc_host *mmc,?
??????????????????????? struct regulator *supply,?
??????????????????????? unsigned short vdd_bit);?
int mmc_regulator_set_vqmmc(struct mmc_host *mmc, struct mmc_ios *ios);?
int mmc_regulator_get_supply(struct mmc_host *mmc);
regulator有關(guān)的輔助函數(shù):
mmc_regulator_get_ocrmask可根據(jù)傳入的regulator指針,獲取該regulator支持的所有電壓值,并以此推導(dǎo)出對應(yīng)的ocr mask(可參考3.1中的介紹)。
mmc_regulator_set_ocr用于設(shè)置host controller為某一個(gè)操作電壓(vdd_bit),該接口會(huì)調(diào)用regulator framework的API,進(jìn)行具體的電壓切換。
mmc_regulator_set_vqmmc可根據(jù)struct mmc_ios信息,自行調(diào)用regulator framework的接口,設(shè)置vqmmc的電壓。
最后,mmc_regulator_get_supply可以幫忙從dts的vmmc、vqmmc屬性值中,解析出對應(yīng)的regulator指針,以便后面使用。
4.2 用于判斷MMC host controller所具備的能力的API
比較簡單,可結(jié)合第3章的介紹理解:
#define mmc_host_is_spi(host)?? ((host)->caps & MMC_CAP_SPI)
static inline int mmc_card_is_removable(struct mmc_host *host)?
static inline int mmc_card_keep_power(struct mmc_host *host)?
static inline int mmc_card_wake_sdio_irq(struct mmc_host *host)?
static inline int mmc_host_cmd23(struct mmc_host *host)?
static inline int mmc_boot_partition_access(struct mmc_host *host)?
static inline int mmc_host_uhs(struct mmc_host *host)?
static inline int mmc_host_packed_wr(struct mmc_host *host)?
static inline int mmc_card_hs(struct mmc_card *card)?
static inline int mmc_card_uhs(struct mmc_card *card)?
static inline bool mmc_card_hs200(struct mmc_card *card)?
static inline bool mmc_card_ddr52(struct mmc_card *card)?
static inline bool mmc_card_hs400(struct mmc_card *card)?
static inline void mmc_retune_needed(struct mmc_host *host)?
static inline void mmc_retune_recheck(struct mmc_host *host)
5. MMC host驅(qū)動(dòng)的編寫步驟
經(jīng)過上面章節(jié)的描述,相信大家對MMC controller driver有了比較深的理解,接下來驅(qū)動(dòng)的編寫就是一件水到渠成的事情了。這里簡要描述一下驅(qū)動(dòng)編寫步驟,也順便為本文做一個(gè)總結(jié)。
5.1 struct mmc_host的填充和注冊
編寫MMC host驅(qū)動(dòng)的所有工作,都是圍繞struct mmc_host結(jié)構(gòu)展開的。在對應(yīng)的platform driver的probe函數(shù)中,通過mmc_alloc_host分配一個(gè)mmc host后,我們需要根據(jù)controller的實(shí)際情況,填充對應(yīng)的字段。
mmc host中大部分和controller能力/特性有關(guān)的字段,可以通過dts配置(然后在代碼中調(diào)用mmc_of_parse自動(dòng)解析并填充),舉例如下(注意其中紅色的部分,都是MMC framework的標(biāo)準(zhǔn)字段):
/* arch/arm/boot/dts/exynos5420-peach-pit.dts */
&mmc_1 {?
??????? status = "okay";?
??????? num-slots = <1>;?
????????non-removable;?
????????cap-sdio-irq;?
??????? keep-power-in-suspend;?
????????clock-frequency = <400000000>;
??????? samsung,dw-mshc-ciu-div = <1>;?
??????? samsung,dw-mshc-sdr-timing = <0 1>;?
??????? samsung,dw-mshc-ddr-timing = <0 2>;?
??????? pinctrl-names = "default";?
??????? pinctrl-0 = <&sd1_clk>, <&sd1_cmd>, <&sd1_int>, <&sd1_bus1>,?
??????????????????? <&sd1_bus4>, <&sd1_bus8>, <&wifi_en>;?
????????bus-width = <4>;?
????????cap-sd-highspeed;?
????????mmc-pwrseq = <&mmc1_pwrseq>;
????????vqmmc-supply = <&buck10_reg>;?
};
5.2 數(shù)據(jù)傳輸?shù)膶?shí)現(xiàn)
填充struct mmc_host變量的過程中,工作量最大的,就是對struct mmc_host_ops的實(shí)現(xiàn)(毫無疑問!所有MMC host的操作邏輯都封在這里呢!?。?。這里簡單介紹一下相關(guān)的概念,具體的驅(qū)動(dòng)編寫步驟,后面文章會(huì)結(jié)合“X Project”詳細(xì)描述。
5.2.1 Sectors(扇區(qū))、Blocks(塊)以及Segments(段)的理解
我們在3.1小節(jié)介紹struct mmc_host的時(shí)候,提到了max_seg_size、max_segs、max_req_size、max_blk_size、max_blk_count等參數(shù)。這和磁盤設(shè)備(塊設(shè)備)中Sectors、Blocks、Segments等概念有關(guān),下面簡單介紹一下:
1)Sectors
Sectors是存儲(chǔ)設(shè)備訪問的基本單位。
對磁盤、NAND等塊設(shè)備來說,Sector的size是固定的,例如512、2048等。
對存儲(chǔ)類的MMC設(shè)備來說,按理說也應(yīng)有固定size的sector。但因?yàn)橛蠱MC協(xié)議的封裝,host驅(qū)動(dòng)以及上面的塊設(shè)備驅(qū)動(dòng),不需要關(guān)注物理的size。它們需要關(guān)注的就是bus上的數(shù)據(jù)傳輸單位(具體可參考MMC protocol的介紹[7])。
最后,對那些非存儲(chǔ)類的MMC設(shè)備來說,完全沒有sector的概念了。
2) Blocks
Blocks是數(shù)據(jù)傳輸?shù)幕締挝唬荲FS(虛擬文件系統(tǒng))抽象出來的概念,是純軟件的概念,和硬件無關(guān)。它必須是2的整數(shù)倍、不能大于Sectors的單位、不能大于page的長度,一般為512B、2048B或者4096B。
對MMC設(shè)備來說,由于協(xié)議的封裝,淡化了Sector的概念,或者說,MMC設(shè)備可以支持一定范圍內(nèi)的任意的Block size。Block size的范圍,由兩個(gè)因素決定:
a)host controller的能力,這反映在struct mmc_host結(jié)構(gòu)的max_blk_size字段上。?
b)卡的能力,這可以通過MMC command從卡的CSD(Card-Specific Data)寄存器中讀出。
3)Segments[8]
塊設(shè)備的數(shù)據(jù)傳輸,本質(zhì)上是設(shè)備上相鄰扇區(qū)與內(nèi)存之間的數(shù)據(jù)傳輸。通常情況下,為了提升性能,數(shù)據(jù)傳輸通過DMA方式。
在磁盤控制器的舊時(shí)代,DMA操作都比較簡單,每次傳輸,數(shù)據(jù)在內(nèi)存中必須是連續(xù)的?,F(xiàn)在則不同,很多SOC都支持“分散/聚合”(scatter-gather)DMA操作,這種操作模式下,數(shù)據(jù)傳輸可以在多個(gè)非連續(xù)的內(nèi)存區(qū)域中進(jìn)行。
對于每個(gè)“分散/聚合”DMA操作,塊設(shè)備驅(qū)動(dòng)需要向控制器發(fā)送:?
a)初始扇區(qū)號和傳輸?shù)目偣采葏^(qū)數(shù)?
b)內(nèi)存區(qū)域的描述鏈表,每個(gè)描述都包含一個(gè)地址和長度。不同的描述之間,可以在物理上連續(xù),也可以不連續(xù)。
控制器來管理整個(gè)數(shù)據(jù)傳輸,例如:在讀操作中,控制器從塊設(shè)備相鄰的扇區(qū)上讀取數(shù)據(jù),然后將數(shù)據(jù)分散存儲(chǔ)在內(nèi)存的不同區(qū)域。
這里的每個(gè)內(nèi)存區(qū)域描述(物理連續(xù)的一段內(nèi)存,可以是一個(gè)page,也可以是page的一部分),就稱作Segment。一個(gè)Segment包含多個(gè)相鄰扇區(qū)。
最后,利用“分散/聚合”的DMA操作,一次數(shù)據(jù)傳輸可以會(huì)涉及多個(gè)segments。
理解了Segment的概念之后,max_seg_size和max_segs兩個(gè)字段就好理解了:
雖然控制器支持“分散/聚合”的DMA操作,但物理硬件總有限制,例如最大的Segment size(也即一個(gè)內(nèi)存描述的最大長度),最多支持的segment個(gè)數(shù)(max_segs)等。
5.2.2 struct mmc_data中的sg
我們在3.7.4小節(jié)介紹struct mmc_data時(shí),提到了scatterlist的概念。結(jié)合上面Segment的解釋,就很好理解了:
MMC core提交給MMC host driver的數(shù)據(jù)傳輸請求,是一個(gè)struct scatterlist鏈表(也即內(nèi)存區(qū)域的描述鏈表),也可以理解為是一個(gè)個(gè)的Segment(Segment的個(gè)數(shù)保存在sg_len變量中了)。
每個(gè)Segment是一段物理地址連續(xù)的內(nèi)存區(qū)域,所有的Segments對應(yīng)了MMC設(shè)備中連續(xù)的Sector(或者說Block,初始扇區(qū)號和傳輸?shù)目偣采葏^(qū)數(shù)已經(jīng)在之前的MMC command中指定了。
host driver在接收到這樣的數(shù)據(jù)傳輸請求之后,需要調(diào)用dma_map_sg將這些Segment映射出來(獲得相應(yīng)的物理地址),以便交給MMC controller傳輸。
當(dāng)然,相鄰兩個(gè)Segment的物理地址可能是連續(xù)(或者其它原因),map的時(shí)候可能會(huì)將兩個(gè)Segment合成一個(gè)。因此可供MMC controller傳輸?shù)膬?nèi)存片可能少于sg_len(具體要看dma_map_sg的返回值,可將結(jié)果保存在sg_count中)。
最后,如何實(shí)施傳輸,則要看具體的MMC controller的硬件實(shí)現(xiàn)(可能涉及DMA操作),后面文章再詳細(xì)介紹。
?
評論
查看更多