zoukankan      html  css  js  c++  java
  • 4. [mmc subsystem] mmc core(第四章)——host模块说明

    零、说明

    对应代码drivers/mmc/core/host.c,drivers/mmc/core/host.h。

    为底层host controller driver实现mmc host的申请以及注册的API等等,以及host相关属性的实现。

    一、API总览

    1、mmc host分配、注册相关

    • mmc_alloc_host & mmc_free_host

    底层host controller驱动调用,用来分配或者释放一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。

        原型:struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
        参数说明:extra——》mmc_host的私有数据的长度,会和mmc_host结构体一起分配,
                         dev——》底层host controller的device结构体,用于作为mmc_host的device的父设备
    
        原型:void mmc_free_host(struct mmc_host *host)
    
    • mmc_add_host & mmc_remove_host

    底层host controller驱动调用,注册或者卸载mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录。然后启动mmc_host。

        原型:int mmc_add_host(struct mmc_host *host)
        原型:void mmc_remove_host(struct mmc_host *host)
    

    2、mmc host class相关

    • mmc_register_host_class & mmc_unregister_host_class

    注册或者卸载mmc_host类。

        原型:int mmc_register_host_class(void)
        原型:void mmc_unregister_host_class(void)
    

    3、mmc host属性解析相关

    • mmc_of_parse

    底层host controller驱动调用,解析mmc_host的dtsi节点的部分属性。

        原型:void mmc_of_parse(struct mmc_host *host)
    

    4、mmc host时钟相关

    mmc_host_clk_hold & mmc_host_clk_release

    mmc core主模块调用,用于获取host时钟和释放host时钟

    二、数据结构

    1、mmc_host_class

    mmc_host_class代表了mmc_host这个类。其内容如下:

    static struct class mmc_host_class = {
        .name       = "mmc_host",        // 添加到sys文件系统之后,会生成/sys/class/mmc_host这个目录
        .dev_release    = mmc_host_classdev_release,    // 从mmc_host这个class下release掉某个设备之后要做的对应操作
        .pm     = &mmc_host_pm_ops,        // 该class下的host的pm电源管理操作
    };
    
    static const struct dev_pm_ops mmc_host_pm_ops = {
        SET_SYSTEM_SLEEP_PM_OPS(mmc_host_suspend, mmc_host_resume)
        SET_RUNTIME_PM_OPS(mmc_host_runtime_suspend, mmc_host_runtime_resume,
                   pm_generic_runtime_idle)
    };
    // 具体函数实现遇到了再补充
    

    2、clk_scaling_attr_grp

    一些和时钟缩放(clk_scaling)相关的属性组

    static struct attribute *clk_scaling_attrs[] = {
        &dev_attr_enable.attr,
        &dev_attr_up_threshold.attr,
        &dev_attr_down_threshold.attr,
        &dev_attr_polling_interval.attr,
        NULL,
    };
    
    static struct attribute_group clk_scaling_attr_grp = {
        .name = "clk_scaling",
        .attrs = clk_scaling_attrs,
    };
    

    对应/sys/class/mmc_host/mmc0/clk_scaling目录下的属性

    3、dev_attr_grp

    和设备相关的属性组,只定义了perf属性

    static struct attribute *dev_attrs[] = {
    #ifdef CONFIG_MMC_PERF_PROFILING
        &dev_attr_perf.attr,
    #endif
        NULL,
    };
    static struct attribute_group dev_attr_grp = {
        .attrs = dev_attrs,
    };
    

    对应/sys/class/mmc_host/mmc0/perf属性

    三、接口代码说明

    1、mmc_register_host_class实现

    注册mmc_host class。

    int mmc_register_host_class(void)
    {
        return class_register(&mmc_host_class);   // 以mmc_host_class为class创建一个class,关于mmc_host_class在上述数据结构已经说明过了
    }
    

    相关节点:/sys/class/mmc_host

    2、mmc_alloc_host实现

    底层host controller驱动调用,用来分配一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。

    • 主要工作:

      • 分配内存空间
      • 初始化其class device(对应/sys/class/mmc0节点)
      • clock gate、锁、工作队列、wakelock、detect工作的初始化
      • 初始化detect成员(也就是检测工作)为mmc_rescan
    • 代码如下:

    /**
     *  mmc_alloc_host - initialise the per-host structure.
     *  @extra: sizeof private data structure
     *  @dev: pointer to host device model structure
     *
     *  Initialise the per-host structure.
     */
    struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
    {
    //    参数说明:extra——》mmc_host的私有数据的长度,会和mmc_host结构体一起分配,
    //                     dev——》底层host controller的device结构体,用于作为mmc_host的device的父设备
        int err;
        struct mmc_host *host;
    
    /* 分配内存空间,其中多分配了extra字节作为私有数据 */
        host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
        if (!host)
            return NULL;
            
         /* scanning will be enabled when we're ready */
    /* 因为只是分配了一个mmc_host,host还没有准备好,所以这里禁用rescan,也就是设置mmc_host->rescan_disable 
        host->rescan_disable = 1;   // 在在mmc_start_host中会去使能
    /* 为该mmc_host分配一个唯一的id号,设置到host->index */
        idr_preload(GFP_KERNEL);
        spin_lock(&mmc_host_lock);
        err = idr_alloc(&mmc_host_idr, host, 0, 0, GFP_NOWAIT);
        if (err >= 0)
            host->index = err;
        spin_unlock(&mmc_host_lock);
        idr_preload_end();
        if (err < 0)
            goto free;
    
    /* 设置mmc_host name */
        dev_set_name(&host->class_dev, "mmc%d", host->index);   // 以mmc_host的id号构成mmc_host的name,例如mmc0、mmc1
    
    /* 关联mmc_host class_dev并进行初始化 */
    /* class_dev就代表了mmc_host 的device结构体,是其在设备驱动模型中的体现 */
        host->parent = dev;                              // 将mmc_host的parent设置成对应host controller节点转化出来的device
        host->class_dev.parent = dev;             
            // 将mmc_host的device(class_dev)的parent设置成对应host controller节点转化出来的device
            // 注册到sysfs之后,会相应生成/sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
            // 其中7824900.sdhci表示qcom的host controller节点转化出来的device
        host->class_dev.class = &mmc_host_class;
             // 将mmc_device(class_dev)的类设置为mmc_host_class
            // 注册到sysfs之后,会相应生成/sys/class/mmc_host/mmc0
    
        device_initialize(&host->class_dev);   // 初始化mmc_host->class_dev
    
    /* clock gate、锁、工作队列、wakelock、detect工作的初始化 */
        mmc_host_clk_init(host);
    
        mutex_init(&host->slot.lock);
        host->slot.cd_irq = -EINVAL;
    
        spin_lock_init(&host->lock);
        init_waitqueue_head(&host->wq);
        host->wlock_name = kasprintf(GFP_KERNEL, "%s_detect", mmc_hostname(host)); // 设置detect_wake_lock的名称为mmc0_detect,在card检测的时候会使用
        wake_lock_init(&host->detect_wake_lock, WAKE_LOCK_SUSPEND, host->wlock_name);   // // 初始化detect_wake_lock
            // 可以通过/sys/kernel/debug/wakeup_sources,相应生成了mmc0_detect和mmc1_detect两个wakelock
    
        INIT_DELAYED_WORK(&host->detect, mmc_rescan);
            // !!!!这个很重要!!!!初始化detect工作为mmc_rescan,后续调度host->detect来检测是否有card插入时,就会调用到mmc_rescan。
    #ifdef CONFIG_PM
        host->pm_notify.notifier_call = mmc_pm_notify;
    #endif
    
    /* 一些size的初始化 */
        host->max_segs = 1;   // 初始化最大支持段(由host自己根据硬件进行修改),可以通过/sys/block/mmcblk0/queue/max_segments进行修改
        host->max_seg_size = PAGE_CACHE_SIZE;   // 初始化段大小,(由host自己根据硬件进行修改)
    
        host->max_req_size = PAGE_CACHE_SIZE;   // 一次MMC请求的最大字节数
        host->max_blk_size = 512;   // 一个块的最大字节数
        host->max_blk_count = PAGE_CACHE_SIZE / 512; // 一次MMC请求的最大块数量
    
        return host;
    
    free:
        kfree(host);
        return NULL;
    }
    
    

    3、mmc_add_host实现

    底层host controller驱动调用,注册mmc_host到设备驱动中,添加到sys类下面,并设置相应的debug目录。然后启动mmc_host。

    • 主要工作:
      • 使能pm runtime功能
      • 将mmc_host的class_dev添加到设备驱动模型中,在sysfs中生成相应的节点
      • 初始化mmc_host相关的debug目录
      • 设置mmc_host的class_dev的属性
      • 调用mmc_start_host启动host(进入mmc core主模块的部分)
    /**
     *  mmc_add_host - initialise host hardware
     *  @host: mmc host
     *
     *  Register the host with the driver model. The host must be
     *  prepared to start servicing requests before this function
     *  completes.
     */
    int mmc_add_host(struct mmc_host *host)
    {
        int err;
    /* 使能mmc host的class_dev的pm runtime功能 */
        err = pm_runtime_set_active(&host->class_dev);
        if (err)
            pr_err("%s: %s: failed setting runtime active: err: %d
    ",
                   mmc_hostname(host), __func__, err);
        else if (mmc_use_core_runtime_pm(host))
            pm_runtime_enable(&host->class_dev);
    
    /* 通过device_add将mmc_host->class_dev添加到设备驱动模型中,在sys下生成相应节点 */
        err = device_add(&host->class_dev);
            // 通过mmc_alloc_host中关于mmc_host的class_dev的关联,可以生成如下两个节点
            // /sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
            // /sys/class/mmc_host/mmc0
    
    /* 使能mmc host的class_dev的异步suspend的功能 */
        device_enable_async_suspend(&host->class_dev);
        led_trigger_register_simple(dev_name(&host->class_dev), &host->led);
    
    /* 设置mmc_host的debug节点 */
    #ifdef CONFIG_DEBUG_FS
        mmc_add_host_debugfs(host);
    #endif
            // 对应sys节点为/sys/kernel/debug/mmc0
    
    /* 以下设置mmc host的class_dev的属性 */
        mmc_host_clk_sysfs_init(host);
            // 对应/sys/class/mmc_host/mmc0/clkgate_delay属性
    
        host->clk_scaling.up_threshold = 35;
        host->clk_scaling.down_threshold = 5;
        host->clk_scaling.polling_delay_ms = 100;
        err = sysfs_create_group(&host->class_dev.kobj, &clk_scaling_attr_grp);
            // 对应/sys/class/mmc_host/mmc0/clk_scaling目录下的四个属性,clk_scaling_attr_grp前面已经说明过了
    
        err = sysfs_create_group(&host->class_dev.kobj, &dev_attr_grp);
            // 对应/sys/class/mmc_host/mmc0/perf属性,dev_attr_grp前面已经说明过了
    
    /* 调用mmc_start_host,也就调用到了mmc core主模块的启动host部分,在mmc core主模块的时候说明 */
        mmc_start_host(host);
        if (!(host->pm_flags & MMC_PM_IGNORE_PM_NOTIFY))
            register_pm_notifier(&host->pm_notify);
    
        return 0;
    }
    

    注意,最后调用了mmc_start_host来启动host。关于mmc_start_host会在mmc core主模块的部分里面说明。

    也就是说,关于host的初始化工作,需要在调用mmc_add_host之前就要完成了。

    相关节点:

    /sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0 
    /sys/class/mmc_host/mmc0 
    /sys/kernel/debug/mmc0
    

    4、mmc_of_parse实现

    解析mmc_host的dtsi节点的部分属性。

    mmc_of_parse提供了通用的、解析host controller dtsi节点的属性的方法,这就要依赖于dtsi的属性是否符合规范。

    但是host controller driver并一定要使用这个,也可以使用自己一套解析的方法。

    简单说明如下:

    void mmc_of_parse(struct mmc_host *host)
    {
        struct device_node *np;
        u32 bus_width;
        bool explicit_inv_wp, gpio_inv_wp = false;
        enum of_gpio_flags flags;
        int len, ret, gpio;
    
        if (!host->parent || !host->parent->of_node)
            return;
    
    /* 获取到mmc_host对应的host controller的dts节点 */
        np = host->parent->of_node;
            // host->parent指向了mmc_host的对应host controller的device,获取其of_node就获取到了对应的dtsi节点
    
    /* 以下就是解析属性,并设置到mmc_host的属性标识caps和caps2 中 */
        /* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */
        if (of_property_read_u32(np, "bus-width", &bus_width) < 0) {
            dev_dbg(host->parent,
                ""bus-width" property is missing, assuming 1 bit.
    ");
            bus_width = 1;
        }
    
        switch (bus_width) {
        case 8:
            host->caps |= MMC_CAP_8_BIT_DATA;   // "bus-width"——》MMC_CAP_8_BIT_DATA
            /* Hosts capable of 8-bit transfers can also do 4 bits */
        case 4:
            host->caps |= MMC_CAP_4_BIT_DATA;  // "bus-width"——》MMC_CAP_4_BIT_DATA
            break;
        case 1:
            break;
        default:
            dev_err(host->parent,
                "Invalid "bus-width" value %ud!
    ", bus_width);
        }
    
        /* f_max is obtained from the optional "max-frequency" property */
        of_property_read_u32(np, "max-frequency", &host->f_max);
           //................后面的代码都类似,直接略过了
    }
    
  • 相关阅读:
    AirtestIDE这个隐藏的小助手,还没用过你就亏啦!
    不懂抓包也能做APP爬虫?1招教你爬取抖音流行歌名
    巧用bat文件做Airtest脚本的“批量运行”
    AirtestIDE1.2.4、1.2.5新版来袭!新增自动更新、iOS更多版本支持...
    NodeService Ensure that Node.js is installed and can be found in one of the PATH directories
    NET CORE通过NodeService调用js
    Java8 的 Stream API 的确牛X,但性能究竟如何呢?
    Java 生成二维码实战
    Java高并发之设计模式,设计思想
    NullPointerException 的处理新方式,Java14 真的太香了!
  • 原文地址:https://www.cnblogs.com/linhaostudy/p/10790541.html
Copyright © 2011-2022 走看看