- 了解sdhci-pltfm
1.sdhci-pltfm 介绍
sdhci-pltfm并不是实际某个host的driver。sdhci-pltfm是指在sdhci core的基础上,提供了统一对sdhci_host的必要属性进行解析和设置的方法。
但是,对于sdhci类的host driver来说,使用sdhci-pltfm并不是必须的,host driver也可以自己来实现对应的操作。
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的使用。
2.数据结构
2.1.struct mmc_host_ops
struct mmc_host_ops {
void (*post_req)(struct mmc_host *host, struct mmc_request *req,
int err);
void (*pre_req)(struct mmc_host *host, struct mmc_request *req);
void (*request)(struct mmc_host *host, struct mmc_request *req);
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);
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
/* Mandatory callback when using MMC_CAP2_SDIO_IRQ_NOTHREAD. */
void (*ack_sdio_irq)(struct mmc_host *host);
/* optional callback for HC quirks */
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
int (*start_signal_voltage_switch)(struct mmc_host *host, struct mmc_ios *ios);
/* Check if the card is pulling dat[0:3] low */
int (*card_busy)(struct mmc_host *host);
/* The tuning command opcode value is different for SD and eMMC cards */
int (*execute_tuning)(struct mmc_host *host, u32 opcode);
/* Prepare HS400 target operating frequency depending host driver */
int (*prepare_hs400_tuning)(struct mmc_host *host, struct mmc_ios *ios);
/* Prepare switch to DDR during the HS400 init sequence */
int (*hs400_prepare_ddr)(struct mmc_host *host);
/* Prepare for switching from HS400 to HS200 */
void (*hs400_downgrade)(struct mmc_host *host);
/* Complete selection of HS400 */
void (*hs400_complete)(struct mmc_host *host);
/* Prepare enhanced strobe depending host driver */
void (*hs400_enhanced_strobe)(struct mmc_host *host,
struct mmc_ios *ios);
int (*select_drive_strength)(struct mmc_card *card,
unsigned int max_dtr, int host_drv,
int card_drv, int *drv_type);
void (*hw_reset)(struct mmc_host *host);
void (*card_event)(struct mmc_host *host);
/*
* Optional callback to support controllers with HW issues for multiple
* I/O. Returns the number of supported blocks for the request.
*/
int (*multi_io_quirk)(struct mmc_card *card,
unsigned int direction, int blk_size);
};
2.2.struct sdhci_ops
struct sdhci_ops {
#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS
u32 (*read_l)(struct sdhci_host *host, int reg);
u16 (*read_w)(struct sdhci_host *host, int reg);
u8 (*read_b)(struct sdhci_host *host, int reg);
void (*write_l)(struct sdhci_host *host, u32 val, int reg);
void (*write_w)(struct sdhci_host *host, u16 val, int reg);
void (*write_b)(struct sdhci_host *host, u8 val, int reg);
#endif
void (*set_clock)(struct sdhci_host *host, unsigned int clock);
void (*set_power)(struct sdhci_host *host, unsigned char mode,
unsigned short vdd);
int (*enable_dma)(struct sdhci_host *host);
unsigned int (*get_max_clock)(struct sdhci_host *host);
unsigned int (*get_min_clock)(struct sdhci_host *host);
unsigned int (*get_timeout_clock)(struct sdhci_host *host);
unsigned int (*get_max_timeout_count)(struct sdhci_host *host);
void (*set_timeout)(struct sdhci_host *host,
struct mmc_command *cmd);
void (*set_bus_width)(struct sdhci_host *host, int width);
void (*platform_send_init_74_clocks)(struct sdhci_host *host,
u8 power_mode);
unsigned int (*get_ro)(struct sdhci_host *host);
void (*reset)(struct sdhci_host *host, u8 mask);
int (*platform_execute_tuning)(struct sdhci_host *host, u32 opcode);
void (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs);
void (*hw_reset)(struct sdhci_host *host);
void (*adma_workaround)(struct sdhci_host *host, u32 intmask);
void (*platform_init)(struct sdhci_host *host);
void (*card_event)(struct sdhci_host *host);
void (*voltage_switch)(struct sdhci_host *host);
int (*select_drive_strength)(struct sdhci_host *host,
struct mmc_card *card,
unsigned int max_dtr, int host_drv,
int card_drv, int *drv_type);
};
2.3.sdhci_pltfm_data
struct sdhci_pltfm_data {
const struct sdhci_ops *ops; // host提供给sdhci core用来操作硬件的操作集
unsigned int quirks; // quirks
unsigned int quirks2; // quirks2
};
该结构体定义sdhci_host的ops、quirks和quirks2,在调用sdhci_pltfm_init生成sdhci_host的时候使用。
sdhci-pltfm要设置的sdhci_host的成员的来源信息:
- sdhci 寄存器映射后的基地址(sdhci_host->ioaddr):由DTS节点中的地址属性解析出来寄存器的物理地址之后,调用devm_ioremap_resource进行映射实现。
- sdhci的quirks、quirks2(sdhci_host->quirks,sdhci_host->quirks2):由平台host驱动(host driver)提供最基本的值。
- sdhci的中断号(sdhci_host->irq):由DTS节点中的中断属性解析出来。
- host->mmc_host_ops = sdhci_ops:sdhci host 定义操作硬件的操作集struct mmc_host_ops。
static const struct mmc_host_ops sdhci_ops = {
.request = sdhci_request,
.post_req = sdhci_post_req,
.pre_req = sdhci_pre_req,
.set_ios = sdhci_set_ios,
.get_cd = sdhci_get_cd,
.get_ro = sdhci_get_ro,
.hw_reset = sdhci_hw_reset,
.enable_sdio_irq = sdhci_enable_sdio_irq,
.ack_sdio_irq = sdhci_ack_sdio_irq,
.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,
.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,
.execute_tuning = sdhci_execute_tuning,
.card_event = sdhci_card_event,
.card_busy = sdhci_card_busy,
};
- host->ops = &sdhci_pltfm_ops:sdhci host 定义操作硬件的操作集struct sdhci_ops。
static const struct sdhci_ops sdhci_pltfm_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
综上,ops、quirks和quirks2这几个的值是必须由平台host驱动(host driver)提供,而ioaddr和irq可以通过解析属性得到。
因此,sdhci-pltfm把ops、quirks和quirks2的值封装到sdhci_pltfm_data中,由底层host驱动提供,如下所示:
static const struct sdhci_pltfm_data sdhci_msm_pdata = {
.quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_SINGLE_POWER_WRITE,
.ops = &sdhci_msm_ops,
};
2.2.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为例,三者之间的关系如下:
pltfm_host = sdhci_priv(host);
msm_host = sdhci_pltfm_priv(pltfm_host);
msm_host->mmc = host->mmc;
msm_host->pdev = pdev;
&ensp关联mmc_host、sdhci_host、sdhci_pltfm_host、sdhci_msm_host。
3.APIs
3.1.sdhci_pltfm分配和释放相关
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata);
void sdhci_pltfm_free(struct platform_device *pdev);
- sdhci_pltfm_init 用于分配sdhci_pltfm_host和sdhci_host的部分成员进行设置。
- sdhci_pltfm_free用于释放sdhci_pltfm_host和sdhci_host。
3.2.属性解析相关
void sdhci_get_of_property(struct platform_device *pdev)
- sdhci_get_of_property 由底层host driver调用,用来解析host的dtsi节点的部分属性,前提是要求这部分属性必须按照一定的规范来。
3.3.3.sdhci_host注册
int sdhci_pltfm_register(struct platform_device *pdev, const struct sdhci_pltfm_data *pdata,size_t priv_size);
int sdhci_pltfm_unregister(struct platform_device *pdev);
- sdhci_pltfm_register 直接根据sdhci_pltfm_data来注册一个sdhci_host,会调用上述的sdhci_pltfm_init 和sdhci_get_of_property操作。
Note:
但是一般用得比较少,因为host driver得到sdhci_host可能需要根据自己的需求来设置sdhci_host,而不是马上注册sdhci_host。
4.函数代码说明
4.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,
size_t priv_size)
{
struct sdhci_host *host;
void __iomem *ioaddr;
int irq, ret;
ioaddr = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(ioaddr)) {
ret = PTR_ERR(ioaddr);
goto err;
}
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
ret = irq;
goto err;
}
host = sdhci_alloc_host(&pdev->dev,
sizeof(struct sdhci_pltfm_host) + priv_size);
host->ioaddr = ioaddr;
host->irq = irq;
host->hw_name = dev_name(&pdev->dev);
if (pdata && pdata->ops)
host->ops = pdata->ops;
else
host->ops = &sdhci_pltfm_ops;
if (pdata) {
host->quirks = pdata->quirks;
host->quirks2 = pdata->quirks2;
}
platform_set_drvdata(pdev, host);
return host;
err:
dev_err(&pdev->dev, "%s failed %d\n", __func__, ret);
return ERR_PTR(ret);
}
- 调用sdhci_alloc_host分配一个sdhci_host
sdhci_alloc_host
-> mmc_alloc_host
-> mmc_rescan
->mmc_rescan_try_freq
-> mmc_attach_sdio
-> mmc_attach_mmc
-> mmc_attach_sd
- 如果sdhci host driver 定义有struct sdhci_ops,则调用自定义的 host->ops = pdata->ops;否则调用sdhci_pltfm_ops.
static const struct sdhci_ops sdhci_pltfm_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};
- 分析代码,sdhci-pltfm要求必须把sdhci的寄存器属性放在host的dtsi的寄存器属性的第一个,同时,也要把sdhci的中断属性放在host的dtsi的中断属性的第一个。简单dtsi的例子如下图所示:
sdhc_1: [email protected] {
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的中断,放在了第一个
4.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;
}
}
4.3.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;
}
refer to
- https://www.cnblogs.com/linhaostudy/p/10818083.html