一、說明 sdhci msm是指高通的mmc host,其使用了標準SDHC標準。故可以使用前面說的《host(第二章)——sdhci》和《host(第三章)——sdhci pltfm說明》的介面。 後續代碼以msm8916平臺的host實現以及linux 4.6.0版本中的sdhci msm的實現 ...
一、說明
sdhci-msm是指高通的mmc host,其使用了標準SDHC標準。故可以使用前面說的《host(第二章)——sdhci》和《host(第三章)——sdhci-pltfm說明》的介面。
後續代碼以msm8916平臺的host實現以及linux 4.6.0版本中的sdhci-msm的實現為例,這部分代碼都是開源的。
由於有一些寄存器內容需要文檔的支撐但我們並沒有,所以這裡只是簡單地介紹一下設計思想和代碼結構。
二、dtsi節點
msm8916有兩個sdhci host,我們以第一個host為例,預設接的是emmc
arch/arm64/boot/dts/qcom/msm8916.dtsi
aliases {
sdhc1 = &sdhc_1; /* SDC1 eMMC slot */
};
sdhc_1: sdhci@07824000 {
compatible = "qcom,sdhci-msm-v4";
// 會和sdhci-msm.c的driver匹配
reg = <0x07824900 0x11c>, <0x07824000 0x800>;
reg-names = "hc_mem", "core_mem";
// 兩部分寄存器,hc_mem表示sdhci使用的寄存器,core_mem表示msm host獨立於sdhci標準之外的、自己需要使用的寄存器
// 因為驅動裡面會使用sdhci-pltfm來進行解析,所以這裡必須把hc_mem放在寄存器的第一個屬性,具體參考《host(第三章)——sdhci-pltfm說明》
interrupts = <0 123 0>, <0 138 0>;
interrupt-names = "hc_irq", "pwr_irq";
// 兩部分中斷,hc_irq表示sdhci使用的中斷,core_mem表示msm host檢測host電源狀態的中斷、獨立於sdhci標準
// 因為驅動裡面會使用sdhci-pltfm來進行解析,所以這裡必須把hc_irq放在中斷的第一個屬性,具體參考《host(第三章)——sdhci-pltfm說明》
clocks = <&gcc GCC_SDCC1_APPS_CLK>,
<&gcc GCC_SDCC1_AHB_CLK>;
clock-names = "core", "iface";
// 兩個時鐘
// core->GCC_SDCC1_APPS_CLK,工作時鐘,也就是輸出時鐘
// iface->GCC_SDCC1_AHB_CLK,匯流排時鐘
bus-width = <8>;
// 匯流排寬度設置為8
non-removable;
// 設置為不可移除
status = "disabled";
};
三、數據結構
1、struct sdhci_msm_host
sdhci-msm host driver根據自身資源定製的host結構體。
struct sdhci_msm_host {
struct platform_device *pdev; // 對應dtsi節點解析出來的平臺設備
void __iomem *core_mem; /* MSM SDCC mapped address */ // host自身的寄存器基地址
struct clk *clk; /* main SD/MMC bus clock */ // 工作時鐘,對應
struct clk *pclk; /* SDHC peripheral bus clock */
struct clk *bus_clk; /* SDHC bus voter clock */
struct mmc_host *mmc; // 對應的mmc_host結構體,具體參考《mmc core》
};
2、sdhci_msm_pdata
struct sdhci_pltfm_data類型。
提供給sdhci-pltfm介面使用的平臺數據結構體,定義了sdhci_host的ops、quirks和quirks2。會在調用sdhci_pltfm_init生成sdhci_host的時候使用。
具體參考《host(第三章)——sdhci-pltfm說明》
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_SINGLE_POWER_WRITE,
.ops = &sdhci_msm_ops,
};
3、sdhci_msm_ops
struct sdhci_ops類型。
為sdhci_host提供出sdhci標準之外的一些實際的硬體操作方法的操作集給sdhci core。
具體參考《host(第二章)——sdhci》
static const struct sdhci_ops sdhci_msm_ops = {
.platform_execute_tuning = sdhci_msm_execute_tuning,
.reset = sdhci_reset,
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
具體後面說明。這個操作集的內容也是sdhci host要實現的核心內容。
四、代碼說明
1、設備驅動模型相關代碼
static struct platform_driver sdhci_msm_driver = {
.probe = sdhci_msm_probe, // probe方法,也就是後面代碼的介紹核心
.remove = sdhci_msm_remove, // remove方法
.driver = {
.name = "sdhci_msm",
.of_match_table = sdhci_msm_dt_match,
},
};
static const struct of_device_id sdhci_msm_dt_match[] = {
{ .compatible = "qcom,sdhci-msm-v4" }, // 以此來和dtsi節點匹配
{},
};
module_platform_driver(sdhci_msm_driver);
2、sdhci_msm_probe
主要工作
- 調用調用sdhci_pltfm_init為sdhci_host、sdhci_pltfm_host、sdhci_msm_host分配記憶體、設置
- 關聯mmc_host、sdhci_host、sdhci_pltfm_host、sdhci_msm_host
- 解析dtsi屬性設置到mmc_host和sdhci_host中
- 獲取各種時鐘
- 獲取host獨立於sdhci的寄存器基地址
- 調用sdhci_add_host將sdhci_host註冊到sdhci core中,相應的mmc_host也會被調用到mmc core中了。host的註冊就完成了。
代碼如下
static int sdhci_msm_probe(struct platform_device *pdev)
{
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_msm_host *msm_host;
struct resource *core_memres;
int ret;
u16 host_version, core_minor;
u32 core_version, caps;
u8 core_major;
host = sdhci_pltfm_init(pdev, &sdhci_msm_pdata, sizeof(*msm_host));
// 調用sdhci_pltfm_init為sdhci_host、sdhci_pltfm_host、sdhci_msm_host分配記憶體
// 並對sdhci_host進行設置,具體可以參考《host(第三章)——sdhci-pltfm說明》
// 這裡傳入了sdhci_msm_pdata會用來設置sdhci_host的相關成員。
// 註意由於和前面文章的kernel版本的差異,這邊的介面可能有點出入
pltfm_host = sdhci_priv(host);
msm_host = sdhci_pltfm_priv(pltfm_host);
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
// 關聯mmc_host、sdhci_host、sdhci_pltfm_host、sdhci_msm_host
// 幾個結構體之間的關係可以參考《host(第一章)——概述》
ret = mmc_of_parse(host->mmc);
// 調用mmc_of_parse解析host的dtsi節點屬性,設置到mmc_host的caps屬性中
// mmc_of_parse屬於mmc core提供的標準介面,具體參考《mmc core——host模塊說明》
sdhci_get_of_property(pdev);
// 調用sdhci_get_of_property解析host的dtsi節點屬性,設置到sdhci_host的quirks和quirks2中
/* Setup SDCC bus voter clock. */
msm_host->bus_clk = devm_clk_get(&pdev->dev, "bus");
// 獲取bus時鐘
/* Setup main peripheral bus clock */
msm_host->pclk = devm_clk_get(&pdev->dev, "iface");
// 獲取iface時鐘到msm_host->pclk中(GCC_SDCC1_AHB_CLK)
ret = clk_prepare_enable(msm_host->pclk);
/* Setup SDC MMC clock */
msm_host->clk = devm_clk_get(&pdev->dev, "core");
// 獲取core時鐘到msm_host->clk中(GCC_SDCC1_AHB_CLK)
core_memres = platform_get_resource(pdev, IORESOURCE_MEM, 1);
msm_host->core_mem = devm_ioremap_resource(&pdev->dev, core_memres);
// 獲取msm host的寄存器基地址
/* Reset the core and Enable SDHC mode */
writel_relaxed(readl_relaxed(msm_host->core_mem + CORE_POWER) |
CORE_SW_RST, msm_host->core_mem + CORE_POWER);
//.......以下過濾一下msm host的複位操作和初始化操作
ret = sdhci_add_host(host);
// 調用sdhci_add_host將得到的sdhci_host註冊到sdhci core中。
return 0;
}