admin管理员组文章数量:1536136
本文部分内容参考
万字长文 | Thermal框架源码剖析,
Linux Thermal机制源码分析之Governor_不捡风筝的玖伍贰柒的博客-CSDN博客
特此致谢!
三、相关源码及分析
Thermal Core主要是初始化driver,注册governor,解析dtsi文件,创建thermal zone和初始通信。相关的文件包括thermal_core.c(完整路径为drivers/thermal/thermal_core.c)、thermal_core.h(完整路径为drivers/thermal/thermal_core.h)和thermal.h(完整路径为include/linux/thermal.h)。
在此先列出前文中代码结构属于Thermal Core的部分,然后针对于各个部分进行源码分析。
注:本文所引用源码对应的内核版本为5.18.8。
1. thermal_init函数
thermal_init函数是Thermal驱动的入口函数,因此分析当然是从这里开始。thermal_init函数位于drivers/thermal/thermal_core.c中,源码如下:
static int __init thermal_init(void)
{
int result;
result = thermal_netlink_init();
if (result)
goto error;
result = thermal_register_governors();
if (result)
goto error;
result = class_register(&thermal_class);
if (result)
goto unregister_governors;
result = of_parse_thermal_zones();
if (result)
goto unregister_class;
result = register_pm_notifier(&thermal_pm_nb);
if (result)
pr_warn("Thermal: Can not register suspend notifier, return %d\n",
result);
return 0;
unregister_class:
class_unregister(&thermal_class);
unregister_governors:
thermal_unregister_governors();
error:
ida_destroy(&thermal_tz_ida);
ida_destroy(&thermal_cdev_ida);
mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock);
return result;
}
postcore_initcall(thermal_init);
Thermal模块加载进内核用的是postcore_initcall(),tsadc驱动一般用的是 module_init(),前者会早于后者加载,这点比较重要,有些代码流程上会依赖这种先后关系,需要留意。thermal_init函数里比较简单,先后调用了5个函数:
(1)thermal_netlink_init:进行generic netlink的初始化。应该是用于用户空间和内核空间通信的(从后边的分析可以看出,对netlink机制不了解并不影响分析Thermal框架)。
(2)thermal_register_governors:注册 governor。
(3)class_register:注册 thermal class,这个函数不会跟进去分析。class 框架性代码,大概知道是干什么用的即可,详细分析对当前所要学习的Thermal框架没有太大帮助。
(4)of_parse_thermal_zones:解析dts里面的 thermal_zones。此为重中之重!
(5)register_pm_notifier:notifier机制,表明Thermal core对pm事件感兴趣。
下边对这5个函数逐个贴出代码并进行初步解析。
(1)thermal_netlink_init
thermal_netlink_init函数进行generic netlink的初始化。其代码位于drivers/thermal/thermal_netlink.c中,如下所示:
static const struct genl_small_ops thermal_genl_ops[] = {
{
.cmd = THERMAL_GENL_CMD_TZ_GET_ID,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.dumpit = thermal_genl_cmd_dumpit,
},
{
.cmd = THERMAL_GENL_CMD_TZ_GET_TRIP,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = thermal_genl_cmd_doit,
},
{
.cmd = THERMAL_GENL_CMD_TZ_GET_TEMP,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = thermal_genl_cmd_doit,
},
{
.cmd = THERMAL_GENL_CMD_TZ_GET_GOV,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.doit = thermal_genl_cmd_doit,
},
{
.cmd = THERMAL_GENL_CMD_CDEV_GET,
.validate = GENL_DONT_VALIDATE_STRICT | GENL_DONT_VALIDATE_DUMP,
.dumpit = thermal_genl_cmd_dumpit,
},
};
static struct genl_family thermal_gnl_family __ro_after_init = {
.hdrsize = 0,
.name = THERMAL_GENL_FAMILY_NAME,
.version = THERMAL_GENL_VERSION,
.maxattr = THERMAL_GENL_ATTR_MAX,
.policy = thermal_genl_policy,
.small_ops = thermal_genl_ops,
.n_small_ops = ARRAY_SIZE(thermal_genl_ops),
.mcgrps = thermal_genl_mcgrps,
.n_mcgrps = ARRAY_SIZE(thermal_genl_mcgrps),
};
int __init thermal_netlink_init(void)
{
return genl_register_family(&thermal_gnl_family);
}
(2)thermal_register_governors
thermal_register_governors函数完成了Thermal Governor的注册。其在同文件(drivers/thermal/thermal_core.c)中,源码如下:
static int __init thermal_register_governors(void)
{
int ret = 0;
struct thermal_governor **governor;
for_each_governor_table(governor) {
ret = thermal_register_governor(*governor);
if (ret) {
pr_err("Failed to register governor: '%s'",
(*governor)->name);
break;
}
pr_info("Registered thermal governor '%s'",
(*governor)->name);
}
if (ret) {
struct thermal_governor **gov;
for_each_governor_table(gov) {
if (gov == governor)
break;
thermal_unregister_governor(*gov);
}
}
return ret;
}
(3)class_register
class_register函数属于内核接口层,其行为比较简单,仅仅是在/sys/class下面创建一个自己命名的目录,主要是提供给设备注册挂入链接,而实际挂链接的动作则在device_register中完成。
具体到本代码,class_register(&thermal_class)这一句代码实际的作用是创建了/sys/class/thermal结点。
thermal_class的定义及初始化在同文件(drivers/thermal/thermal_core.c)中,代码如下:
static struct class thermal_class = {
.name = "thermal",
.dev_release = thermal_release,
};
(4)of_parse_thermal_zones
of_parse_thermal_zones函数解析dtsi配置文件中的thermal-zones字段,并且注册thermal_zone_devices。其在同文件(drivers/thermal/thermal_of.c)中,源码如下:
/**
* of_parse_thermal_zones - parse device tree thermal data
*
* Initialization function that can be called by machine initialization
* code to parse thermal data and populate the thermal framework
* with hardware thermal zones info. This function only parses thermal zones.
* Cooling devices and sensor devices nodes are supposed to be parsed
* by their respective drivers.
*
* Return: 0 on success, proper error code otherwise
*
*/
int __init of_parse_thermal_zones(void)
{
struct device_node *np, *child;
struct __thermal_zone *tz;
struct thermal_zone_device_ops *ops;
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np) {
pr_debug("unable to find thermal zones\n");
return 0; /* Run successfully on systems without thermal DT */
}
for_each_available_child_of_node(np, child) {
struct thermal_zone_device *zone;
struct thermal_zone_params *tzp;
int i, mask = 0;
u32 prop;
tz = thermal_of_build_thermal_zone(child);
if (IS_ERR(tz)) {
pr_err("failed to build thermal zone %pOFn: %ld\n",
child,
PTR_ERR(tz));
continue;
}
ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
if (!ops)
goto exit_free;
tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);
if (!tzp) {
kfree(ops);
goto exit_free;
}
/* No hwmon because there might be hwmon drivers registering */
tzp->no_hwmon = true;
if (!of_property_read_u32(child, "sustainable-power", &prop))
tzp->sustainable_power = prop;
for (i = 0; i < tz->ntrips; i++)
mask |= 1 << i;
/* these two are left for temperature drivers to use */
tzp->slope = tz->slope;
tzp->offset = tz->offset;
zone = thermal_zone_device_register(child->name, tz->ntrips,
mask, tz,
ops, tzp,
tz->passive_delay,
tz->polling_delay);
if (IS_ERR(zone)) {
pr_err("Failed to build %pOFn zone %ld\n", child,
PTR_ERR(zone));
kfree(tzp);
kfree(ops);
of_thermal_free_zone(tz);
/* attempting to build remaining zones still */
}
}
of_node_put(np);
return 0;
exit_free:
of_node_put(child);
of_node_put(np);
of_thermal_free_zone(tz);
/* no memory available, so free what we have built */
of_thermal_destroy_zones();
return -ENOMEM;
}
(5)register_pm_notifier
register_pm_notifier(&thermal_pm_nb)注册了Thermal的notifier。register_pm_notifier函数在drivers/main/power.c中实现,源码如下:
int register_pm_notifier(struct notifier_block *nb)
{
return blocking_notifier_chain_register(&pm_chain_head, nb);
}
EXPORT_SYMBOL_GPL(register_pm_notifier);
thermal_pm_nb在同文件(drivers/thermal/thermal_core.c)中定义和初始化(就在thermal_init的上边),代码如下:
static struct notifier_block thermal_pm_nb = {
.notifier_call = thermal_pm_notify,
};
thermal_pm_notify函数在thermal_pm_nb的上边,代码如下:
static int thermal_pm_notify(struct notifier_block *nb,
unsigned long mode, void *_unused)
{
struct thermal_zone_device *tz;
switch (mode) {
case PM_HIBERNATION_PREPARE:
case PM_RESTORE_PREPARE:
case PM_SUSPEND_PREPARE:
atomic_set(&in_suspend, 1);
break;
case PM_POST_HIBERNATION:
case PM_POST_RESTORE:
case PM_POST_SUSPEND:
atomic_set(&in_suspend, 0);
list_for_each_entry(tz, &thermal_tz_list, node) {
if (!thermal_zone_device_is_enabled(tz))
continue;
thermal_zone_device_init(tz);
thermal_zone_device_update(tz,
THERMAL_EVENT_UNSPECIFIED);
}
break;
default:
break;
}
return 0;
}
版权声明:本文标题:Linux内核Thermal框架详解三、Thermal Core(2) 内容由热心网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:https://m.elefans.com/xitong/1726926560a1090606.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论