一、sdhci pltfm說明 sdhci pltfm並不是實際某個host的driver。 sdhci pltfm是指在sdhci core的基礎上,提供了統一對sdhci_host的必要屬性進行解析和設置的方法。 但是,對於sdhci類的host driver來說,使用sdhci pltfm並不 ...
一、sdhci-pltfm說明
sdhci-pltfm並不是實際某個host的driver。
sdhci-pltfm是指在sdhci core的基礎上,提供了統一對sdhci_host的必要屬性進行解析和設置的方法。
但是,對於sdhci類的host driver來說,使用sdhci-pltfm並不是必須的,host driver也可以自己來實現對應的操作。
通過《host(第二章)——sdhci》,我們知道了host driver調用sdhci_add_host註冊sdhci_host的之前需要設置的信息如下:
- sdhci的寄存器的映射過後的基地址(sdhci_host->ioaddr)
- sdhci的癖好quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2)
- sdhci的中斷號(sdhci_host->irq)
- host提供給sdhci core用來操作硬體的操作集(sdhci_host->ops)
因此,sdhci-pltfm實現了兩個方法來統一設置這些信息,方便host driver對於sdhci driver的使用。
後續繼續說明。
二、數據結構說明
1、sdhci_pltfm_data
首先看一下sdhci-pltfm要設置的sdhci_host的成員的來源信息:
- sdhci的寄存器的映射過後的基地址(sdhci_host->ioaddr)
由DTS節點中的地址屬性解析出來寄存器的物理地址之後,進行映射得到
- sdhci的癖好quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2)
由平臺host驅動(host driver)提供最基本的值,後續會進行調整
- sdhci的中斷號(sdhci_host->irq)
由DTS節點中的中斷屬性解析出來
- host提供給sdhci core用來操作硬體的操作集(sdhci_host->ops)
由平臺host驅動(host driver)提供
綜上,ops、quirks和quirks2這幾個的值是必須由平臺host驅動(host driver)提供,而ioaddr和irq可以通過解析屬性得到。
因此,sdhci-pltfm把ops、quirks和quirks2的值封裝到sdhci_pltfm_data中,由底層host驅動提供。
其內容如下:
struct sdhci_pltfm_data {
const struct sdhci_ops *ops; // host提供給sdhci core用來操作硬體的操作集
unsigned int quirks; // sdhci的癖好quirks
unsigned int quirks2; // sdhci的癖好quirks2
};
2、sdhci_pltfm_host
sdhci_pltfm也為host抽象出一個host結構體sdhci_pltfm_host來作為sdhci_host和平臺定製的host的中間層。
struct sdhci_pltfm_host {
struct clk *clk;
/* migrate from sdhci_of_host */
unsigned int clock;
u16 xfer_mode_shadow;
unsigned long private[0] ____cacheline_aligned;
};
以高通定製的host結構體sdhci_msm_host為例,三者之間的關係如下:
sdhci_host->private = sdhci_pltfm_host
sdhci_pltfm_host->priv = sdhci_msm_host
platform_get_drvdata(sdhci_msm_host->struct platform_device) = sdhci_host
sdhci_pltfm_host = sdhci_priv(sdhci_host);
sdhci_msm_host= sdhci_pltfm_priv(sdhci_pltfm_host);
三、API總覽
1、sdhci_pltfm分配和釋放相關
- sdhci_pltfm_init & sdhci_pltfm_free
由底層host driver調用。
sdhci_pltfm_init 用於分配sdhci_pltfm_host和sdhci_host的部分成員進行設置。
sdhci_pltfm_free用於釋放sdhci_pltfm_host和sdhci_host。
原型:struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata)
參數說明:struct platform_device *pdev——》host對應的平臺設備的device
struct sdhci_pltfm_data *pdata——》需要host driver提供給sdhci_host的一些信息,前面說過了
使用案例:sdhci_pltfm_init(pdev, &msm_host->sdhci_msm_pdata);
原型:void sdhci_pltfm_free(struct platform_device *pdev)
2、屬性解析相關
- sdhci_get_of_property
由底層host driver調用。
用來解析host的dtsi節點的部分屬性,前提是要求這部分屬性必須按照一定的規範來。
原型:void sdhci_get_of_property(struct platform_device *pdev)
3、sdhci_host註冊相關
- sdhci_pltfm_register & sdhci_pltfm_unregister
由底層host driver調用。
sdhci_pltfm_register 直接根據sdhci_pltfm_data來註冊一個sdhci_host,會調用上述的sdhci_pltfm_init 和sdhci_get_of_property操作。
註意,但是一般用得比較少,因為host driver得到sdhci_host可能需要根據自己的需求來設置sdhci_host,而不是馬上註冊sdhci_host。
原型:int sdhci_pltfm_register(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata)
參數說明:struct platform_device *pdev——》host對應的平臺設備的device
struct sdhci_pltfm_data *pdata——》需要host driver提供給sdhci_host的一些信息,前面說過了
四、介面代碼說明
1、sdhci_pltfm_init
主要工作
- 調用sdhci_alloc_host分配一個sdhci_host
- 根據sdhci_pltfm_data設置sdhci_host->ops
- 根據sdhci_pltfm_data設置sdhci_host->quirks
- 根據sdhci_pltfm_data設置sdhci_host->quirks2
- 獲取dtsi節點中的第一個中斷屬性並申請,設置sdhci_host->irq
- 獲取dtsi節點中的第一個寄存器屬性並映射,設置sdhci_host->ioaddr
- 調用平臺的初始化操作
具體代碼如下
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata)
{
// struct platform_device:sdhci host的平臺設備
// const struct sdhci_pltfm_data:sdhci host的平臺數據結構體,包含了對應host的sdhci_ops操作集
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct device_node *np = pdev->dev.of_node;
struct resource *iomem;
int ret;
/* 獲取sdhci記憶體資源區域 */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); // 所以在dtsi節點中,sdhci的記憶體資源必須作為記憶體列表的第一個屬性!!!!
/* 調用sdhci_alloc_host獲取一個標準的struct sdhci_host結構體 */
host = sdhci_alloc_host(&pdev->dev, sizeof(*pltfm_host));
/* 將struct sdhci_host的私有數據和struct sdhci_pltfm_host關聯 */
pltfm_host = sdhci_priv(host);
/* 根據傳進來的sdhci host的平臺數據來初始化sdhci_host的ops、quirks */
host->hw_name = dev_name(&pdev->dev);
host->ops = pdata->ops;
host->quirks = pdata->quirks;
/* 獲取中斷 */
host->irq = platform_get_irq(pdev, 0); // 所以在dtsi節點中,sdhci的中斷屬性必須作為中斷列表的第一個屬性!!!!!
/* 申請sdhci的記憶體資源並且進行映射 */
if (!request_mem_region(iomem->start, resource_size(iomem),
mmc_hostname(host->mmc))) {
dev_err(&pdev->dev, "cannot request region\n");
ret = -EBUSY;
goto err_request;
}
host->ioaddr = ioremap(iomem->start, resource_size(iomem));
if (!host->ioaddr) {
dev_err(&pdev->dev, "failed to remap registers\n");
ret = -ENOMEM;
goto err_remap;
}
/* 調用host->ops->platform_init進行初始化 */
if (host->ops && host->ops->platform_init)
host->ops->platform_init(host);
/* 將struct sdhci_host作為對應host的私有數據 */
platform_set_drvdata(pdev, host);
return host;
}
註意,通過上述代碼,sdhci-pltfm要求必須把sdhci的寄存器屬性放在host的dtsi的寄存器屬性的第一個,同時,也要把sdhci的中斷屬性放在host的dtsi的中斷屬性的第一個。簡單dtsi的例子如下圖所示:
sdhc_1: sdhci@07824000 {
reg = <0x07824900 0x11c>, <0x07824000 0x800>;
reg-names = "hc_mem", "core_mem"; // 其中,hc_mem表示sdhci的寄存器屬性,放在了第一個
interrupts = <0 123 0>, <0 138 0>;
interrupt-names = "hc_irq", "pwr_irq"; // 其中,hc_irq表示sdhci的中斷,放在了第一個
2、sdhci_get_of_property
代碼如下:
void sdhci_get_of_property(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct sdhci_host *host = platform_get_drvdata(pdev); // 從平臺設備結構體中獲取私有數據,對應就是sdhci_host
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); // sdhci_host的私有護具就是struct sdhci_pltfm_host
const __be32 *clk;
u32 bus_width;
int size;
if (of_device_is_available(np)) {
if (of_get_property(np, "sdhci,auto-cmd12", NULL))
host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;
// 解析"sdhci,auto-cmd12"屬性,設置quirks的SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12標識
// sdhci,auto-cmd12————》SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12
// Controller uses Auto CMD12 command to stop the transfer,控制器使用CMD12自動結束傳輸
if (of_get_property(np, "sdhci,1-bit-only", NULL) ||
(of_property_read_u32(np, "bus-width", &bus_width) == 0 &&
bus_width == 1))
host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
// 解析"sdhci,1-bit-only"屬性,設置quirks的SDHCI_QUIRK_FORCE_1_BIT_DATA標識
// sdhci,1-bit-only————》SDHCI_QUIRK_FORCE_1_BIT_DATA
// Controller can only handle 1-bit data transfers,該sdhci controller只支持1bit位寬傳輸
if (sdhci_of_wp_inverted(np))
host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
// sdhci,wp-inverted | wp-inverted————》SDHCI_QUIRK_INVERTED_WRITE_PROTECT
if (of_get_property(np, "broken-cd", NULL))
host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;
// broken-cd————》SDHCI_QUIRK_BROKEN_CARD_DETECTION
// Controller has unreliable card detection,sdhci controller沒有實現card檢測
if (of_get_property(np, "no-1-8-v", NULL))
host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;
// no-1-8-v————》SDHCI_QUIRK2_NO_1_8_V
// The system physically doesn't support 1.8v, even if the host does,不支持1.8V
clk = of_get_property(np, "clock-frequency", &size);
if (clk && size == sizeof(*clk) && *clk)
pltfm_host->clock = be32_to_cpup(clk);
// clock-frequency————》pltfm_host->clock
// 獲取時鐘頻率
if (of_find_property(np, "keep-power-in-suspend", NULL))
host->mmc->pm_caps |= MMC_PM_KEEP_POWER;
if (of_find_property(np, "enable-sdio-wakeup", NULL))
host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}
}
3、sdhci_pltfm_register
使用得比較少,簡單瞭解下即可。
int sdhci_pltfm_register(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata)
{
struct sdhci_host *host;
int ret = 0;
/* 調用sdhci_pltfm_init分配並初始化一個sdhci_host */
host = sdhci_pltfm_init(pdev, pdata);
if (IS_ERR(host))
return PTR_ERR(host);
/* 調用sdhci_get_of_property解析dtsi屬性並設置sdhci_host的部分成員 */
sdhci_get_of_property(pdev);
/* 調用sdhci_add_host將該sdhci_host註冊到sdhci core中 */
ret = sdhci_add_host(host);
if (ret)
sdhci_pltfm_free(pdev);
return ret;
}