0
点赞
收藏
分享

微信扫一扫

Linux内核4.14版本——mmc host(3)——sdhci-pltfm说明

dsysama 2022-01-23 阅读 101

目录

1. sdhci-pltfm说明

2. 数据结构说明

2.1 struct sdhci_pltfm_data

2.2 struct sdhci_pltfm_host

3. API接口

3.1 sdhci_pltfm_init

3.2 sdhci_get_of_property

3.3 sdhci_pltfm_register


1. 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-pltfm实现了两个方法来统一设置这些信息,方便host driver对于sdhci driver的使用。

2. 数据结构说明

2.1 struct sdhci_pltfm_data

struct sdhci_pltfm_data {
	const struct sdhci_ops *ops;
	unsigned int quirks;
	unsigned int quirks2;
};

首先看一下sdhci-pltfm要设置的sdhci_host的成员的来源信息:

       综上,ops、quirks和quirks2这几个的值是必须由平台host驱动(host driver)提供,而ioaddr和irq可以通过解析属性得到。
       因此,sdhci-pltfm把ops、quirks和quirks2的值封装到sdhci_pltfm_data中,由底层host驱动提供。

2.2 struct 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);

3. API接口

3.1 sdhci_pltfm_init

struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
				    const struct sdhci_pltfm_data *pdata,
				    size_t priv_size)
{
	struct sdhci_host *host;
	struct resource *iomem;
	void __iomem *ioaddr;
	int irq, ret;

	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	ioaddr = devm_ioremap_resource(&pdev->dev, iomem);
	if (IS_ERR(ioaddr)) {
		ret = PTR_ERR(ioaddr);
		goto err;
	}

	irq = platform_get_irq(pdev, 0);
	if (irq < 0) {
		dev_err(&pdev->dev, "failed to get IRQ number\n");
		ret = irq;
		goto err;
	}

	host = sdhci_alloc_host(&pdev->dev,
		sizeof(struct sdhci_pltfm_host) + priv_size);

	if (IS_ERR(host)) {
		ret = PTR_ERR(host);
		goto err;
	}

	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-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的中断,放在了第一个

3.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);
	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
	u32 bus_width;

	if (of_get_property(np, "sdhci,auto-cmd12", NULL))
		host->quirks |= SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12;

	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;

	if (sdhci_of_wp_inverted(np))
		host->quirks |= SDHCI_QUIRK_INVERTED_WRITE_PROTECT;

	if (of_get_property(np, "broken-cd", NULL))
		host->quirks |= SDHCI_QUIRK_BROKEN_CARD_DETECTION;

	if (of_get_property(np, "no-1-8-v", NULL))
		host->quirks2 |= SDHCI_QUIRK2_NO_1_8_V;

	if (of_device_is_compatible(np, "fsl,p2020-rev1-esdhc"))
		host->quirks |= SDHCI_QUIRK_BROKEN_DMA;

	if (of_device_is_compatible(np, "fsl,p2020-esdhc") ||
	    of_device_is_compatible(np, "fsl,p1010-esdhc") ||
	    of_device_is_compatible(np, "fsl,t4240-esdhc") ||
	    of_device_is_compatible(np, "fsl,mpc8536-esdhc"))
		host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;

	of_property_read_u32(np, "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_property_read_bool(np, "wakeup-source") ||
	    of_property_read_bool(np, "enable-sdio-wakeup")) /* legacy */
		host->mmc->pm_caps |= MMC_PM_WAKE_SDIO_IRQ;
}

3.3 sdhci_pltfm_register

int sdhci_pltfm_register(struct platform_device *pdev,
			const struct sdhci_pltfm_data *pdata,
			size_t priv_size)
{
	struct sdhci_host *host;
	int ret = 0;

	host = sdhci_pltfm_init(pdev, pdata, priv_size);
	if (IS_ERR(host))
		return PTR_ERR(host);

	sdhci_get_of_property(pdev);

	ret = sdhci_add_host(host);
	if (ret)
		sdhci_pltfm_free(pdev);

	return ret;
}
举报

相关推荐

0 条评论