天天看点

linux MMC framework(3) - sdhci-pltfm

  • 了解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

linux MMC framework(3) - sdhci-pltfm

主要工作

  • 调用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

继续阅读