一、API总览
1、mmc host分配、注册相关
- mmc_alloc_host & mmc_free_host
底层host controller驱动调用,用来分配或者释放一个struct mmc_host结构体,将其于mmc_host_class关联,并且做部分初始化操作。
代码语言:javascript复制 原型: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。
代码语言:javascript复制 原型: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类。
代码语言:javascript复制 原型:int mmc_register_host_class(void)
原型:void mmc_unregister_host_class(void)
3、mmc host属性解析相关
- mmc_of_parse
底层host controller驱动调用,解析mmc_host的dtsi节点的部分属性。
代码语言:javascript复制 原型: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这个类。其内容如下:
代码语言:javascript复制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)相关的属性组
代码语言:javascript复制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属性
代码语言:javascript复制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。
代码语言:javascript复制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: %dn",
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之前就要完成了。
相关节点:
代码语言:javascript复制/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并一定要使用这个,也可以使用自己一套解析的方法。
简单说明如下:
代码语言:javascript复制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);
//................后面的代码都类似,直接略过了
}