• 9. [mmc subsystem] host(第三章)——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
    ");
            ret = -EBUSY;
            goto err_request;
        }
    
        host->ioaddr = ioremap(iomem->start, resource_size(iomem));
        if (!host->ioaddr) {
            dev_err(&pdev->dev, "failed to remap registers
    ");
            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;
    }
    
  • 相关阅读:
    正则表达式速查表
    Python第三方库管理Anaconda
    Python3.x和Python2.x的区别
    python 学习 “笨办法学python”(随书补充)
    python 中文输入的注意事项
    mongodb update 字符 操作(补充)
    mongodb update 字符 操作
    04.视频播放器通用架构实践
    05.视频播放器内核切换封装
    03.视频播放器Api说明
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/10818083.html
Copyright © 2020-2023  润新知