1. 框架結(jié)構(gòu)
thermal core:thermal主要的程序,驅(qū)動(dòng)初始化程序,維系thermal zone、governor、cooling device三者的關(guān)系。
thermal zone device:創(chuàng)建thermal zone節(jié)點(diǎn)和連接thermal sensor,在/sys/class/thermal/目錄下的thermal_zone*,通過dtsi文件進(jìn)行配置生成。thermal sensor是溫度傳感器(即熱敏電阻NTC),主要是給thermal提供溫度感知。
thermal govnernor:溫度控制算法,解決溫控發(fā)生時(shí)(即throttle),cooling device如何選擇cooling state的問題。
step_wise
power_allocator
user_space
bang_bang
fair_share
thermal cooling device:系統(tǒng)溫控的執(zhí)行者,實(shí)施冷卻措施的驅(qū)動(dòng)(cpufreq_cooling、cpuidle_cooling、 devfreq_cooling等)。cooling device根據(jù)governor計(jì)算出來的state,實(shí)施冷卻操作,一般情況下,state越高表示系統(tǒng)的冷卻需求越高。cooling device需要與trip point進(jìn)行綁定,當(dāng) trip point 觸發(fā)后,由相應(yīng)的cooling device 去實(shí)施冷卻措施。
2. 代碼結(jié)構(gòu)
2.1 thermal core
thermal_core.c主要是初始化driver,注冊governor,解析dtsi文件,創(chuàng)建thermal zone和初始通信。
2.1.1 thermal結(jié)構(gòu)體定義
thermal.h,公開接口給其他驅(qū)動(dòng)程序調(diào)用
// thermal_zone_device ops配置
struct thermal_zone_device_ops {
int (*bind) (structthermal_zone_device *,
structthermal_cooling_device *);
int (*unbind)(struct thermal_zone_device *,
structthermal_cooling_device *);
int (*get_temp)(struct thermal_zone_device *, int *);
int (*set_trips)(struct thermal_zone_device *, int, int);
int (*change_mode)(struct thermal_zone_device *,
enumthermal_device_mode);
int (*get_trip_type)(struct thermal_zone_device *, int,
enumthermal_trip_type *);
int(*get_trip_temp) (struct thermal_zone_device *, int, int *);
int(*set_trip_temp) (struct thermal_zone_device *, int, int);
int(*get_trip_hyst) (struct thermal_zone_device *, int, int *);
int(*set_trip_hyst) (struct thermal_zone_device *, int, int);
int(*get_crit_temp) (struct thermal_zone_device *, int *);
int (*set_emul_temp)(struct thermal_zone_device *, int);
int (*get_trend)(struct thermal_zone_device *, int,
enum thermal_trend*);
int (*notify)(struct thermal_zone_device *, int,
enumthermal_trip_type);
};
// thermal_cooling_device ops配置
struct thermal_cooling_device_ops {
int(*get_max_state) (struct thermal_cooling_device *, unsigned long *);
int(*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
int (*set_cur_state)(struct thermal_cooling_device *, unsigned long);
int(*get_requested_power)(struct thermal_cooling_device *, u32 *);
int(*state2power)(struct thermal_cooling_device *, unsigned long, u32 *);
int(*power2state)(struct thermal_cooling_device *, u32, unsigned long *);
};
thermal_core.c
//thermal驅(qū)動(dòng)的init入口函數(shù)
static int __init thermal_init(void)
{
int result;
// generic netlink初始化
result =thermal_netlink_init();
if (result)
goto error;
// 注冊thermal governor
result =thermal_register_governors();
if (result)
goto error;
// 注冊/sys/class/thermal節(jié)點(diǎn)
result =class_register(&thermal_class);
if (result)
goto unregister_governors;
// 解析dtsi配置文件中的thermal-zones字段,并注冊thermal_zone_device
result =of_parse_thermal_zones();
if (result)
goto unregister_class;
// 注冊notifier
result =register_pm_notifier(&thermal_pm_nb);
if (result)
pr_warn("Thermal: Can not register suspend notifier, return %d ",
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);
mutex_destroy(&poweroff_lock);
return result;
}
/* 將所有的governor策略(step_wise、power_allocator、user_space、fair_share)默認(rèn)都注冊給kernel */
static int __init thermal_register_governors(void)
{
int ret = 0;
structthermal_governor **governor;
//遍歷注冊所有的governor策略
for_each_governor_table(governor) {
//注冊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) {
structthermal_governor **gov;
for_each_governor_table(gov) {
if (gov ==governor)
break;
thermal_unregister_governor(*gov);
}
}
return ret;
}
// 將新governor添加到全局governor_list,設(shè)置默認(rèn)的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)) {
bool match_default;
err = 0;
// 將新governor添加到全局governor_list
list_add(&governor->governor_list, &thermal_governor_list);
// 查找匹配默認(rèn)的governor,并設(shè)置默認(rèn)值
match_default = !strncmp(governor->name,
DEFAULT_THERMAL_GOVERNOR,
THERMAL_NAME_LENGTH);
// Kconfig中配置默認(rèn)的governor
if (!def_governor && match_default)
def_governor = governor;
}
mutex_lock(&thermal_list_lock);
list_for_each_entry(pos, &thermal_tz_list, node) {
/*
* 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)) {
int ret;
ret = thermal_set_governor(pos, governor);
if (ret)
dev_err(&pos->device,
"Failed to set governor %s for thermal zone %s: %d ",
governor->name, pos->type, ret);
}
}
mutex_unlock(&thermal_list_lock);
mutex_unlock(&thermal_governor_lock);
return err;
}
// 注冊一個(gè)thermal_zone_device
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 id;
int result;
int count;
struct thermal_governor *governor;
if (!type || strlen(type) == 0) {
pr_err("Error: No thermal zone type defined ");
return ERR_PTR(-EINVAL);
}
if (type && strlen(type) >= THERMAL_NAME_LENGTH) {
pr_err("Error: Thermal zone name (%s) too long, should be under %d chars ",
type, THERMAL_NAME_LENGTH);
return ERR_PTR(-EINVAL);
}
if (trips > THERMAL_MAX_TRIPS || trips < 0 || mask >> trips) {
pr_err("Error: Incorrect number of thermal trips ");
return ERR_PTR(-EINVAL);
}
if (!ops) {
pr_err("Error: Thermal zone device ops not defined ");
return ERR_PTR(-EINVAL);
}
if (trips > 0 && (!ops->get_trip_type || !ops->get_trip_temp))
return ERR_PTR(-EINVAL);
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);
// 初始化一個(gè)鏈表thermal_instances
INIT_LIST_HEAD(&tz->thermal_instances);
ida_init(&tz->ida);
mutex_init(&tz->lock);
// 自動(dòng)分配id
id = ida_simple_get(&thermal_tz_ida, 0, 0, GFP_KERNEL);
if (id < 0) {
result = id;
goto free_tz;
}
tz->id = id;
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;
/* sys I/F */
/* Add nodes that are always present via .groups */
result = thermal_zone_create_device_groups(tz, mask);
if (result)
goto remove_id;
/* A new thermal zone needs to be updated anyway. */
atomic_set(&tz->need_update, 1);
// 設(shè)置thermal_zone節(jié)點(diǎn)名稱
dev_set_name(&tz->device, "thermal_zone%d", tz->id);
result = device_register(&tz->device);
if (result)
goto release_device;
for (count = 0; count < trips; count++) {
if (tz->ops->get_trip_type(tz, count, &trip_type))
set_bit(count, &tz->trips_disabled);
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);
}
/* Update 'this' zone's governor information */
mutex_lock(&thermal_governor_lock);
// thermal_zone設(shè)置governor,否則默認(rèn)governor
if (tz->tzp)
governor = __find_governor(tz->tzp->governor_name);
else
governor = def_governor;
result = thermal_set_governor(tz, 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);
// 將thermal zone加入到thermal_tz_list
list_add_tail(&tz->node, &thermal_tz_list);
mutex_unlock(&thermal_list_lock);
// 將thermal_cdev_list上的cooling設(shè)備綁定到thermal_zone_device上
bind_tz(tz);
// 初始化work queue下半部分,處理中斷需要響應(yīng)的操作,定時(shí)去調(diào)用thermal_zone_device_update函數(shù)
// 設(shè)置polling_delay值為輪詢周期
INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
// 對thermal zone的溫度等復(fù)位。
thermal_zone_device_reset(tz);
/* Update the new thermal zone and mark it as already updated. */
if (atomic_cmpxchg(&tz->need_update, 1, 0))
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
thermal_notify_tz_create(tz->id, tz->type);
return tz;
unregister:
device_del(&tz->device);
release_device:
put_device(&tz->device);
tz = NULL;
remove_id:
ida_simple_remove(&thermal_tz_ida, id);
free_tz:
kfree(tz);
return ERR_PTR(result);
}
// 將thermal_cdev_list上的cooling設(shè)備綁定到thermal_zone_device上
static void bind_tz(struct thermal_zone_device *tz)
{
int i, ret;
struct thermal_cooling_device *pos = NULL;
const struct thermal_zone_params *tzp = tz->tzp;
if (!tzp && !tz->ops->bind)
return;
mutex_lock(&thermal_list_lock);
/* If there is ops->bind, try to use ops->bind */
if (tz->ops->bind) {
// 遍歷thermal_cdev_list,綁定cooling設(shè)備
list_for_each_entry(pos, &thermal_cdev_list, node) {
ret = tz->ops->bind(tz, pos);
if (ret)
print_bind_err_msg(tz, pos, ret);
}
goto exit;
}
if (!tzp || !tzp->tbp)
goto exit;
list_for_each_entry(pos, &thermal_cdev_list, node) {
for (i = 0; i < tzp->num_tbps; i++) {
if (tzp->tbp[i].cdev || !tzp->tbp[i].match)
continue;
if (tzp->tbp[i].match(tz, pos))
continue;
tzp->tbp[i].cdev = pos;
__bind(tz, tzp->tbp[i].trip_mask, pos,
tzp->tbp[i].binding_limits,
tzp->tbp[i].weight);
}
}
exit:
mutex_unlock(&thermal_list_lock);
}
// 檢查thermal_zone_device
static void thermal_zone_device_check(struct work_struct *work)
{
//通過結(jié)構(gòu)體成員變量地址來獲取這個(gè)thermal_zone_device結(jié)構(gòu)體的地址
struct thermal_zone_device *tz = container_of(work, struct
thermal_zone_device,
poll_queue.work);
//更新thermal_zone_device
thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}
void thermal_zone_device_update(struct thermal_zone_device *tz,
enum thermal_notify_event event)
{
int count;
//判斷是否需要輪詢的方式
if (should_stop_polling(tz))
return;
if (atomic_read(&in_suspend))
return;
// 判斷sensor是否實(shí)現(xiàn)get_temp函數(shù)
if (!tz->ops->get_temp)
return;
// 更新sensor溫度,也就是thermal_zone的溫度
update_temperature(tz);
// 更新trip值
thermal_zone_set_trips(tz);
tz->notify_event = event;
for (count = 0; count < tz->trips; count++)
handle_thermal_trip(tz, count);
}
// 更新thermal zone溫度
static void update_temperature(struct thermal_zone_device *tz)
{
int temp, ret;
// 獲取thermal_zone的溫度
ret = thermal_zone_get_temp(tz, &temp);
if (ret) {
if (ret != -EAGAIN)
dev_warn(&tz->device,
"failed to read out thermal zone (%d) ",
ret);
return;
}
mutex_lock(&tz->lock);
tz->last_temperature = tz->temperature;
tz->temperature = temp;
mutex_unlock(&tz->lock);
trace_thermal_temperature(tz);
thermal_genl_sampling_temp(tz->id, temp);
}
// 遍歷處理符合條件的trips
static void handle_thermal_trip(struct thermal_zone_device *tz, int trip)
{
enum thermal_trip_type type;
int trip_temp, hyst = 0;
/* Ignore disabled trip points */
if (test_bit(trip, &tz->trips_disabled))
return;
// 獲取trip_temp、trip_type、get_trip_hyst
tz->ops->get_trip_temp(tz, trip, &trip_temp);
tz->ops->get_trip_type(tz, trip, &type);
if (tz->ops->get_trip_hyst)
tz->ops->get_trip_hyst(tz, trip, &hyst);
if (tz->last_temperature != THERMAL_TEMP_INVALID) {
// 觸發(fā)trip
if (tz->last_temperature < trip_temp &&
tz->temperature >= trip_temp)
thermal_notify_tz_trip_up(tz->id, trip);
// 觸發(fā)hysteresis
if (tz->last_temperature >= trip_temp &&
tz->temperature < (trip_temp - hyst))
thermal_notify_tz_trip_down(tz->id, trip);
}
// 處理critical||hot的trips
if (type == THERMAL_TRIP_CRITICAL || type == THERMAL_TRIP_HOT)
handle_critical_trips(tz, trip, type);
// 如果設(shè)置了governor,調(diào)用governor->throttle函數(shù)
else
handle_non_critical_trips(tz, trip);
/*
* Alright, we handled this trip successfully.
* So, start monitoring again.
*/
monitor_thermal_zone(tz);
}
// 處理type為critical||hot的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);
if (tz->ops->notify)
tz->ops->notify(tz, trip, trip_type);
// 如果是critical,準(zhǔn)備關(guān)機(jī)
if (trip_type == THERMAL_TRIP_CRITICAL) {
dev_emerg(&tz->device,
"critical temperature reached (%d C), shutting down ",
tz->temperature / 1000);
mutex_lock(&poweroff_lock);
if (!power_off_triggered) {
/*
* Queue a backup emergency shutdown in the event of
* orderly_poweroff failure
*/
// 調(diào)用thermal_emergency_poweroff準(zhǔn)備關(guān)機(jī)操作
thermal_emergency_poweroff();
orderly_poweroff(true);
power_off_triggered = true;
}
mutex_unlock(&poweroff_lock);
}
}
//處理其他的trips
//一般情況都是這個(gè)
static void handle_non_critical_trips(struct thermal_zone_device *tz, int trip)
{
//如果設(shè)置了governor,調(diào)用governor->throttle函數(shù)
//否則調(diào)用默認(rèn)的
tz->governor ? tz->governor->throttle(tz, trip) :
def_governor->throttle(tz, trip);
}
// 監(jiān)控delay時(shí)間進(jìn)行延時(shí)后工作
static void monitor_thermal_zone(struct thermal_zone_device *tz)
{
bool stop;
stop = should_stop_polling(tz);
mutex_lock(&tz->lock);
// 超過閥值輪詢時(shí)間
if (!stop && tz->passive)
thermal_zone_device_set_polling(tz, tz->passive_delay);
// 未超過閥值輪詢時(shí)間
else if (!stop && tz->polling_delay)
thermal_zone_device_set_polling(tz, tz->polling_delay);
// 取消調(diào)用,不輪詢
else
thermal_zone_device_set_polling(tz, 0);
mutex_unlock(&tz->lock);
}
static void thermal_zone_device_set_polling(struct thermal_zone_device *tz,
int delay)
{
//需延時(shí)后再使用system_freezable_power_efficient_wq的工作隊(duì)列進(jìn)行工作
if (delay > 1000)
mod_delayed_work(system_freezable_power_efficient_wq,
&tz->poll_queue,
round_jiffies(msecs_to_jiffies(delay)));
else if (delay)
mod_delayed_work(system_freezable_power_efficient_wq,
&tz->poll_queue,
msecs_to_jiffies(delay));
// 刪除提交到工作隊(duì)列的任務(wù),不輪詢
else
cancel_delayed_work(&tz->poll_queue);
}
在thermal_helps.c定義thermal_zone_get_temp函數(shù)
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);
// 獲取當(dāng)前sensor的溫度,sensor里面實(shí)現(xiàn)
ret = tz->ops->get_temp(tz, temp);
// thermal debug開關(guān)
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);
if (!ret && type == THERMAL_TRIP_CRITICAL) {
ret = tz->ops->get_trip_temp(tz, count,
&crit_temp);
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;
}
mutex_unlock(&tz->lock);
exit:
return ret;
}
// 獲取當(dāng)前溫度下的thermal zone下一次的trip點(diǎn)
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 (i = 0; i < tz->trips; i++) {
int trip_low;
// 獲取sensor的觸發(fā)溫度
tz->ops->get_trip_temp(tz, i, &trip_temp);
// 獲取sensor下降溫度值恢復(fù)狀態(tài)
tz->ops->get_trip_hyst(tz, i, &hysteresis);
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;
}
/* No need to change trip points */
// 與上次對比相同,不需要進(jìn)行更新
if (tz->prev_low_trip == low && tz->prev_high_trip == high)
goto exit;
tz->prev_low_trip = low;
tz->prev_high_trip = high;
dev_dbg(&tz->device,
"new temperature boundaries: %d < x < %d ", low, high);
/*
* Set a temperature window. When this window is left the driver
* must inform the thermal core via thermal_zone_device_update.
*/
// 設(shè)置新的溫度trip
ret = tz->ops->set_trips(tz, low, high);
if (ret)
dev_err(&tz->device, "Failed to set trips: %d ", ret);
exit:
mutex_unlock(&tz->lock);
}
2.2 dtsi文件解析
thermal_of.c解析dtsi文件,主要函數(shù)是of_parse_thermal_zones,創(chuàng)建解析生成thermal zone節(jié)點(diǎn)。
//解析dtsi中的&thermal_zones
int __init of_parse_thermal_zones(void)
{
struct device_node *np, *child;
struct __thermal_zone *tz;
struct thermal_zone_device_ops *ops;
//查找到thermal-zones
np = of_find_node_by_name(NULL, "thermal-zones");
if (!np) {
pr_debug("unable to find thermal zones ");
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;
//創(chuàng)建一個(gè)thermal zone節(jié)點(diǎn)
tz = thermal_of_build_thermal_zone(child);
if (IS_ERR(tz)) {
pr_err("failed to build thermal zone %pOFn: %ld ",
child,
PTR_ERR(tz));
continue;
}
//申請一段新內(nèi)存,并將of_thermal_ops中的內(nèi)容復(fù)制到新申請的這段內(nèi)存中
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;
// 解析sustainable-power字段
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;
// 向thermal_core注冊thermal_zone_device
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 ", 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;
}
//創(chuàng)建一個(gè)thermal zone節(jié)點(diǎn)
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 ");
return ERR_PTR(-EINVAL);
}
tz = kzalloc(sizeof(*tz), GFP_KERNEL);
if (!tz)
return ERR_PTR(-ENOMEM);
//解析polling-delay-passive,超過閥值輪詢時(shí)間
ret = of_property_read_u32(np, "polling-delay-passive", &prop);
if (ret < 0) {
pr_err("%pOFn: missing polling-delay-passive property ", np);
goto free_tz;
}
tz->passive_delay = prop;
//解析polling-delay,未超閥值輪詢時(shí)間
ret = of_property_read_u32(np, "polling-delay", &prop);
if (ret < 0) {
pr_err("%pOFn: missing polling-delay property ", np);
goto free_tz;
}
tz->polling_delay = prop;
/*
* 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.
*/
//暫時(shí)支持一個(gè)sensor對應(yīng)一個(gè)thermal zone
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 */
// 查找trips字段
child = of_get_child_by_name(np, "trips");
/* No trips provided */
if (!child)
goto finish;
//獲取trips字段下child數(shù)量
tz->ntrips = of_get_child_count(child);
if (tz->ntrips == 0) /* must have at least one child */
goto finish;
tz->trips = kcalloc(tz->ntrips, sizeof(*tz->trips), GFP_KERNEL);
if (!tz->trips) {
ret = -ENOMEM;
goto free_tz;
}
i = 0;
for_each_child_of_node(child, gchild) {
// 遍歷解析trips字段下面的字段
ret = thermal_of_populate_trip(gchild, &tz->trips[i++]);
if (ret)
goto free_trips;
}
// 減少節(jié)點(diǎn)引用
of_node_put(child);
/* cooling-maps */
// 查找cooling-maps字段
child = of_get_child_by_name(np, "cooling-maps");
/* cooling-maps not provided */
if (!child)
goto finish;
tz->num_tbps = of_get_child_count(child);
if (tz->num_tbps == 0)
goto finish;
tz->tbps = kcalloc(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下的字段,綁定cooling device
ret = thermal_of_populate_bind_params(gchild, &tz->tbps[i++],
tz->trips, tz->ntrips);
if (ret)
goto free_tbps;
}
finish:
of_node_put(child);
return tz;
free_tbps:
for (i = i - 1; i >= 0; i--) {
struct __thermal_bind_params *tbp = tz->tbps + i;
int j;
for (j = 0; j < tbp->count; j++)
of_node_put(tbp->tcbp[j].cooling_device);
kfree(tbp->tcbp);
}
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);
}
// 遍歷解析trips下的字段
static int thermal_of_populate_trip(struct device_node *np,
struct thermal_trip *trip)
{
int prop;
int ret;
// 解析temperature字段,觸發(fā)溫度值
ret = of_property_read_u32(np, "temperature", &prop);
if (ret < 0) {
pr_err("missing temperature property ");
return ret;
}
trip->temperature = prop;
// 解析hysteresis字段,下降溫度值恢復(fù)狀態(tài)
ret = of_property_read_u32(np, "hysteresis", &prop);
if (ret < 0) {
pr_err("missing hysteresis property ");
return ret;
}
trip->hysteresis = prop;
// 解析type字段,一般配置為passive,當(dāng)溫控發(fā)生后由governor控制
ret = thermal_of_get_trip_type(np, &trip->type);
if (ret < 0) {
pr_err("wrong trip type property ");
return ret;
}
/* Required for cooling map matching */
trip->np = np;
of_node_get(np);
return 0;
}
//解析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 __thermal_cooling_bind_param *__tcbp;
struct device_node *trip;
int ret, i, count;
u32 prop;
// 默認(rèn)contribution字段,表示權(quán)重值,可選
__tbp->usage = THERMAL_WEIGHT_DEFAULT;
ret = of_property_read_u32(np, "contribution", &prop);
if (ret == 0)
__tbp->usage = prop;
// 獲取trip字段下phandle
trip = of_parse_phandle(np, "trip", 0);
if (!trip) {
pr_err("missing trip property ");
return -ENODEV;
}
//匹配trips列表中的trip
for (i = 0; i < ntrips; i++)
if (trip == trips[i].np) {
__tbp->trip_id = i;
break;
}
if (i == ntrips) {
ret = -ENODEV;
goto end;
}
//獲取cooling-device的phandle個(gè)數(shù)
count = of_count_phandle_with_args(np, "cooling-device",
"#cooling-cells");
if (count <= 0) {
pr_err("Add a cooling_device property with at least one device ");
ret = -ENOENT;
goto end;
}
__tcbp = kcalloc(count, sizeof(*__tcbp), GFP_KERNEL);
if (!__tcbp) {
ret = -ENOMEM;
goto end;
}
for (i = 0; i < count; i++) {
//獲取cooling-device的phandle參數(shù)
ret = of_parse_phandle_with_args(np, "cooling-device",
"#cooling-cells", i, &cooling_spec);
if (ret < 0) {
pr_err("Invalid cooling-device entry ");
goto free_tcbp;
}
__tcbp[i].cooling_device = cooling_spec.np;
//參數(shù)個(gè)數(shù)必須大于等于2,寫最小最大的范圍值,代表可調(diào)整最小最大檔位
if (cooling_spec.args_count >= 2) { /* at least min and max */
__tcbp[i].min = cooling_spec.args[0];
__tcbp[i].max = cooling_spec.args[1];
} else {
pr_err("wrong reference to cooling device, missing limits ");
}
}
__tbp->tcbp = __tcbp;
__tbp->count = count;
goto end;
free_tcbp:
for (i = i - 1; i >= 0; i--)
of_node_put(__tcbp[i].cooling_device);
kfree(__tcbp);
end:
of_node_put(trip);
return ret;
}
thermal_extra.drawio.svg
2.3 thermal governor
目前可配置默認(rèn)的thermal governor策略
/* Default Thermal Governor */
#if defined(CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE)
#define DEFAULT_THERMAL_GOVERNOR "step_wise"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE)
#define DEFAULT_THERMAL_GOVERNOR "fair_share"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE)
#define DEFAULT_THERMAL_GOVERNOR "user_space"
#elif defined(CONFIG_THERMAL_DEFAULT_GOV_POWER_ALLOCATOR)
#define DEFAULT_THERMAL_GOVERNOR "power_allocator"
#endif
Kconfig中配置thermalgovernor默認(rèn)step_wise下降溫度值恢復(fù)狀態(tài)
config THERMAL_WRITABLE_TRIPS
bool "Enable writable trip points"
help
This option allows the system integrator to choose whether
trip temperatures can be changed from userspace. The
writable trips need to be specified when setting up the
thermal zone but the choice here takes precedence.
Say 'Y' here if you would like to allow userspace tools to
change trip temperatures.
choice
prompt "Default Thermal governor"
default THERMAL_DEFAULT_GOV_STEP_WISE
help
This option sets which thermal governor shall be loaded at
startup. If in doubt, select 'step_wise'.
config THERMAL_DEFAULT_GOV_STEP_WISE
bool "step_wise"
select THERMAL_GOV_STEP_WISE
help
Use the step_wise governor as default. This throttles the
devices one step at a time.
config THERMAL_DEFAULT_GOV_FAIR_SHARE
bool "fair_share"
select THERMAL_GOV_FAIR_SHARE
help
Use the fair_share governor as default. This throttles the
devices based on their 'contribution' to a zone. The
contribution should be provided through platform data.
config THERMAL_DEFAULT_GOV_USER_SPACE
bool "user_space"
select THERMAL_GOV_USER_SPACE
help
Select this if you want to let the user space manage the
platform thermals.
config THERMAL_DEFAULT_GOV_POWER_ALLOCATOR
bool "power_allocator"
depends on THERMAL_GOV_POWER_ALLOCATOR
help
Select this if you want to control temperature based on
system and device power allocation. This governor can only
operate on cooling devices that implement the power API.
endchoice
2.3.1 step_wise governor
step_wise governor 是每個(gè)輪詢周期逐級提高冷卻狀態(tài),是一種相對溫和的溫控策略。根據(jù)cur_state、溫升趨勢trend、是否throttle去計(jì)算cooling_device的target_state,從而達(dá)到控制cooling_device來控制溫升。
對于cooling state的計(jì)算策略:
1.當(dāng)溫升趨勢為上升且發(fā)生throttle,使用更高一級的cooling state
2.當(dāng)溫升趨勢為下降
若發(fā)生throttle,不改變coolingstate
若解除throttle,使用更低一級的coolingstate
1.當(dāng)達(dá)到最高溫線且發(fā)生throttle,使用最高級的 cooling state
2.當(dāng)達(dá)到最低溫線且發(fā)生throttle,使用最低級的cooling state
注意: cooling state 取值范圍在[instance->lower,instance->upper],若cur_state< instance->lower,target_state則取值為THERMAL_NO_TARGET。
代碼框架圖
thermal.h定義了溫升趨勢trend。
enum thermal_trend { THERMAL_TREND_STABLE, /* 穩(wěn)定temperature is stable */ THERMAL_TREND_RAISING,/* 上升 temperature is raising */ THERMAL_TREND_DROPPING, /* 下降temperature is dropping */ THERMAL_TREND_RAISE_FULL, /* 最高溫線apply highest cooling action */ THERMAL_TREND_DROP_FULL, /* 最低溫線apply lowest cooling action */ };
gov_step_wise.c
static int step_wise_throttle(struct thermal_zone_device *tz, int trip) { struct thermal_instance*instance; // 更新trip、trend和計(jì)算cooling_device的target_state thermal_zone_trip_update(tz, trip); if (tz->forced_passive) thermal_zone_trip_update(tz, THERMAL_TRIPS_NONE); mutex_lock(&tz->lock); // 遍歷更新cooling_device的state list_for_each_entry(instance, &tz->thermal_instances, tz_node) thermal_cdev_update(instance->cdev); mutex_unlock(&tz->lock); return 0; } // 更新trip、trend和計(jì)算cooling_device的target_state static void thermal_zone_trip_update(struct thermal_zone_device *tz, int trip) { int trip_temp; enum thermal_trip_type trip_type; enum thermal_trend trend; struct thermal_instance *instance; bool throttle = false; int old_target; // 獲取trip的類型和溫度 if (trip == THERMAL_TRIPS_NONE) { trip_temp = tz->forced_passive; trip_type = THERMAL_TRIPS_NONE; } else { tz->ops->get_trip_temp(tz, trip, &trip_temp); tz->ops->get_trip_type(tz, trip, &trip_type); } // 獲取溫升趨勢,穩(wěn)定(THERMAL_TREND_STABLE),上升(THERMAL_TREND_RAISING),下降(THERMAL_TREND_DROPPING) trend = get_tz_trend(tz, trip); // 當(dāng)zone溫度大于trip_temp,則需要進(jìn)行觸發(fā) if (tz->temperature >= trip_temp) { throttle = true; trace_thermal_zone_trip(tz, trip, trip_type); } dev_dbg(&tz->device, "Trip%d[type=%d,temp=%d]:trend=%d,throttle=%d ", trip, trip_type, trip_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; // 計(jì)算cooling_device的target_state instance->target = get_target_state(instance, trend, throttle); dev_dbg(&instance->cdev->device, "old_target=%d, target=%d ", old_target, (int)instance->target); if (instance->initialized && old_target == instance->target) continue; /* Activate a passive thermal instance */ if (old_target == THERMAL_NO_TARGET && instance->target != THERMAL_NO_TARGET) update_passive_instance(tz, trip_type, 1); /* Deactivate a passive thermal instance */ else if (old_target != THERMAL_NO_TARGET && instance->target == THERMAL_NO_TARGET) update_passive_instance(tz, trip_type, -1); instance->initialized = true; mutex_lock(&instance->cdev->lock); instance->cdev->updated = false; /* cdev needs update */ mutex_unlock(&instance->cdev->lock); } mutex_unlock(&tz->lock); } // 計(jì)算cooling_device的target_state static unsigned long get_target_state(struct thermal_instance *instance, enum thermal_trend trend,bool throttle) { struct thermal_cooling_device *cdev = instance->cdev; unsigned long cur_state; unsigned long next_target; /* * We keep this instance the way it is by default. * Otherwise, we use the current state of the * cdev in use to determine the next_target. */ //獲取cooling device的當(dāng)前state cdev->ops->get_cur_state(cdev, &cur_state); next_target = instance->target; dev_dbg(&cdev->device, "cur_state=%ld ", cur_state); //如果沒有初始化 if (!instance->initialized) { if (throttle) { // next_target初始值為(cur_state + 1),取值范圍在[instance->lower,instance->upper] next_target = (cur_state + 1) >= instance->upper ? instance->upper : ((cur_state + 1) < instance->lower ? instance->lower : (cur_state + 1)); } else { next_target = THERMAL_NO_TARGET; } return next_target; } switch (trend) { // 當(dāng)溫升趨勢為上升且發(fā)生throttle,使用更高一級的cooling state // 取值范圍在[instance->lower,instance->upper] case THERMAL_TREND_RAISING: if (throttle) { next_target = cur_state < instance->upper ? (cur_state + 1) : instance->upper; if (next_target < instance->lower) next_target = instance->lower; } break; // 當(dāng)達(dá)到最高溫線且發(fā)生throttle,使用最高級的cooling state,將溫度快速降下來 case THERMAL_TREND_RAISE_FULL: if (throttle) next_target = instance->upper; break; // 當(dāng)溫升趨勢為下降 // 發(fā)生throttle,不改變cooling state // 解除throttle,使用低一級的cooling state case THERMAL_TREND_DROPPING: if (cur_state <= instance->lower) { if (!throttle) next_target = THERMAL_NO_TARGET; } else { if (!throttle) { next_target = cur_state - 1; if (next_target > instance->upper) next_target = instance->upper; } } break; // 當(dāng)達(dá)到最低溫線且發(fā)生throttle,使用最低級的cooling state case THERMAL_TREND_DROP_FULL: if (cur_state == instance->lower) { if (!throttle) next_target = THERMAL_NO_TARGET; } else next_target = instance->lower; break; default: break; } return next_target; }
thermal_cdev_update函數(shù)
// 更新cooling device的state void thermal_cdev_update(struct thermal_cooling_device *cdev) { struct thermal_instance *instance; unsigned long target = 0; mutex_lock(&cdev->lock); /* cooling device is updated*/ if (cdev->updated) { mutex_unlock(&cdev->lock); return; } /* Make sure cdev enters the deepest cooling state */ list_for_each_entry(instance, &cdev->thermal_instances, cdev_node) { dev_dbg(&cdev->device, "zone%d->target=%lu ", instance->tz->id, instance->target); if (instance->target == THERMAL_NO_TARGET) continue; if (instance->target > target) target = instance->target; } // 設(shè)置cooling device的state thermal_cdev_set_cur_state(cdev, target); cdev->updated = true; mutex_unlock(&cdev->lock); trace_cdev_update(cdev, target); dev_dbg(&cdev->device, "set to state %lu ", target); }
2.3.2 power_allocator governor
IPA(Intelligent PowerAllocation)
代碼框架圖
功耗均衡原理圖
gov_power_allocator.c
static int power_allocator_throttle(struct thermal_zone_device *tz, int trip)
{
int ret;
int switch_on_temp, control_temp;
struct power_allocator_params *params = tz->governor_data;
/*
* We get called for every trip point but we only need to do
* our calculations once
*/
if (trip != params->trip_max_desired_temperature)
return 0;
// 獲取trip溫度,作為switch_on觸發(fā)溫度
ret = tz->ops->get_trip_temp(tz, params->trip_switch_on,
&switch_on_temp);
if (!ret && (tz->temperature < switch_on_temp)) {
tz->passive = 0;
reset_pid_controller(params);
allow_maximum_power(tz);
return 0;
}
tz->passive = 1;
// 獲取trip溫度,作為目標(biāo)的溫度值
ret = tz->ops->get_trip_temp(tz, params->trip_max_desired_temperature,
&control_temp);
if (ret) {
dev_warn(&tz->device,
"Failed to get the maximum desired temperature: %d ",
ret);
return ret;
}
// IPA主要的算法邏輯
return allocate_power(tz, control_temp);
}
// IPA主要的算法邏輯
static int allocate_power(struct thermal_zone_device *tz,
int control_temp)
{
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
u32 *req_power, *max_power, *granted_power, *extra_actor_power;
u32 *weighted_req_power;
u32 total_req_power, max_allocatable_power, total_weighted_req_power;
u32 total_granted_power, power_range;
int i, num_actors, total_weight, ret = 0;
int trip_max_desired_temperature = params->trip_max_desired_temperature;
mutex_lock(&tz->lock);
num_actors = 0;
total_weight = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if ((instance->trip == trip_max_desired_temperature) &&
cdev_is_power_actor(instance->cdev)) {
num_actors++;
total_weight += instance->weight;
}
}
if (!num_actors) {
ret = -ENODEV;
goto unlock;
}
/*
* We need to allocate five arrays of the same size:
* req_power, max_power, granted_power, extra_actor_power and
* weighted_req_power. They are going to be needed until this
* function returns. Allocate them all in one go to simplify
* the allocation and deallocation logic.
*/
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*max_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power));
BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power));
req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL);
if (!req_power) {
ret = -ENOMEM;
goto unlock;
}
max_power = &req_power[num_actors];
granted_power = &req_power[2 * num_actors];
extra_actor_power = &req_power[3 * num_actors];
weighted_req_power = &req_power[4 * num_actors];
i = 0;
total_weighted_req_power = 0;
total_req_power = 0;
max_allocatable_power = 0;
// 遍歷所有的cooling device
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
int weight;
struct thermal_cooling_device *cdev = instance->cdev;
if (instance->trip != trip_max_desired_temperature)
continue;
// cooling device的ops的函數(shù)指針get_requested_power、state2power和power2state是否存在
if (!cdev_is_power_actor(cdev))
continue;
// 獲取cooling device的功耗需求requested power
if (cdev->ops->get_requested_power(cdev, &req_power[i]))
continue;
if (!total_weight)
weight = 1 << FRAC_BITS;
else
weight = instance->weight;
//獲取cooling device的權(quán)重功耗,weight*requested_power
weighted_req_power[i] = frac_to_int(weight * req_power[i]);
// 獲取cooling device可以消耗的最大功率
if (power_actor_get_max_power(cdev, &max_power[i]))
continue;
// 總的cdev需要的功耗
total_req_power += req_power[i];
// 總的最大可分配的功耗
max_allocatable_power += max_power[i];
// 總的cdev需要的權(quán)重功耗
total_weighted_req_power += weighted_req_power[i];
i++;
}
// PID控制算法,power_range是當(dāng)前溫度下可配置的最大功耗值
power_range = pid_controller(tz, control_temp, max_allocatable_power);
// 分?jǐn)傆?jì)算出當(dāng)前溫度下每個(gè)cooling device的最終的total granted_power
// 公式:total granted_power = granted_power + extra_granted_power
divvy_up_power(weighted_req_power, max_power, num_actors,
total_weighted_req_power, power_range, granted_power,
extra_actor_power);
total_granted_power = 0;
i = 0;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
if (instance->trip != trip_max_desired_temperature)
continue;
if (!cdev_is_power_actor(instance->cdev))
continue;
// 給cooling device設(shè)置granted_power
power_actor_set_power(instance->cdev, instance,
granted_power[i]);
total_granted_power += granted_power[i];
i++;
}
trace_thermal_power_allocator(tz, req_power, total_req_power,
granted_power, total_granted_power,
num_actors, power_range,
max_allocatable_power, tz->temperature,
control_temp - tz->temperature);
kfree(req_power);
unlock:
mutex_unlock(&tz->lock);
return ret;
}
// 分?jǐn)傆?jì)算出當(dāng)前溫度下cooling device的最終的total_granted_power
static void divvy_up_power(u32 *req_power, u32 *max_power, int num_actors,
u32 total_req_power, u32 power_range,
u32 *granted_power, u32 *extra_actor_power)
{
u32 extra_power, capped_extra_power;
int i;
/*
* Prevent division by 0 if none of the actors request power.
*/
if (!total_req_power)
total_req_power = 1;
capped_extra_power = 0;
extra_power = 0;
for (i = 0; i < num_actors; i++) {
u64 req_range = (u64)req_power[i] * power_range;
// granted_power(cooling device被分配的功耗),
// total_req_power值為total_weighted_req_power
// req_power值為weighted_req_power
// power_range:power_range是當(dāng)前溫度下可配置的最大功耗值
//公式:四舍五入power_range * (weighted_req_power[i] / total_weighted_req_power)
granted_power[i] = DIV_ROUND_CLOSEST_ULL(req_range,
total_req_power);
// device granted_power不能大于max_power
if (granted_power[i] > max_power[i]) {
// 額外需要的功耗,累加分配過多的功耗
extra_power += granted_power[i] - max_power[i];
granted_power[i] = max_power[i];
}
// 計(jì)算分配過多的功耗,再分配的權(quán)重
// 公式:(max_power[i] - granted_power[i])/capped_extra_power
extra_actor_power[i] = max_power[i] - granted_power[i];
capped_extra_power += extra_actor_power[i];
}
if (!extra_power)
return;
/*
* Re-divvy the reclaimed extra among actors based on
* how far they are from the max
*/
// 重新分配額外功耗
// 假設(shè)granted_extra_power
// 公式:granted_extra_power[i] = extra_power * (max_power[i] - granted_power[i])/capped_extra_power
// cooling device總的分配功耗:granted_power[i] += granted_extra_power[i]
// extra_power最大取值為capped_extra_power
extra_power = min(extra_power, capped_extra_power);
if (capped_extra_power > 0)
for (i = 0; i < num_actors; i++)
granted_power[i] += (extra_actor_power[i] *
extra_power) / capped_extra_power;
}
// pid控制算法
static u32 pid_controller(struct thermal_zone_device *tz,
int control_temp,
u32 max_allocatable_power)
{
s64 p, i, d, power_range;
s32 err, max_power_frac;
u32 sustainable_power;
struct power_allocator_params *params = tz->governor_data;
max_power_frac = int_to_frac(max_allocatable_power);
// sustainable_power:保證所有cooling device的正常運(yùn)行的最小功耗值。(state最大)
if (tz->tzp->sustainable_power) {
//如果設(shè)置了,按照設(shè)置的來
sustainable_power = tz->tzp->sustainable_power;
} else {
// 默認(rèn)sustainable_power,所有cooling device在最大state下的最小功耗值進(jìn)行累加
sustainable_power = estimate_sustainable_power(tz);
// 默認(rèn)pid的參數(shù)值,K_pu、K_po、K_pi
estimate_pid_constants(tz, sustainable_power,
params->trip_switch_on, control_temp,
true);
}
// 當(dāng)前溫度和目標(biāo)溫度的差值
err = control_temp - tz->temperature;
err = int_to_frac(err);
/*
* 計(jì)算比例項(xiàng)
* 公式:K_p*err(目標(biāo)溫度和當(dāng)前溫度的差值)
* 當(dāng)前溫度<=目標(biāo)溫度 k_pu = int_to_frac(2*sustainable_power / (control_temp - switch_on_temp))
* 當(dāng)前溫度>目標(biāo)溫度 k_po = int_to_frac(sustainable_power / (control_temp - switch_on_temp))
*/
p = mul_frac(err < 0 ? tz->tzp->k_po : tz->tzp->k_pu, err);
/*
* 計(jì)算積分項(xiàng)
* 公式:K_i*err_integral(差值的累加)
* 默認(rèn):K_i = int_to_frac(10 / 1000)
* if the error is less than cut off allow integration (but
* the integral is limited to max power)
*/
i = mul_frac(tz->tzp->k_i, params->err_integral);
// integral_cutoff默認(rèn)為0
// err < 0,這次的err不進(jìn)行累加
if (err < int_to_frac(tz->tzp->integral_cutoff)) {
s64 i_next = i + mul_frac(tz->tzp->k_i, err);
// (K_i * err_integral)必須小于max_power_frac
if (abs(i_next) < max_power_frac) {
i = i_next;
params->err_integral += err;
}
}
/*
* 計(jì)算微分項(xiàng)
* 公式:K_d*(err - prev_err) / passive_delay
* 默認(rèn):K_d = 0
* We do err - prev_err, so with a positive k_d, a decreasing
* error (i.e. driving closer to the line) results in less
* power being applied, slowing down the controller)
*/
d = mul_frac(tz->tzp->k_d, err - params->prev_err);
d = div_frac(d, tz->passive_delay);
params->prev_err = err;
power_range = p + i + d;
//當(dāng)前溫度下允許的最大功耗值 = sustainable_power + frac_to_int(p + i + d)
power_range = sustainable_power + frac_to_int(power_range);
// power_range 取值在[0,max_allocatable_power]
power_range = clamp(power_range, (s64)0, (s64)max_allocatable_power);
trace_thermal_power_allocator_pid(tz, frac_to_int(err),
frac_to_int(params->err_integral),
frac_to_int(p), frac_to_int(i),
frac_to_int(d), power_range);
return power_range;
}
// 所有cooling device在最大state下的最小功耗值進(jìn)行累加
static u32 estimate_sustainable_power(struct thermal_zone_device *tz)
{
u32 sustainable_power = 0;
struct thermal_instance *instance;
struct power_allocator_params *params = tz->governor_data;
list_for_each_entry(instance, &tz->thermal_instances, tz_node) {
struct thermal_cooling_device *cdev = instance->cdev;
u32 min_power;
if (instance->trip != params->trip_max_desired_temperature)
continue;
// 獲取cdev的最小功耗值
if (power_actor_get_min_power(cdev, &min_power))
continue;
// 累加cooling device的最小功耗值
sustainable_power += min_power;
}
return sustainable_power;
}
// 默認(rèn)pid的參數(shù)值
static void estimate_pid_constants(struct thermal_zone_device *tz,
u32 sustainable_power, int trip_switch_on,
int control_temp, bool force)
{
int ret;
int switch_on_temp;
u32 temperature_threshold;
// 獲取switch_on_temp,觸發(fā)算法開關(guān)
ret = tz->ops->get_trip_temp(tz, trip_switch_on, &switch_on_temp);
if (ret)
switch_on_temp = 0;
// 目標(biāo)溫度和觸發(fā)溫度的差值
temperature_threshold = control_temp - switch_on_temp;
/*
* estimate_pid_constants() tries to find appropriate default
* values for thermal zones that don't provide them. If a
* system integrator has configured a thermal zone with two
* passive trip points at the same temperature, that person
* hasn't put any effort to set up the thermal zone properly
* so just give up.
*/
if (!temperature_threshold)
return;
// Kp的取值分階段k_pu和k_po,int_to_frac只是為了避免小數(shù)的影響,先左移動(dòng),后在mul_frac中右移
// k_po = int_to_frac(sustainable_power / (control_temp - switch_on_temp))
if (!tz->tzp->k_po || force)
tz->tzp->k_po = int_to_frac(sustainable_power) /
temperature_threshold;
// k_pu = int_to_frac(2*sustainable_power / (control_temp - switch_on_temp))
if (!tz->tzp->k_pu || force)
tz->tzp->k_pu = int_to_frac(2 * sustainable_power) /
temperature_threshold;
// k_i = int_to_frac(10 / 1000)
if (!tz->tzp->k_i || force)
tz->tzp->k_i = int_to_frac(10) / 1000;
/*
* The default for k_d and integral_cutoff is 0, so we can
* leave them as they are.
*/
// 默認(rèn)k_d = 0 , integral_cutoff = 0
}
power_actor_get_max_power,獲取cooling device最大功耗值
int power_actor_get_max_power(struct thermal_cooling_device *cdev,
u32 *max_power)
{
if (!cdev_is_power_actor(cdev))
return -EINVAL;
// 將cooling device的state轉(zhuǎn)換為power,當(dāng)power = max_power,state為0
return cdev->ops->state2power(cdev, 0, max_power);
}
例如,cooling device是cpu,冷卻措施是調(diào)節(jié)cpu frequency,cpufreq_cooling.c
// 將 cpu cdev state轉(zhuǎn)換為功耗
static int cpufreq_state2power(struct thermal_cooling_device *cdev,
unsigned long state, u32 *power)
{
unsigned int freq, num_cpus, idx;
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
/* Request state should be less than max_level */
if (state > cpufreq_cdev->max_level)
return -EINVAL;
//獲取同一個(gè)簇中的cpu數(shù)量
num_cpus = cpumask_weight(cpufreq_cdev->policy->cpus);
idx = cpufreq_cdev->max_level - state;
// 獲取相應(yīng)的state對應(yīng)的CPU頻率
freq = cpufreq_cdev->em->table[idx].frequency;
// 獲取同一簇的cpu頻率對應(yīng)的功耗值,查表
*power = cpu_freq_to_power(cpufreq_cdev, freq) * num_cpus;
return 0;
}
// 獲取CPU freq的requested_power(當(dāng)前cpu load需要的功耗值)
static int cpufreq_get_requested_power(struct thermal_cooling_device *cdev,
u32 *power)
{
unsigned long freq;
int i = 0, cpu;
u32 total_load = 0;
struct cpufreq_cooling_device *cpufreq_cdev = cdev->devdata;
struct cpufreq_policy *policy = cpufreq_cdev->policy;
u32 *load_cpu = NULL;
// 獲取當(dāng)前的CPU頻率
freq = cpufreq_quick_get(policy->cpu);
if (trace_thermal_power_cpu_get_power_enabled()) {
u32 ncpus = cpumask_weight(policy->related_cpus);
load_cpu = kcalloc(ncpus, sizeof(*load_cpu), GFP_KERNEL);
}
// 遍歷獲取cpu的負(fù)載
for_each_cpu(cpu, policy->related_cpus) {
u32 load;
if (cpu_online(cpu))
load = get_load(cpufreq_cdev, cpu, i);
else
load = 0;
total_load += load;
if (load_cpu)
load_cpu[i] = load;
i++;
}
//cpu總負(fù)載
cpufreq_cdev->last_load = total_load;
// 獲取cpu動(dòng)態(tài)功耗值
// 根據(jù)查找表,cpu當(dāng)前頻率對應(yīng)的功耗值
// 然后raw_cpu_power * (total_load / 100)
*power = get_dynamic_power(cpufreq_cdev, freq);
if (load_cpu) {
trace_thermal_power_cpu_get_power(policy->related_cpus, freq,
load_cpu, i, *power);
kfree(load_cpu);
}
return 0;
}
2.3.3 bang_bang governor
當(dāng)throttle發(fā)生,打開風(fēng)扇
當(dāng)throttle解除,關(guān)閉風(fēng)扇。
2.3.4 user_space governor
user_space governor 是通過 uevent 將溫區(qū)當(dāng)前溫度,溫控觸發(fā)點(diǎn)等信息上報(bào)到用戶空間,由用戶空間軟件制定溫控的策略。
2.4 綁定sensor
以bcl_soc為例,這里是創(chuàng)建一個(gè)platform_driver,platform_driver必須實(shí)現(xiàn)probe和remove函數(shù),bcl_soc是不需要通過polling(輪詢)的方式去檢查是否觸發(fā),polling-delay是輪詢的周期。它是通過監(jiān)聽系統(tǒng)電量的變化,去回調(diào)battery_supply_callback函數(shù),去喚醒隊(duì)列中的bcl_evaluate_soc函數(shù),通過bcl_evaluate_soc函數(shù)進(jìn)行獲取當(dāng)前的溫度和處理符合觸發(fā)條件的trips。
bcl_soc:bcl-soc { compatible = "qcom,msm-bcl-soc"; #thermal-sensor-cells = <0>; };
bcl_soc.c
#define pr_fmt(fmt) "%s:%s " fmt, KBUILD_MODNAME, __func__ #include#include #include #include #include #include #include #include #include #include #include "../thermal_core.h" #define BCL_DRIVER_NAME "bcl_soc_peripheral" struct bcl_device { struct notifier_block psy_nb; struct work_struct soc_eval_work; long int trip_temp; int trip_val; struct mutex state_trans_lock; bool irq_enabled; struct thermal_zone_device *tz_dev; struct thermal_zone_of_device_ops ops; }; static struct bcl_device *bcl_perph; // 綁定trip_temp接口,設(shè)置觸發(fā)值trip_temp static int bcl_set_soc(void *data, int low, int high) { if (low == bcl_perph->trip_temp) return 0; mutex_lock(&bcl_perph->state_trans_lock); pr_debug("low soc threshold:%d ", low); // 設(shè)置trip_temp bcl_perph->trip_temp = low; if (low == INT_MIN) { bcl_perph->irq_enabled = false; goto unlock_and_exit; } bcl_perph->irq_enabled = true; schedule_work(&bcl_perph->soc_eval_work); unlock_and_exit: mutex_unlock(&bcl_perph->state_trans_lock); return 0; } // 綁定get_temp接口,獲取電量值 static int bcl_read_soc(void *data, int *val) { static struct power_supply*batt_psy; union power_supply_propval ret = {0,}; int err = 0; *val = 100; if (!batt_psy) batt_psy = power_supply_get_by_name("battery"); if (batt_psy) { // 獲取電量 err = power_supply_get_property(batt_psy, POWER_SUPPLY_PROP_CAPACITY, &ret); if (err) { pr_err("battery percentage read error:%d ", err); return err; } *val = ret.intval; } pr_debug("soc:%d ", *val); return err; } // 獲取當(dāng)前溫度和處理thermal zone trip static void bcl_evaluate_soc(struct work_struct *work) { int battery_percentage; // 獲取電量 if (bcl_read_soc(NULL, &battery_percentage)) return; mutex_lock(&bcl_perph->state_trans_lock); if (!bcl_perph->irq_enabled) goto eval_exit; if (battery_percentage > bcl_perph->trip_temp) goto eval_exit; // 當(dāng)前電量值 bcl_perph->trip_val = battery_percentage; mutex_unlock(&bcl_perph->state_trans_lock); // 處理thermal zone trip,調(diào)用的是thermal core中的handle_thermal_trip of_thermal_handle_trip(bcl_perph->tz_dev); return; eval_exit: mutex_unlock(&bcl_perph->state_trans_lock); } // 電量變化回調(diào)battery_supply_callback函數(shù),去喚醒隊(duì)列中的bcl_evaluate_soc函數(shù) static int battery_supply_callback(struct notifier_block *nb, unsigned long event, void *data) { struct power_supply *psy = data; if (strcmp(psy->desc->name, "battery")) return NOTIFY_OK; schedule_work(&bcl_perph->soc_eval_work); return NOTIFY_OK; } static int bcl_soc_remove(struct platform_device *pdev) { power_supply_unreg_notifier(&bcl_perph->psy_nb); flush_work(&bcl_perph->soc_eval_work); if (bcl_perph->tz_dev) thermal_zone_of_sensor_unregister(&pdev->dev, bcl_perph->tz_dev); return 0; } static int bcl_soc_probe(struct platform_device *pdev) { int ret = 0; //申請內(nèi)存空間, 當(dāng)設(shè)備被拆卸或者驅(qū)動(dòng)程序卸載時(shí),內(nèi)存會被自動(dòng)釋放 bcl_perph = devm_kzalloc(&pdev->dev, sizeof(*bcl_perph), GFP_KERNEL); if (!bcl_perph) return -ENOMEM; mutex_init(&bcl_perph->state_trans_lock); // 指向get_temp、set_trips函數(shù) bcl_perph->ops.get_temp = bcl_read_soc; bcl_perph->ops.set_trips = bcl_set_soc; // 定義初始化工作隊(duì)列 INIT_WORK(&bcl_perph->soc_eval_work, bcl_evaluate_soc); // 回調(diào)函數(shù) bcl_perph->psy_nb.notifier_call = battery_supply_callback; //注冊監(jiān)聽接口,系統(tǒng)任何PSY設(shè)備的狀態(tài)發(fā)生改變,并調(diào)用了power_supply_changed接口,power supply core就通知notifier的監(jiān)聽者。 ret = power_supply_reg_notifier(&bcl_perph->psy_nb); if (ret < 0) { pr_err("soc notifier registration error. defer. err:%d ", ret); ret = -EPROBE_DEFER; goto bcl_soc_probe_exit; } // 向thermal zone注冊sensor bcl_perph->tz_dev = thermal_zone_of_sensor_register(&pdev->dev, 0, bcl_perph, &bcl_perph->ops); if (IS_ERR(bcl_perph->tz_dev)) { pr_err("soc TZ register failed. err:%ld ", PTR_ERR(bcl_perph->tz_dev)); ret = PTR_ERR(bcl_perph->tz_dev); bcl_perph->tz_dev = NULL; goto bcl_soc_probe_exit; } thermal_zone_device_update(bcl_perph->tz_dev, THERMAL_DEVICE_UP); // 將soc_eval_work添加到默認(rèn)的工作隊(duì)列 schedule_work(&bcl_perph->soc_eval_work); // 設(shè)置driver data的結(jié)構(gòu)體是bcl_perph dev_set_drvdata(&pdev->dev, bcl_perph); return 0; bcl_soc_probe_exit: bcl_soc_remove(pdev); return ret; } //在dtsi中匹配.compatible = "qcom,msm-bcl-soc"的sensor,可以多個(gè) static const struct of_device_idbcl_match[] = { { .compatible = "qcom,msm-bcl-soc", }, {}, }; static struct platform_driver bcl_driver= { .probe = bcl_soc_probe, .remove = bcl_soc_remove, .driver = { .name = BCL_DRIVER_NAME, .owner = THIS_MODULE, .of_match_table = bcl_match, }, }; builtin_platform_driver(bcl_driver); 提供給sensor driver去調(diào)用的API接口 // 向thermal zone注冊sensor,通過data傳入sensor_data struct thermal_zone_device * thermal_zone_of_sensor_register(struct device *dev, int sensor_id, void*data, const struct thermal_zone_of_device_ops *ops) { struct device_node *np, *child, *sensor_np; struct thermal_zone_device *tzd = ERR_PTR(-ENODEV); np = of_find_node_by_name(NULL, "thermal-zones"); if (!np) return ERR_PTR(-ENODEV); if (!dev || !dev->of_node) { of_node_put(np); return ERR_PTR(-ENODEV); } sensor_np = of_node_get(dev->of_node); for_each_available_child_of_node(np, child) { int ret, id; // //解析dtsi中thermal-sensors節(jié)點(diǎn) ret = thermal_zone_of_get_sensor_id(child, sensor_np, &id); if (ret) continue; if (id == sensor_id) { // 在thermal zone中綁定sensor tzd = thermal_zone_of_add_sensor(child, sensor_np, data, ops); if (!IS_ERR(tzd)) thermal_zone_device_enable(tzd); of_node_put(child); goto exit; } } exit: of_node_put(sensor_np); of_node_put(np); return tzd; } /***sensor API ***/ // 在thermal zone中綁定sensor static struct thermal_zone_device * thermal_zone_of_add_sensor(struct device_node *zone, struct device_node *sensor, void *data, const struct thermal_zone_of_device_ops *ops) { struct thermal_zone_device *tzd; struct __thermal_zone *tz; // 獲取當(dāng)前的thermal zone tzd = thermal_zone_get_zone_by_name(zone->name); if (IS_ERR(tzd)) return ERR_PTR(-EPROBE_DEFER); tz = tzd->devdata; if (!ops) return ERR_PTR(-EINVAL); mutex_lock(&tzd->lock); // 綁定ops tz->ops = ops; // 綁定sensor_data tz->sensor_data = data; // 綁定sensor中實(shí)現(xiàn)的get_temp、get_trend tzd->ops->get_temp = of_thermal_get_temp; tzd->ops->get_trend = of_thermal_get_trend; /* * The thermal zone core will calculate the window if they have set the * optional set_trips pointer. */ if (ops->set_trips) tzd->ops->set_trips = of_thermal_set_trips; if (ops->set_emul_temp) tzd->ops->set_emul_temp = of_thermal_set_emul_temp; mutex_unlock(&tzd->lock); return tzd; }
審核編輯:湯梓紅
-
傳感器
+關(guān)注
關(guān)注
2548文章
50740瀏覽量
752148 -
框架
+關(guān)注
關(guān)注
0文章
399瀏覽量
17437 -
程序
+關(guān)注
關(guān)注
116文章
3778瀏覽量
80860 -
源碼
+關(guān)注
關(guān)注
8文章
633瀏覽量
29147 -
Thermal
+關(guān)注
關(guān)注
0文章
8瀏覽量
7367
原文標(biāo)題:萬字長文 | Thermal框架源碼剖析
文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論