Linux Thermal是Linux系统下的温度控制相关模块,主要用于解决设备因性能增强而引起的发热问题,确保设备温度维持在一个安全、舒适的范围内,防止硬件过热导致系统不稳定或缩减芯片寿命。Linux Thermal的主要框架包括获取温度的设备和控制温度的设备,以及一些使用温度控制设备的策略。在Linux Thermal框架中,获取温度的设备被抽象为Thermal Zone Device,控制温度的设备被抽象为Thermal Cooling Device。
内核中源码路径:
Thermal_core.c (drivers\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;
}
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为例
int thermal_gov_step_wise_register(void)
{
?? ?return thermal_register_governor(&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;
}
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);
}
?
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);
}
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;
}
函数原型如下:
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;
}
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,
};
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);
}
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;
}
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);
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);//处理热阈值的函数,根据温度的情况进行相应的热处理
}
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);//保存温度
}
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;
}
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);
}
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);
}
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);
}
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);
?? ?}
}
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为例
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将完成循环
}