本文主要是介绍Thermal子系统之thermal_init流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Linux Thermal是Linux系统下的温度控制相关模块,主要用于解决设备因性能增强而引起的发热问题,确保设备温度维持在一个安全、舒适的范围内,防止硬件过热导致系统不稳定或缩减芯片寿命。Linux Thermal的主要框架包括获取温度的设备和控制温度的设备,以及一些使用温度控制设备的策略。在Linux Thermal框架中,获取温度的设备被抽象为Thermal Zone Device,控制温度的设备被抽象为Thermal Cooling Device。
内核中源码路径:
Thermal_core.c (drivers\thermal)
1、thermal初始化:
static int __init thermal_init(void)
{
int result;
thermal_passive_wq = alloc_workqueue("thermal_passive_wq",
WQ_HIGHPRI | WQ_UNBOUND
| WQ_FREEZABLE,
THERMAL_MAX_ACTIVE);//为thermal_passive_wq分配工作队列空间
if (!thermal_passive_wq) {
result = -ENOMEM;
goto init_exit;
}
result = thermal_register_governors();//注册热管理策略governors,
if (result)
goto destroy_wq;
result = class_register(&thermal_class);//注册class节点
if (result)
goto unregister_governors;
result = of_parse_thermal_zones();//解析thermal zone数据,并将硬件thermal zone信息填充到thermal框架中
if (result)
goto exit_zone_parse;
result = register_pm_notifier(&thermal_pm_nb);//注册suspend notifier
if (result)
pr_warn("Thermal: Can not register suspend notifier, return %d\n",
result);
return 0;
exit_zone_parse:
class_unregister(&thermal_class);
unregister_governors:
thermal_unregister_governors();
destroy_wq:
destroy_workqueue(thermal_passive_wq);
init_exit:
idr_destroy(&thermal_tz_idr);
idr_destroy(&thermal_cdev_idr);
mutex_destroy(&thermal_idr_lock);
mutex_destroy(&thermal_list_lock);
mutex_destroy(&thermal_governor_lock);
return result;
}
1.1注册热管理策略governors
static int __init thermal_register_governors(void)
{
int result;
result = thermal_gov_step_wise_register();//注册 Step Wise 热管理策略的函数
if (result)
return result;
result = thermal_gov_fair_share_register();//注册公平分享(Fair Share)热管理策略的函数
if (result)
return result;
result = thermal_gov_bang_bang_register();//注册 bang-bang 控制热管理策略的函数。Bang-bang 控制是一种控制理论中的控制策略,它通过简单地打开或关闭控制器来控制系统的输出。在热管理中,bang-bang 控制策略可以被用来快速响应温度变化,通过开启或关闭散热设备来控制温度。
if (result)
return result;
result = thermal_gov_user_space_register();//注册用户空间热管理策略的函数。
if (result)
return result;
result = thermal_gov_low_limits_register();//注册低限制热管理策略的函数。低限制策略是一种热管理策略,它通过设置较低的温度限制来控制设备的温度。这种策略通常用于保护设备免受过热,确保设备的可靠性和寿命。
if (result)
return result;
return thermal_gov_power_allocator_register();//注册电源分配器热管理策略的函数。电源分配器策略是一种热管理策略,它通过分配电源使用来控制设备的温度。这种策略通常用于平衡设备的性能和功耗,以提高整体的热效率。
}
以step_wise为例
1.1.1 thermal_gov_step_wise_register注册函数
int thermal_gov_step_wise_register(void)
{
return thermal_register_governor(&thermal_gov_step_wise);
}
1.1.1.1 thermal_gov_step_wise定义如下:
static struct thermal_governor thermal_gov_step_wise = {
.name = "step_wise",
.throttle = step_wise_throttle,
};
static int step_wise_throttle(struct thermal_zone_device *tz, int trip)
{
struct thermal_instance *instance;
thermal_zone_trip_update(tz, trip);//thermal zone 触发点更新
if (tz->forced_passive)//判断是否强制更新
thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node)
thermal_cdev_update(instance->cdev);//更新每一个cooling 设备
mutex_unlock(&tz->lock);
return 0;
}
1.1.1.1.1 thermal_zone_trip_update函数
static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip)
{
int trip_temp, hyst_temp;
enum thermal_trip_type trip_type;
enum thermal_trend trend;
struct thermal_instance *instance;
bool throttle = false;
int old_target;
if (trip == THERMAL_TRIPS_NONE) {
hyst_temp = trip_temp = tz->forced_passive;
trip_type = THERMAL_TRIPS_NONE;
} else {
tz->ops->get_trip_temp(tz, trip, &trip_temp);//获取触发点温度,此回调函数来自of_thermal_ops
if (tz->ops->get_trip_hyst) {
tz->ops->get_trip_hyst(tz, trip, &hyst_temp);//获取trip_hyst,此值用于温度低于trip_temp-hysteresis值时恢复状态,此回调函数来自of_thermal_ops
hyst_temp = trip_temp - hyst_temp;
} else {
hyst_temp = trip_temp;
}
tz->ops->get_trip_type(tz, trip, &trip_type);//获取触发类型,此回调函数来自of_thermal_ops
}
trend = get_tz_trend(tz, trip);//判断温度是在增加、下降还是不变
dev_dbg(&tz->device,
"Trip%d[type=%d,temp=%d,hyst=%d]:trend=%d,throttle=%d\n",
trip, trip_type, trip_temp, hyst_temp, trend, throttle);
mutex_lock(&tz->lock);
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip)
continue;
old_target = instance->target;
/*
* Step wise has to lower the mitigation only if the
* temperature goes below the hysteresis temperature.
* Atleast, it has to hold on to mitigation device lower
* limit if the temperature is above the hysteresis
* temperature.
*/
if (tz->temperature >= trip_temp ||
(tz->temperature > hyst_temp &&
old_target != THERMAL_NO_TARGET))//判断温度是不是达到触发点
throttle = true;
else
throttle = false;
instance->target = get_target_state(instance, trend, throttle);//得到目标值
dev_dbg(&instance->cdev->device, "old_target=%d, target=%d\n",
old_target, (int)instance->target);
if (instance->initialized && old_target == instance->target)
continue;
if (!instance->initialized) {//如果没有初始化
if (instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
update_passive_instance(tz, trip_type, 1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
}
} else {
/* Activate a passive thermal instance */
if (old_target == THERMAL_NO_TARGET &&
instance->target != THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
true);
update_passive_instance(tz, trip_type, 1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
/* Deactivate a passive thermal instance */
} else if (old_target != THERMAL_NO_TARGET &&
instance->target == THERMAL_NO_TARGET) {
trace_thermal_zone_trip(tz, trip, trip_type,
false);
update_passive_instance(tz, trip_type, -1);//更新passive值用于在monitor_thermal_zone函数中是否使用passive_delay循环
}
}
instance->initialized = true;
mutex_lock(&instance->cdev->lock);
instance->cdev->updated = false; /* cdev needs update */
mutex_unlock(&instance->cdev->lock);
}
mutex_unlock(&tz->lock);
}
1.1.1.1.2 thermal_cdev_update函数
void thermal_cdev_update(struct thermal_cooling_device *cdev)
{
struct thermal_instance *instance;
unsigned long current_target = 0, min_target = ULONG_MAX;
mutex_lock(&cdev->lock);
/* cooling device is updated*/
if (cdev->updated) {
mutex_unlock(&cdev->lock);
return;
}
/* Make sure cdev enters the deepest cooling state */
current_target = cdev->sysfs_cur_state_req;//获取当前的状态
min_target = cdev->sysfs_min_state_req;
list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) {//遍历每一个cooling device
dev_dbg(&cdev->device, "zone%d->target=%lu\n",
instance->tz->id, instance->target);
if (instance->target == THERMAL_NO_TARGET)
continue;
if (instance->tz->governor->min_state_throttle) {//用于low_limits governor
if (instance->target < min_target)
min_target = instance->target;
} else {
if (instance->target > current_target)//目标状态如果大于当前状态,更新当前状态值
current_target = instance->target;
}
}
trace_cdev_update_start(cdev);
cdev->ops->set_cur_state(cdev, current_target);//设置当前状态值到cooling device
if (cdev->ops->set_min_state)
cdev->ops->set_min_state(cdev, min_target);//设置最小的状态值
cdev->updated = true;//更新完成
mutex_unlock(&cdev->lock);
trace_cdev_update(cdev, current_target, min_target);
dev_dbg(&cdev->device, "set to state %lu min state %lu\n",
current_target, min_target);
}
1.1.1.2 thermal_register_governor函数
int thermal_register_governor(struct thermal_governor *governor)
{
int err;
const char *name;
struct thermal_zone_device *pos;
if (!governor)
return -EINVAL;
mutex_lock(&thermal_governor_lock);
err = -EBUSY;
if (__find_governor(governor->name) == NULL) {//__find_governor查找governor是否已经存在thermal_governor_list链表中,若不在,将governor添加到thermal_governor_list中
err = 0;
list_add(&governor->governor_list, &thermal_governor_list);
if (!def_governor && !strncmp(governor->name,
DEFAULT_THERMAL_GOVERNOR, THERMAL_NAME_LENGTH))
def_governor = governor;
}
mutex_lock(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node) {//遍历链表thermal_tz_list
/*
* only thermal zones with specified tz->tzp->governor_name
* may run with tz->govenor unset
*/
if (pos->governor)
continue;
name = pos->tzp->governor_name;
if (!strncasecmp(name, governor->name, THERMAL_NAME_LENGTH)) {//如果governor name一样,则进行绑定
int ret;
ret = thermal_set_governor(pos, governor);//绑定governor和thermal_zone_device
if (ret)
dev_err(&pos->device,
"Failed to set governor %s for thermal zone %s: %d\n",
governor->name, pos->type, ret);
}
}
mutex_unlock(&thermal_list_lock);
mutex_unlock(&thermal_governor_lock);
return err;
}
static int thermal_set_governor(struct thermal_zone_device *tz,
struct thermal_governor *new_gov)
{
int ret = 0;
if (tz->governor && tz->governor->unbind_from_tz)
tz->governor->unbind_from_tz(tz);
if (new_gov && new_gov->bind_to_tz) {
ret = new_gov->bind_to_tz(tz);
if (ret) {
bind_previous_governor(tz, new_gov->name);
return ret;
}
}
tz->governor = new_gov;//帮governor给thermal zone device
return ret;
}
1.2 解析thermal zone数据 of_parse_thermal_zones
函数原型如下:
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");//从设备树中解析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) {//遍历thermal-zones中每一个孩子
struct thermal_zone_device *zone;
struct thermal_zone_params *tzp;
int i, mask = 0;
u32 prop;
const char *governor_name;
tz = thermal_of_build_thermal_zone(child);//建立__thermal_zone
if (IS_ERR(tz)) {
pr_err("failed to build thermal zone %s: %ld\n",
child->name,
PTR_ERR(tz));
continue;
}
ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);//复制of_thermal_ops,提供各种回调函数
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_string(child, "thermal-governor",
&governor_name))//从设备树中读取"thermal-governor"值
strlcpy(tzp->governor_name, governor_name,
THERMAL_NAME_LENGTH);
if (!of_property_read_u32(child, "sustainable-power", &prop))//从设备树中读取"sustainable-power"值
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;
if (of_property_read_bool(child, "tracks-low"))//从设备树中读取"tracks-low"值
tzp->tracks_low = true;
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 %s zone %ld\n", child->name,
PTR_ERR(zone));
kfree(tzp);
kfree(ops);
of_thermal_free_zone(tz);
/* attempting to build remaining zones still */
continue;
}
tz->tzd = zone;
}
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;
}
1.2.1 of_thermal_ops结构
static struct thermal_zone_device_ops of_thermal_ops = {
.get_mode = of_thermal_get_mode,
.set_mode = of_thermal_set_mode,
.get_trip_type = of_thermal_get_trip_type,
.get_trip_temp = of_thermal_get_trip_temp,
.set_trip_temp = of_thermal_set_trip_temp,
.get_trip_hyst = of_thermal_get_trip_hyst,
.set_trip_hyst = of_thermal_set_trip_hyst,
.get_crit_temp = of_thermal_get_crit_temp,
.bind = of_thermal_bind,
.unbind = of_thermal_unbind,
.is_wakeable = of_thermal_is_wakeable,
};
1.2.2 建立__thermal_zone
thermal_of_build_thermal_zone函数原型如下:
static struct __thermal_zone
__init *thermal_of_build_thermal_zone(struct device_node *np)
{
struct device_node *child = NULL, *gchild;
struct __thermal_zone *tz;
int ret, i;
u32 prop, coef[2];
if (!np) {
pr_err("no thermal zone np\n");
return ERR_PTR(-EINVAL);
}
tz = kzalloc(sizeof(*tz), GFP_KERNEL);//分配空间
if (!tz)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&tz->list);
ret = of_property_read_u32(np, "polling-delay-passive", &prop);//读取设备树参数,用于passive轮询间隔时间
if (ret < 0) {
pr_err("missing polling-delay-passive property\n");
goto free_tz;
}
tz->passive_delay = prop;
ret = of_property_read_u32(np, "polling-delay", &prop);//读取设备树参数,用于轮询间隔时间
if (ret < 0) {
pr_err("missing polling-delay property\n");
goto free_tz;
}
tz->polling_delay = prop;
tz->default_disable = of_property_read_bool(np,
"disable-thermal-zone");//读取设备树参数,用于默认关闭
tz->is_wakeable = of_property_read_bool(np,
"wake-capable-sensor");//读取设备树参数,用于唤醒系统的传感器
/*
* REVIST: for now, the thermal framework supports only
* one sensor per thermal zone. Thus, we are considering
* only the first two values as slope and offset.
*/
ret = of_property_read_u32_array(np, "coefficients", coef, 2);
if (ret == 0) {
tz->slope = coef[0];
tz->offset = coef[1];
} else {
tz->slope = 1;
tz->offset = 0;
}
/* trips */
child = of_get_child_by_name(np, "trips");//触发点
/* No trips provided */
if (!child)
goto finish;
tz->ntrips = of_get_child_count(child);//得到触发点的个数
if (tz->ntrips == 0) /* must have at least one child */
goto finish;
tz->trips = kzalloc(tz->ntrips * sizeof(*tz->trips), GFP_KERNEL);//分配触发点的空间
if (!tz->trips) {
ret = -ENOMEM;
goto free_tz;
}
i = 0;
for_each_child_of_node(child, gchild) {
ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);//得到每个触发点的参数
if (ret)
goto free_trips;
}
of_node_put(child);
/* cooling-maps */
child = of_get_child_by_name(np, "cooling-maps");//从设备节点中读取cooling-maps
/* cooling-maps not provided */
if (!child)
goto finish;
tz->num_tbps = of_get_child_count(child);//得到cooling-maps孩子的个数
if (tz->num_tbps == 0)
goto finish;
tz->tbps = kzalloc(tz->num_tbps * sizeof(*tz->tbps), GFP_KERNEL);//分配空间
if (!tz->tbps) {
ret = -ENOMEM;
goto free_trips;
}
i = 0;
for_each_child_of_node(child, gchild) {//遍历cooling-maps中每一个孩子
ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
tz->trips, tz->ntrips);//从设备树中解析和填充cooling-maps数据
if (ret)
goto free_tbps;
}
finish:
of_node_put(child);
tz->mode = THERMAL_DEVICE_DISABLED;
return tz;
free_tbps:
for (i = i - 1; i >= 0; i--)
of_node_put(tz->tbps[i].cooling_device);
kfree(tz->tbps);
free_trips:
for (i = 0; i < tz->ntrips; i++)
of_node_put(tz->trips[i].np);
kfree(tz->trips);
of_node_put(gchild);
free_tz:
kfree(tz);
of_node_put(child);
return ERR_PTR(ret);
}
1.2.2.1 解析和填充cooling-maps数据
static int thermal_of_populate_bind_params(struct device_node *np,
struct __thermal_bind_params *__tbp,
struct thermal_trip *trips,
int ntrips)
{
struct of_phandle_args cooling_spec;
struct device_node *trip;
int ret, i;
u32 prop;
/* Default weight. Usage is optional */
__tbp->usage = THERMAL_WEIGHT_DEFAULT;
ret = of_property_read_u32(np, "contribution", &prop);
if (ret == 0)
__tbp->usage = prop;
trip = of_parse_phandle(np, "trip", 0);//从设备树中读取trip值
if (!trip) {
pr_err("missing trip property\n");
return -ENODEV;
}
/* match using device_node */
for (i = 0; i < ntrips; i++)//和设备节点进行匹配
if (trip == trips[i].np) {
__tbp->trip_id = i;
break;
}
if (i == ntrips) {
ret = -ENODEV;
goto end;
}
ret = of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells",
0, &cooling_spec);//从cooling-maps的孩子中解析cooling-device,并读取对应参数
if (ret < 0) {
pr_err("missing cooling_device property\n");
goto end;
}
__tbp->cooling_device = cooling_spec.np;
if (cooling_spec.args_count >= 2) { /* at least min and max */
__tbp->min = cooling_spec.args[0];
__tbp->max = cooling_spec.args[1];
} else {
pr_err("wrong reference to cooling device, missing limits\n");
}
end:
of_node_put(trip);
return ret;
}
1.2.3 注册新的热区设备
thermal_zone_device_register函数用于注册新的热区设备,原型如下:
struct thermal_zone_device *thermal_zone_device_register(const char *type,
int trips, int mask, void *devdata,
struct thermal_zone_device_ops *ops,
struct thermal_zone_params *tzp,
int passive_delay, int polling_delay)
{
struct thermal_zone_device *tz;
enum thermal_trip_type trip_type;
int trip_temp;
int result;
int count;
int passive = 0;
struct thermal_governor *governor;
//参数异常检测
if (type && strlen(type) >= THERMAL_NAME_LENGTH)
return ERR_PTR(-EINVAL);
if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips)
return ERR_PTR(-EINVAL);
if (!ops)
return ERR_PTR(-EINVAL);
if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
return ERR_PTR(-EINVAL);
tz = kzalloc(sizeof(struct thermal_zone_device), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);
INIT_LIST_HEAD(&tz->thermal_instances);//初始化链表头
idr_init(&tz->idr);
mutex_init(&tz->lock);
result = get_idr(&thermal_tz_idr, &thermal_idr_lock, &tz->id);
if (result) {
kfree(tz);
return ERR_PTR(result);
}
strlcpy(tz->type, type ? : "", sizeof(tz->type));
tz->ops = ops;
tz->tzp = tzp;
tz->device.class = &thermal_class;
tz->devdata = devdata;
tz->trips = trips;
tz->passive_delay = passive_delay;//从设备树中读取的参数
tz->polling_delay = polling_delay;//从设备树中读取的参数
/* A new thermal zone needs to be updated anyway. */
atomic_set(&tz->need_update, 1);
dev_set_name(&tz->device, "thermal_zone%d", tz->id);
result = device_register(&tz->device);//注册设备,名字为thermal_zone*,可以在/sys/class/thermal/路径下查看到
if (result) {
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
kfree(tz);
return ERR_PTR(result);
}
/* sys I/F */
if (type) {
result = device_create_file(&tz->device, &dev_attr_type);//创建获取type节点
if (result)
goto unregister;
}
result = device_create_file(&tz->device, &dev_attr_temp);//创建获取温度的节点
if (result)
goto unregister;
if (ops->get_mode) {
result = device_create_file(&tz->device, &dev_attr_mode);//创建获取温度的节点
if (result)
goto unregister;
}
result = create_trip_attrs(tz, mask);//创建触发点的属性
if (result)
goto unregister;
for (count = 0; count < trips; count++) {
if (tz->ops->get_trip_type(tz, count, &trip_type))//获取触发点的类型,触发点的类型在enum thermal_trip_type中定义
set_bit(count, &tz->trips_disabled);
if (trip_type == THERMAL_TRIP_PASSIVE)
passive = 1;
if (tz->ops->get_trip_temp(tz, count, &trip_temp))//获取触发温度
set_bit(count, &tz->trips_disabled);
/* Check for bogus trip points */
if (trip_temp == 0)
set_bit(count, &tz->trips_disabled);
}
if (!passive) {
result = device_create_file(&tz->device, &dev_attr_passive);//创建passive的节点
if (result)
goto unregister;
}
result = device_create_file(&tz->device, &dev_attr_passive_delay);//创建passive_delay的节点
if (result)
goto unregister;
result = device_create_file(&tz->device, &dev_attr_polling_delay);//创建polling_delay的节点
if (result)
goto unregister;
if (IS_ENABLED(CONFIG_THERMAL_EMULATION)) {
result = device_create_file(&tz->device, &dev_attr_emul_temp);
if (result)
goto unregister;
}
/* Create policy attribute */
result = device_create_file(&tz->device, &dev_attr_policy);创建policy的节点
if (result)
goto unregister;
/* Add thermal zone params */
result = create_tzp_attrs(&tz->device);//创建热区参数节点属性
if (result)
goto unregister;
/* Create available_policies attribute */
result = device_create_file(&tz->device, &dev_attr_available_policies);//创建available_policies的节点
if (result)
goto unregister;
/* Update 'this' zone's governor information */
mutex_lock(&thermal_governor_lock);
if (tz->tzp)
governor = __find_governor(tz->tzp->governor_name);//查找governor
else
governor = def_governor;
result = thermal_set_governor(tz, governor);thermal zone 和governor 进行绑定
if (result) {
mutex_unlock(&thermal_governor_lock);
goto unregister;
}
mutex_unlock(&thermal_governor_lock);
if (!tz->tzp || !tz->tzp->no_hwmon) {
result = thermal_add_hwmon_sysfs(tz);
if (result)
goto unregister;
}
mutex_lock(&thermal_list_lock);
list_add_tail(&tz->node, &thermal_tz_list);//thermal zone添加到链表
mutex_unlock(&thermal_list_lock);
/* Bind cooling devices for this zone */
bind_tz(tz);
INIT_DEFERRABLE_WORK(&(tz->poll_queue), thermal_zone_device_check);//初始化work,用于循环check温度状态
thermal_zone_device_reset(tz);//thermal zone 设备复位
/* Update the new thermal zone and mark it as already updated. */
if (atomic_cmpxchg(&tz->need_update, 1, 0))//此函数返回&tz->need_update的counter值来判断是否需要更新,如果&tz->need_update的counter值等于1,&tz->need_update的counter值将更新为0
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);//thermal zone 设备更新
return tz;
unregister:
release_idr(&thermal_tz_idr, &thermal_idr_lock, tz->id);
device_unregister(&tz->device);
return ERR_PTR(result);
}
EXPORT_SYMBOL_GPL(thermal_zone_device_register);
1.2.3.1 thermal zone 设备更新函数thermal_zone_device_update
thermal zone 设备更新通过thermal_zone_device_update,并由此建立启动循环检测状态
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
int count;
if (atomic_read(&in_suspend) && (!tz->ops->is_wakeable ||
!(tz->ops->is_wakeable(tz))))状态判断
return;
if (!tz->ops->get_temp)//判断获取温度接口是否存在
return;
trace_thermal_device_update(tz, event);//trace 使用
update_temperature(tz);//更新温度
thermal_zone_set_trips(tz);//找到合适的触发点温度设置
tz->notify_event = event;
for (count = 0; count < tz->trips; count++)
handle_thermal_trip(tz, count);//处理热阈值的函数,根据温度的情况进行相应的热处理
}
1.2.3.1.1 更新温度函数update_temperature
static void update_temperature(struct thermal_zone_device *tz)
{
int temp, ret;
ret = thermal_zone_get_temp(tz, &temp);//获取温度
if (ret) {
if (ret != -EAGAIN)
dev_warn(&tz->device,
"failed to read out thermal zone (%d)\n",
ret);
return;
}
store_temperature(tz, temp);//保存温度
}
1.2.3.1.1.1 获取温度函数thermal_zone_get_temp
int thermal_zone_get_temp(struct thermal_zone_device *tz, int *temp)
{
int ret = -EINVAL;
int count;
int crit_temp = INT_MAX;
enum thermal_trip_type type;
if (!tz || IS_ERR(tz) || !tz->ops->get_temp)
goto exit;
mutex_lock(&tz->lock);
ret = tz->ops->get_temp(tz, temp);//获取温度,此回调函数有thermal_zone_of_sensor_register注册sensor时赋值
if (IS_ENABLED(CONFIG_THERMAL_EMULATION) && tz->emul_temperature) {
for (count = 0; count < tz->trips; count++) {
ret = tz->ops->get_trip_type(tz, count, &type);//来自of_thermal_ops
if (!ret && type == THERMAL_TRIP_CRITICAL) {
ret = tz->ops->get_trip_temp(tz, count,
&crit_temp);//来自of_thermal_ops
break;
}
}
/*
* Only allow emulating a temperature when the real temperature
* is below the critical temperature so that the emulation code
* cannot hide critical conditions.
*/
if (!ret && *temp < crit_temp)
*temp = tz->emul_temperature;
}
trace_thermal_query_temp(tz, *temp);
mutex_unlock(&tz->lock);
exit:
return ret;
}
1.2.3.1.1.2 保存温度函数store_temperature
static void store_temperature(struct thermal_zone_device *tz, int temp)
{
mutex_lock(&tz->lock);
tz->last_temperature = tz->temperature;
tz->temperature = temp;//将温度保存到thermal zone 设备中
mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
if (tz->last_temperature == THERMAL_TEMP_INVALID ||
tz->last_temperature == THERMAL_TEMP_INVALID_LOW)
dev_dbg(&tz->device, "last_temperature N/A, current_temperature=%d\n",
tz->temperature);
else
dev_dbg(&tz->device, "last_temperature=%d, current_temperature=%d\n",
tz->last_temperature, tz->temperature);
}
1.2.3.1.2 thermal_zone_set_trips
void thermal_zone_set_trips(struct thermal_zone_device *tz)
{
int low = -INT_MAX;
int high = INT_MAX;
int trip_temp, hysteresis;
int i, ret;
mutex_lock(&tz->lock);
if (!tz->ops->set_trips || !tz->ops->get_trip_hyst)
goto exit;
//此for循环用于找到最接近temp且小于temp的trip temp
for (i = 0; i < tz->trips; i++) {
int trip_low;
tz->ops->get_trip_temp(tz, i, &trip_temp);//获取触发点温度 来自of_thermal_ops
tz->ops->get_trip_hyst(tz, i, &hysteresis);//此值用于温度低于trip_temp-hysteresis值时恢复状态,来自of_thermal_ops
trip_low = trip_temp - hysteresis;
if (trip_low < tz->temperature && trip_low > low)
low = trip_low;
if (trip_temp > tz->temperature && trip_temp < high)
high = trip_temp;
}
tz->prev_low_trip = low;
tz->prev_high_trip = high;
dev_dbg(&tz->device,
"new temperature boundaries: %d < x < %d\n", low, high);
/*
* Set a temperature window. When this window is left the driver
* must inform the thermal core via thermal_zone_device_update.
*/
ret = tz->ops->set_trips(tz, low, high);//设置触发点,此回调函数有thermal_zone_of_sensor_register注册sensor时赋值
if (ret)
dev_err(&tz->device, "Failed to set trips: %d\n", ret);
trace_thermal_set_trip(tz);
exit:
mutex_unlock(&tz->lock);
}
1.2.3.1.3 处理热阈值函数handle_thermal_trip
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
enum thermal_trip_type type;
/* Ignore disabled trip points */
if (test_bit(trip, &tz->trips_disabled))
return;
tz->ops->get_trip_type(tz, trip, &type);//获取触发类型,来自of_thermal_ops
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
handle_critical_trips(tz, trip, type);//如果是关键触发点,
else
handle_non_critical_trips(tz, trip, type);//如果不是关键的触发点,走governor处理
/*
* Alright, we handled this trip successfully.
* So, start monitoring again.
*/
monitor_thermal_zone(tz);//循环处理
trace_thermal_handle_trip(tz, trip);
}
1.2.3.1.3.1 handle_critical_trips函数
static void handle_critical_trips(struct thermal_zone_device *tz,
int trip, enum thermal_trip_type trip_type)
{
int trip_temp;
tz->ops->get_trip_temp(tz, trip, &trip_temp);//获取触发点温度
/* If we have not crossed the trip_temp, we do not care. */
if (trip_temp <= 0 || tz->temperature < trip_temp)
return;
trace_thermal_zone_trip(tz, trip, trip_type, true);
if (tz->ops->notify)//定义了通知回调,调用通知回调函数
tz->ops->notify(tz, trip, trip_type);
if (trip_type == THERMAL_TRIP_CRITICAL) {//如果是critical类型,关机
dev_emerg(&tz->device,
"critical temperature reached(%d C),shutting down\n",
tz->temperature / 1000);
orderly_poweroff(true);
}
}
1.2.3.1.3.2 handle_non_critical_trips函数
static void handle_non_critical_trips(struct thermal_zone_device *tz,
int trip, enum thermal_trip_type trip_type)
{
tz->governor ? tz->governor->throttle(tz, trip) :
def_governor->throttle(tz, trip);//调用governor 回调
}
governor->throttle回调见以step_wise为例
1.2.3.1.3.3 monitor_thermal_zone函数
static void monitor_thermal_zone(struct thermal_zone_device *tz)
{
mutex_lock(&tz->lock);
if (tz->passive)
thermal_zone_device_set_polling(thermal_passive_wq,
tz, tz->passive_delay);//继续延迟polling
else if (tz->polling_delay)
thermal_zone_device_set_polling(
system_freezable_power_efficient_wq,
tz, tz->polling_delay);//继续延迟polling
else
thermal_zone_device_set_polling(NULL, tz, 0);//取消polling
mutex_unlock(&tz->lock);
}
static void thermal_zone_device_set_polling(struct workqueue_struct *queue,
struct thermal_zone_device *tz,
int delay)
{
if (delay > 1000)
mod_delayed_work(queue, &tz->poll_queue,
round_jiffies(msecs_to_jiffies(delay)));
else if (delay)
mod_delayed_work(queue, &tz->poll_queue,
msecs_to_jiffies(delay));
else
cancel_delayed_work(&tz->poll_queue);
}
延时时间到了后会调用thermal_zone_device_check函数
static void thermal_zone_device_check(struct work_struct *work)
{
struct thermal_zone_device *tz = container_of(work, struct
thermal_zone_device,
poll_queue.work);
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);//此函数回到1.2.3.1将完成循环
}
这篇关于Thermal子系统之thermal_init流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!