[mmc subsystem] mmc core(第四章)——host模块说明

mmc subsystem系列(持续更新中):
[mmc subsystem] 概念与框架
[mmc subsystem] mmc core(第一章)——概述
[mmc subsystem] mmc core(第二章)——数据结构和宏定义说明
[mmc subsystem] mmc core(第三章)——bus模块说明
[mmc subsystem] mmc core(第四章)——host模块说明
[mmc subsystem] mmc core(第五章)——card相关模块(mmc type card)
[mmc subsystem] mmc core(第六章)——mmc core主模块

建议先参考《[mmc subsystem] 概念与框架》和《[mmc subsystem] mmc core(第一章)——概述》对整体有一个了解。

=========================================================================================================

零、说明

对应代码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

扫描二维码关注公众号,回复: 3285020 查看本文章

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;
}

这边重点强调一个mmc_host->detect=mmc_rescan,当mmc_host的detect work被执行时,就会调用到mmc_rescan中。
相关节点,以mmc_host0为例:
/sys/bus/platform/devices/7824900.sdhci/mmc_host/mmc0
/sys/class/mmc_host/mmc0

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\n",
               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.\n");
        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!\n", bus_width);
    }

    /* f_max is obtained from the optional "max-frequency" property */
    of_property_read_u32(np, "max-frequency", &host->f_max);
       //................后面的代码都类似,直接略过了
}

猜你喜欢

转载自blog.csdn.net/ooonebook/article/details/55001299