Linux电源管理(五),发热管理(thermal),温度控制
更多linux系统电源管理相关的内容请看:Linux电源管理、功耗管理 和 发热管理 (CPUFreq、CPUIdle、RPM、thermal、睡眠 和 唤醒)-CSDN博客
本文主要基于linux-5.4.18版本的内核代码进行分析。
1 简介
1.1 硬件知识
CPU等芯片在工作时会产生大量热量,导致硬件温度升高,而每个芯片都有自己的工作温度区间(例如FT-2000四核处理器的工作温度如下图所示)。当芯片的温度不在这个区间内时,芯片将无法正常工作。
《FT-2000/4 系列处理器数据手册》1 简介
为了控制硬件温度,硬件上有两类降温设备(Cooling device):
- 主动降温(Active Cooling)设备,一般是风扇;
- 被动降温(Passive Cooling)设备,一般是发热的芯片本身,通过降低频率或者电压来减少热量的产生。
1.2 温度控制的方案
《Advanced Configuration and Power Interface (ACPI) Specification》3.10 Thermal Management Concepts
如上图所示,很多硬件设计中,CPU的温度传感器和风扇是直接受EC (Embedded Controller)控制的,所以出现了两种温度控制的方案:
- 第一种、完全由EC来控制硬件温度,linux系统不干预;
- 第二种、Linux系统通过EC来控制硬件温度
本文主要分析第二种方案。
2 thermal_zone_device
2.1 简介
可以获取温度的设备抽象为thermal_zone_device, 如Temperature Sensor等。
《SoC底层软件低功耗系统设计与实现》 9.1.2 模块功能详解
2.2 数据结构
2.2.1 struct thermal_zone_device
//include/linux/thermal.h
struct thermal_zone_device {int temperature; //current temperature. int last_temperature; //previous temperature readint prev_low_trip; //上⼀次温控的低温触发点int prev_high_trip; //上⼀次温控的⾼温触发点struct thermal_zone_device_ops *ops;struct thermal_zone_params *tzp;struct thermal_governor *governor;struct delayed_work poll_queue;......
};
register和unregister
struct thermal_zone_device *thermal_zone_device_register(const char *, int, int,void *, struct thermal_zone_device_ops *,struct thermal_zone_params *, int, int);void thermal_zone_device_unregister(struct thermal_zone_device *);
2.2.2 struct thermal_zone_device_ops
//include/linux/thermal.h
struct thermal_zone_device_ops {int (*bind) (struct thermal_zone_device *,struct thermal_cooling_device *);int (*unbind) (struct thermal_zone_device *,struct thermal_cooling_device *);int (*get_temp) (struct thermal_zone_device *, int *);int (*set_trips) (struct thermal_zone_device *, int, int);int (*get_mode) (struct thermal_zone_device *,enum thermal_device_mode *);int (*set_mode) (struct thermal_zone_device *,enum thermal_device_mode);int (*get_trip_type) (struct thermal_zone_device *, int,enum thermal_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,enum thermal_trip_type);
};
2.3 温度状态 和 温度阈值(trip point)
2.3.1 简介
软件上有四个温度阈值,分别是active、passive、hot和critical。
当实际温度超过active温度阈值时,应该执行主动降温操作;
当实际温度超过pssive温度阈值时,应该执行被动降温操作;
当实际温度超过hot温度阈值时,在支持ACPI的设备上应该执行S4(挂起到硬盘)操作;
当实际温度超过critical温度阈值时,应该执行关机操作。
当实际温度超过某个阈值后,具体执行什么操作请看下面的 “4 绑定zone_device的trip points和cooling_device” 小节
2.3.2 数据结构
//include/linux/thermal.h
/*** struct thermal_trip - representation of a point in temperature domain* @np: pointer to struct device_node that this trip point was created from* @temperature: temperature value in miliCelsius* @hysteresis: relative hysteresis in miliCelsius* @type: trip point type*/struct thermal_trip {struct device_node *np;int temperature;int hysteresis;enum thermal_trip_type type;
};
//include/linux/thermal.h
enum thermal_trip_type {THERMAL_TRIP_ACTIVE = 0,THERMAL_TRIP_PASSIVE,THERMAL_TRIP_HOT,THERMAL_TRIP_CRITICAL,
};
2.3.3 通过设备树指定trip points
trips {cpu_alert0: cpu-alert0 {temperature = <90000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "active";}; cpu_alert1: cpu-alert1 {temperature = <100000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "passive";}; cpu_crit: cpu-crit {temperature = <125000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "critical";}; };
上面的设备树信息说明
The trip node is a node to describe a point in the temperature domain in which the system takes an action. This node describes just the point, not the action.
Required properties:
- temperature: An integer indicating the trip temperature level,
Type: signed in millicelsius.
Size: one cell
- hysteresis: A low hysteresis value on temperature property (above).
Type: unsigned This is a relative value, in millicelsius.
Size: one cell
- type: a string containing the trip type. Expected values are:
"active": A trip point to enable active cooling
"passive": A trip point to enable passive cooling
"hot": A trip point to notify emergency
"critical": Hardware not reliable.
Documentation/devicetree/bindings/thermal/thermal.txt
2.3.4 通过ACPI Source Language (ASL)来指定trip points
Thermal Objects
<1> _ACx (Active Cooling)
This optional object, if present under a thermal zone, returns the temperature trip point at which OSPM must start or
stop Active cooling, where x is a value between 0 and 9 that designates multiple active cooling levels of the thermal
zone.
<2> _PSV (Passive)
This optional object, if present under a thermal zone, evaluates to the temperature at which OSPM must activate
passive cooling policy.
<3> _CRT (Critical Temperature)
This object, when defined under a thermal zone, returns the critical temperature at which OSPM must shutdown the
system.
<4> _HOT (Hot Temperature)
This optional object, when defined under a thermal zone, returns the critical temperature at which OSPM may choose
to transition the system into the S4 sleeping state.
《Advanced Configuration and Power Interface (ACPI) Specification》11.4 Thermal Objects
示例
ThermalZone (TZ0) {Method(_PSV) { Return(_C2K(70)) } /* passive cooling temp */Method(_HOT) { Return(_C2K(85)) } /* hot temp */Method(_CRT) { Return(_C2K(95)) } /* critical temp */......
} /* ThermalZone(TZ0) */
2.3.5 查看trip points信息
/sys/class/thermal/thermal_zoneX/trip_point_0_type
/sys/class/thermal/thermal_zoneX/trip_point_0_temp
/sys/class/thermal/thermal_zoneX/trip_point_0_hyst
示例
# cat /sys/class/thermal/thermal_zone4/trip_point_0_type
critical
# cat /sys/class/thermal/thermal_zone4/trip_point_0_temp
110050
# cat /sys/class/thermal/thermal_zone4/trip_point_0_hyst
0
2.4 查看或者设置 thermal_zone_device信息
/sys/class/thermal/thermal_zoneX/
3 cooling_device
3.1 简介
可以控制温度的设备抽象为thermal_cooling_device, 如⻛扇、CPU、DDR、GPU等。
《SoC底层软件低功耗系统设计与实现》 9.1.2 模块功能详解
主动(active)降温设备 和 被动(passive)降温设备
Cooling devices are nodes providing control on power dissipation. There are essentially two ways to provide control on power dissipation. First is by means of regulating device performance, which is known as passive cooling. A typical passive cooling is a CPU that has dynamic voltage and frequency scaling (DVFS), and uses lower frequencies as cooling states. Second is by means of activating devices in order to remove the dissipated heat, which is known as active cooling, e.g. regulating fan speeds. In both cases, cooling devices shall have a way to determine the state of cooling in which the device is.
Documentation/devicetree/bindings/thermal/thermal.txt
Active cooling devices typically consume power and produce some amount of noise when enabled. These devices
attempt to cool a thermal zone through the removal of heat rather than limiting the performance of a device to address
an adverse thermal condition.
《Advanced Configuration and Power Interface (ACPI) Specification》11.1.4 Active Cooling
Passive cooling controls are able to cool a thermal zone without creating noise and without consuming additional
power (actually saving power), but do so by decreasing the performance of the devices in the zone .
《Advanced Configuration and Power Interface (ACPI) Specification》11.1.5 Passive Cooling
3.2 数据结构
3.2.1 struct thermal_cooling_device;
//include/linux/thermal.h
struct thermal_cooling_device {int id; char type[THERMAL_NAME_LENGTH];struct device device;struct device_node *np;void *devdata;void *stats;const struct thermal_cooling_device_ops *ops;bool updated; /* true if the cooling device does not need update */struct mutex lock; /* protect thermal_instances list */struct list_head thermal_instances;struct list_head node;
};
3.2.2 struct thermal_cooling_device_ops;
//include/linux/thermal.h
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 *,struct thermal_zone_device *, u32 *);int (*state2power)(struct thermal_cooling_device *,struct thermal_zone_device *, unsigned long, u32 *);int (*power2state)(struct thermal_cooling_device *,struct thermal_zone_device *, u32, unsigned long *);
};
3.3 states
3.3.1 简介
冷却设备(cooling device)维护一个冷却(cooling)等级,即状态(state),一般状态越高,系统的冷却需求越高,所采取的冷却措施也可能更激进。冷却设备根据不同等级的冷却需求进行冷却行为。冷却设备只根据state进行冷却操作,是实施者,而state的计算由thermal governor完成。
《SoC底层软件低功耗系统设计与实现》 9.1.2 模块功能详解
Any cooling device has a range of cooling states (i.e. different levels of heat dissipation). For example a fan's cooling states correspond to the different fan speeds possible. Cooling states are referred to by single unsigned integers, where larger numbers mean greater heat dissipation. The precise set of cooling states associated with a device should be defined in a particular device's binding.
Documentation/devicetree/bindings/thermal/thermal.txt
3.3.2 设置cooling device的states
3.3.2.1 通过设备树指定PWM风扇的states (cooling-levels)
//arch/arm/boot/dts/exynos5410-odroidxu.dtsfan0: pwm-fan {compatible = "pwm-fan";pwms = <&pwm 0 20972 0>;#cooling-cells = <2>;cooling-levels = <0 130 170 230>;};
cooling-levels : PWM duty cycle values in a range from 0 to 255 which correspond to thermal cooling states
Documentation/devicetree/bindings/hwmon/pwm-fan.txt
3.3.2.2 通过设备树指定CPU的states (operating-points)
//Documentation/devicetree/bindings/thermal/thermal.txt
cpus {/* * Here is an example of describing a cooling device for a DVFS* capable CPU. The CPU node describes its four OPPs.* The cooling states possible are 0..3, and they are* used as OPP indexes. The minimum cooling state is 0, which means* all four OPPs can be available to the system. The maximum* cooling state is 3, which means only the lowest OPPs (198MHz@0.85V)* can be available in the system.*/cpu0: cpu@0 {...operating-points = < /* kHz uV */ 970000 1200000 792000 1100000 396000 950000198000 850000>;#cooling-cells = <2>; /* min followed by max */};...
};
3.3.3.3 通过ACPI Source Language (ASL)指定风扇的states (转速)
根据下面的 “7.2.2 控制风扇的转速(fan_set_cur_state)” 章节信息,使用ACPI 1.0规范时,风扇对应的cooling device只有2种states,即打开(D0) 和 关闭(D3);使用ACPI 4.0规范时,可以通过“_FPS (Fan Performance States)”来指定多个states。
3.3.3.4 通过ACPI Source Language (ASL)指定CPU的states
_TSS (Throttling Supported States)
This optional object indicates to OSPM the number of supported processor throttling states that a platform supports.
《Advanced Configuration and Power Interface (ACPI) Specification》
8.4.5.2 _TSS (Throttling Supported States)
ASL示例
Scope(\_SB) {Device(CPU0) {Name(_HID, "ACPI0007")Name(_UID, 0)......// Throttling Supported States// The values shown are for exemplary purposes onlyName(_TSS, Package() {// Read: freq percentage, power, latency, control, statusPackage() {0x64, 1000, 0x0, 0x7, 0x0}, // Throttle off (100%)Package() {0x58, 800, 0x0, 0xF, 0x0}, // 87.5%Package() {0x4B, 600, 0x0, 0xE, 0x0}, // 75%Package() {0x3F, 400, 0x0, 0xD, 0x0} // 62.5%})......}......
}
《Advanced Configuration and Power Interface (ACPI) Specification》
11.7.3 Example: Thermal Zone with Multiple Devices
对应的驱动初始化流程请看“7.3.2” 小节
3.3.3 查看cooling device的states信息
/sys/class/thermal/cooling_device0/cur_state
/sys/class/thermal/cooling_device0/max_state
3.4 查看cooling_device信息
/sys/class/thermal/cooling_deviceX
4 绑定zone_device的trip points和cooling_device
4.1 简介
thermal_zone_device_register()函数在注册thermal_zone_device时,会调用bind_tz()函数,bind_tz()会遍历已经注册的cooling device,然后执行cooling device和当前注册的thermal_zone_device绑定工作。
thermal_cooling_device_register()函数在注册thermal_cooling_device时,会调用bind_cdev()函数,bind_tz()会遍历已经注册的zone device,然后执行zone device和当前注册的thermal_cooling_device绑定工作。
bind_tz() 和 bind_cdev()最终都会执行struct thermal_zone_device_ops中的bind()函数 和 struct thermal_bind_params中的match()函数来完成具体工作,所以需要实现bind() 或者 match()函数才能完成绑定工作。
最终的绑定工作通过thermal_zone_bind_cooling_device()函数实现,绑定关系通过struct thermal_instance记录。
4.2 数据结构
/** This structure is used to describe the behavior of* a certain cooling device on a certain trip point* in a certain thermal zone*/
struct thermal_instance {int id;char name[THERMAL_NAME_LENGTH];struct thermal_zone_device *tz;struct thermal_cooling_device *cdev;int trip;bool initialized;unsigned long upper; /* Highest cooling state for this trip point */unsigned long lower; /* Lowest cooling state for this trip point */unsigned long target; /* expected cooling state */char attr_name[THERMAL_NAME_LENGTH];struct device_attribute attr;char weight_attr_name[THERMAL_NAME_LENGTH];struct device_attribute weight_attr;struct list_head tz_node; /* node in tz->thermal_instances */struct list_head cdev_node; /* node in cdev->thermal_instances */unsigned int weight; /* The weight of the cooling device */
};
4.3 绑定函数
This interface function bind a thermal cooling device to the certain trip point of a thermal zone device.
This function is usually called in the thermal zone device .bind callback.
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz, int trip,struct thermal_cooling_device *cdev,unsigned long upper, unsigned long lower,unsigned int weight);
4.4 指定thermal zone device的每个trip point和 cooling device之间的绑定关系
4.4.1 设备树
trips {cpu_alert0: cpu-alert0 { //trip pointtemperature = <90000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "active";};......cooling-maps {map0 {trip = <&cpu_alert0>;cooling-device = <&fan0 THERMAL_NO_LIMIT 4>; };......
4.4.2 ACPI
_ACx (Active Cooling)
_ALx (Active List)
_PSV (Passive)
_PSL (Passive List)
《Advanced Configuration and Power Interface (ACPI) Specification》
11.4 Thermal Objects
4.5 查看绑定关系
# ls /sys/class/thermal/thermal_zone7/cdev* -l
lrwxrwxrwx 1 root root 0 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev0 -> ../cooling_device9
-r--r--r-- 1 root root 4096 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev0_trip_point
-rw-r--r-- 1 root root 4096 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev0_weight
lrwxrwxrwx 1 root root 0 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev1 -> ../cooling_device10
-r--r--r-- 1 root root 4096 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev1_trip_point
-rw-r--r-- 1 root root 4096 5月 11 18:51 /sys/class/thermal/thermal_zone7/cdev1_weight
# cat /sys/class/thermal/thermal_zone7/cdev0_trip_point
2
# cat /sys/class/thermal/thermal_zone7/cdev1_trip_point
1
# cat /sys/class/thermal/thermal_zone7/trip_point_2_temp
80000
# cat /sys/class/thermal/thermal_zone7/trip_point_1_temp
60000
上面的信息显示thermal_zone7的1号trip point (60摄氏度)和cooling_device10绑定在一起。2号trip point (80摄氏度)和cooling_device9绑定在一起
5 governors
5.1 简介
温控策略抽象为thermal_governor, ⽐如step_wise、bang_bang等。
《SoC底层软件低功耗系统设计与实现》 9.1.2 模块功能详解
5.2 现有的governors
5.2.1 step_wise
/** If the temperature is higher than a trip point,* a. if the trend is THERMAL_TREND_RAISING, use higher cooling* state for this trip point* b. if the trend is THERMAL_TREND_DROPPING, do nothing* c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit* for this trip point* d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit* for this trip point* If the temperature is lower than a trip point,* a. if the trend is THERMAL_TREND_RAISING, do nothing* b. if the trend is THERMAL_TREND_DROPPING, use lower cooling* state for this trip point, if the cooling state already* equals lower limit, deactivate the thermal instance* c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing* d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,* if the cooling state already equals lower limit,* deactivate the thermal instance*/
drivers/thermal/step_wise.c
5.2.2 power_allocator
5.2.3 bang_bang
* Regulation Logic: a two point regulation, deliver cooling state depending* on the previous state shown in this diagram:** Fan: OFF ON** |* |* trip_temp: +---->+* | | ^* | | |* | | Temperature* (trip_temp - hyst): +<----+* |* |* |** * If the fan is not running and temperature exceeds trip_temp, the fan* gets turned on.* * In case the fan is running, temperature must fall below* (trip_temp - hyst) so that the fan gets turned off again.*
drivers/thermal/gov_bang_bang.c
5.2.4 user_space
Notifies user space about thermal events
drivers/thermal/user_space.c
5.2.5 fair_share
* Throttling Logic: This uses three parameters to calculate the new* throttle state of the cooling devices associated with the given zone.** Parameters used for Throttling:* P1. max_state: Maximum throttle state exposed by the cooling device.* P2. percentage[i]/100:* How 'effective' the 'i'th device is, in cooling the given zone.* P3. cur_trip_level/max_no_of_trips:* This describes the extent to which the devices should be throttled.* We do not want to throttle too much when we trip a lower temperature,* whereas the throttling is at full swing if we trip critical levels.* (Heavily assumes the trip points are in ascending order)* new_state of cooling device = P3 * P2 * P1
drivers/thermal/fair_share.c
5.3 查看和设置governor
/sys/class/thermal/thermal_zoneX/available_policies
/sys/class/thermal/thermal_zoneX/policy
6 核心代码流程
6.1 简介
内核代码中会循环读取温度数据,如果温度超过某个trip point温度,就会启动和这个trip point绑定的cooling device开始降温工作。
6.2 代码流程
轮训工作通过工作项实现
thermal_zone_device_register();-> INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check);
工作项处理函数thermal_zone_device_check()的流程如下:
《SoC底层软件低功耗系统设计与实现》9.1.6 关于critical事件和非critical事件的处理流程
monitor_thermal_zone()函数会重新添加工作项,以此来达到循环执行的效果 (循环的间隔时间通过struct thermal_zone_device中的passive_delay变量和polling_delay变量控制)。
monitor_thermal_zone();-> thermal_zone_device_set_polling();-> mod_delayed_work(system_freezable_power_efficient_wq, &tz->poll_queue, <时间>);
tz->governor->throttle()会执行governors的控温逻辑,选择cooling device的states,然后调用cdev->ops->set_cur_state();
以step_wise(governor)为例
step_wise_throttle();-> thermal_zone_trip_update(tz, trip);-> trend = get_tz_trend(tz, trip);-> instance->target = get_target_state(instance, trend, throttle);-> thermal_cdev_update();-> cdev->ops->set_cur_state();
关键函数get_target_state()的注释如下:
//drivers/thermal/step_wise.c
/** If the temperature is higher than a trip point,* a. if the trend is THERMAL_TREND_RAISING, use higher cooling* state for this trip point* b. if the trend is THERMAL_TREND_DROPPING, do nothing* c. if the trend is THERMAL_TREND_RAISE_FULL, use upper limit* for this trip point* d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit* for this trip point* If the temperature is lower than a trip point,* a. if the trend is THERMAL_TREND_RAISING, do nothing* b. if the trend is THERMAL_TREND_DROPPING, use lower cooling* state for this trip point, if the cooling state already* equals lower limit, deactivate the thermal instance* c. if the trend is THERMAL_TREND_RAISE_FULL, do nothing* d. if the trend is THERMAL_TREND_DROP_FULL, use lower limit,* if the cooling state already equals lower limit,* deactivate the thermal instance*/
static unsigned long get_target_state(struct thermal_instance *instance,enum thermal_trend trend, bool throttle)
{......
}
7 基于ACPI的温控驱动分析
7.1 thermal_zone_device
7.1.1 数据结构
7.1.1.1 struct acpi_driver acpi_thermal_driver;
//include/acpi/acpi_drivers.h
#define ACPI_THERMAL_HID "LNXTHERM"//drivers/acpi/thermal.c
static const struct acpi_device_id thermal_device_ids[] = {{ACPI_THERMAL_HID, 0},{"", 0},
};
MODULE_DEVICE_TABLE(acpi, thermal_device_ids);static struct acpi_driver acpi_thermal_driver = {.name = "thermal",.class = ACPI_THERMAL_CLASS,.ids = thermal_device_ids,.ops = {.add = acpi_thermal_add,.remove = acpi_thermal_remove,.notify = acpi_thermal_notify,}, .drv.pm = &acpi_thermal_pm,
};
7.1.1.2 struct thermal_zone_device_ops acpi_thermal_zone_ops;
//drivers/acpi/thermal.c
static struct thermal_zone_device_ops acpi_thermal_zone_ops = {.bind = acpi_thermal_bind_cooling_device,.unbind = acpi_thermal_unbind_cooling_device,.get_temp = thermal_get_temp,.get_mode = thermal_get_mode,.set_mode = thermal_set_mode,.get_trip_type = thermal_get_trip_type,.get_trip_temp = thermal_get_trip_temp,.get_crit_temp = thermal_get_crit_temp,.get_trend = thermal_get_trend,.notify = thermal_notify,
};
7.1.2 ACPI中的 Thermal Objects
《Advanced Configuration and Power Interface (ACPI) Specification》11.4 Thermal Objects
7.1.3 驱动初始化大致流程
acpi_thermal_add();-> acpi_thermal_get_info(tz);-> acpi_thermal_get_trip_points(tz); /* Get trip points [_CRT, _PSV, etc.] (required) */-> acpi_thermal_trips_update();-> acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, &tmp);-> tz->trips.critical.temperature = tmp;-> acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, &tmp);-> tz->trips.hot.temperature = tmp;-> acpi_evaluate_integer(tz->device->handle, "_PSV", NULL, &tmp);-> tz->trips.passive.temperature = tmp;-> acpi_thermal_get_temperature(tz); /* Get temperature [_TMP] (required) */-> acpi_evaluate_integer(tz->device->handle, "_TMP", NULL, &tmp);-> tz->temperature = tmp;-> acpi_thermal_set_cooling_mode(); /* Set the cooling mode [_SCP] to active cooling (default) */-> acpi_thermal_register_thermal_zone();-> thermal_zone_device_register("acpitz", trips, 0, tz, &acpi_thermal_zone_ops, ...);-> sysfs_create_link(&tz->device->dev.kobj, &tz->thermal_zone->device.kobj, "thermal_zone");-> sysfs_create_link(&tz->thermal_zone->device.kobj, &tz->device->dev.kobj, "device");
7.1.4 ACPI Source Language (ASL)示例
ThermalZone (TZ0) {Method(_TMP) { Return (\_SB.PCI0.ISA0.EC0.TMP )} // get current tempName(_PSL, Package() {\_SB.CPU0, \\_SB.CPU1}) // passive cooling devicesName(_AL0, Package() {\_SB.PCI0.ISA0.EC0.FN1}) // active coolingMethod(_AC0) { Return (\_SB.PCI0.ISA0.EC0.AC0) } // fan temp (high)Method(_AC1) { Return (\_SB.PCI0.ISA0.EC0.AC1) } // fan temp (low)Method(_PSV) { Return (\_SB.PCI0.ISA0.EC0.PSV) } // passive cooling tempMethod(_HOT) { Return (\_SB.PCI0.ISA0.EC0.HOT) } // get critical S4 tempMethod(_CRT) { Return (\_SB.PCI0.ISA0.EC0.CRT) } // get crit. tempName(_TC1, 4) // bogus example constantName(_TC2, 3) // bogus example constantMethod(_SCP, 1) { Store (Arg0, \\_SB.PCI0.ISA0.EC0.MODE) } // set cooling modeName(_TSP, 150) // passive sampling = 15 sec
} // end of TZ0
《Advanced Configuration and Power Interface (ACPI) Specification》
11.7.3 Example: Thermal Zone with Multiple Devices
7.2 cooling_device: Fan
7.2.1 数据结构
7.2.1.1 struct platform_driver acpi_fan_driver;
//drivers/acpi/fan.c
static const struct acpi_device_id fan_device_ids[] = {{"PNP0C0B", 0},{"INT3404", 0},{"", 0},
};
MODULE_DEVICE_TABLE(acpi, fan_device_ids);static struct platform_driver acpi_fan_driver = {.probe = acpi_fan_probe,.remove = acpi_fan_remove,.driver = {.name = "acpi-fan",.acpi_match_table = fan_device_ids,.pm = FAN_PM_OPS_PTR,},
};
7.2.1.2 struct thermal_cooling_device_ops fan_cooling_ops;
//drivers/acpi/fan.c
static const struct thermal_cooling_device_ops fan_cooling_ops = {.get_max_state = fan_get_max_state,.get_cur_state = fan_get_cur_state,.set_cur_state = fan_set_cur_state,
};
7.2.2 控制风扇的转速(fan_set_cur_state)
7.2.2.1 简介
在ACPI规范中Device Power States有四种
《Advanced Configuration and Power Interface (ACPI) Specification》 A.2 Device Power States
7.2.2.2 控制固定转速的风扇
对于固定转速的风扇,控制起来很方便,进入D0状态就是打开风扇,进入D3状态就是关闭风扇。
fan_set_cur_state();-> fan_set_state();
static int fan_set_state(struct acpi_device *device, unsigned long state)
{if (state != 0 && state != 1)return -EINVAL;return acpi_device_set_power(device,state ? ACPI_STATE_D0 : ACPI_STATE_D3_COLD);
}
7.2.2.3 ACPI 1.0版本下控制可调转速的风扇
ACPI 1.0 defined a simple fan device that is assumed to be in operation when it is in the D0 state. Thermal zones reference fan device(s) as being responsible primarily for cooling within that zone.Notice that multiple fan devices can be present for any one thermal zone. They might be actual different fans, or they might be used to implement one fan of multiple speeds (for example, by turning both “fans” on the one fan will run full speed).
《Advanced Configuration and Power Interface (ACPI) Specification》11.3 Fan Device
示例
// Following is a single fan with two speeds. This is represented
// by creating two logical fan devices. When FN2 is turned on then
// the fan is at a low speed. When FN1 and FN2 are both on then
// the fan is at high speed.
//
// Create FAN device object FN1
Device (FN1) {// Device ID for the FANName(_HID, EISAID("PNP0C0B"))Name(_UID, 0)Name(_PR0, Package(){FN10, FN11})
}
// Create FAN device object FN2
Device (FN2) {// Device ID for the FANName(_HID, EISAID("PNP0C0B"))Name(_UID, 1)Name(_PR0, Package(){FN10})
}
《Advanced Configuration and Power Interface (ACPI) Specification》11.7.2 Example: Multiple-Speed Fans
7.2.2.4 ACPI 4.0版本下控制可调转速的风扇
ACPI 4.0添加了4个Fan Objects来描述风扇信息,其中“_FPS (Fan Performance States)”可以描述一个风扇支持的多个转速信息。
Package {Revision, // Integer - Current revision is: 0FanPState[0], // Package......FanPState[n] // Package
}
Each FanPState sub-Package contains the elements described below:
Package () // Fan P-State
{Control, // Integer DWORDTripPoint, // Integer DWORDSpeed, // Integer DWORDNoiseLevel, // Integer DWORDPower // Integer DWORD
}
《Advanced Configuration and Power Interface (ACPI) Specification》11.3.1 Fan Objects
驱动流程
fan_set_cur_state();-> fan_set_state_acpi4();
7.2.3 驱动初始化大致流程
acpi_fan_probe(); -> acpi_fan_get_fps(); //get _FPS (Fan Performance States)-> acpi_evaluate_object(device->handle, "_FPS", NULL, &buffer);-> fan->fps_count = obj->package.count - 1;-> thermal_cooling_device_register(name, device, &fan_cooling_ops);
7.2.4 ACPI Source Language (ASL)示例
请看上面的“7.2.2.3”小节
7.3 cooling_device: Processor
7.3.1 数据结构
//drivers/acpi/processor_thermal.c
static const struct acpi_device_id processor_device_ids[] = {{ACPI_PROCESSOR_OBJECT_HID, 0}, //ACPI_PROCESSOR_OBJECT_HID "LNXCPU"{ACPI_PROCESSOR_DEVICE_HID, 0}, //ACPI_PROCESSOR_DEVICE_HID "ACPI0007"{"", 0},
};
MODULE_DEVICE_TABLE(acpi, processor_device_ids);static struct device_driver acpi_processor_driver = {.name = "processor",.bus = &cpu_subsys,.acpi_match_table = processor_device_ids,.probe = acpi_processor_start,.remove = acpi_processor_stop,
};
const struct thermal_cooling_device_ops processor_cooling_ops = { .get_max_state = processor_get_max_state,.get_cur_state = processor_get_cur_state,.set_cur_state = processor_set_cur_state,
};
7.3.2 初始化流程
acpi_processor_driver_init(void); //module_init(acpi_processor_driver_init);-> driver_register(&acpi_processor_driver);-> acpi_processor_start();-> __acpi_processor_start();-> acpi_pss_perf_init();-> acpi_processor_get_throttling_info();-> acpi_processor_get_throttling_states();-> acpi_evaluate_object(pr->handle, "_TSS", NULL, &buffer);-> pr->throttling.state_count = tss->package.count;-> thermal_cooling_device_register("Processor", device, &processor_cooling_ops);
7.3.3 ACPI Source Language (ASL)示例
Scope(\_SB) {Device(CPU0) {Name(_HID, "ACPI0007")Name(_UID, 0)//// Load additional objects if 3.0 Thermal model support is available//Method(_INI, 0) {If (\_OSI("3.0 Thermal Model")) {LoadTable("OEM1", "PmRef", "Cpu0", "\\_SB.CPU0") // 3.0 Thermal Model}}// For brevity, most processor objects have been excluded// from this example (such as \_PSS, \_CST, \_PCT, \_PPC, etc.)// Processor Throttle Control objectName(_PTC, ResourceTemplate() {Register(SystemIO, 32, 0, 0x120) // Processor ControlRegister(SystemIO, 32, 0, 0x120) // Processor Status})// Throttling Supported States// The values shown are for exemplary purposes onlyName(_TSS, Package() {// Read: freq percentage, power, latency, control, statusPackage() {0x64, 1000, 0x0, 0x7, 0x0}, // Throttle off (100%)Package() {0x58, 800, 0x0, 0xF, 0x0}, // 87.5%Package() {0x4B, 600, 0x0, 0xE, 0x0}, // 75%Package() {0x3F, 400, 0x0, 0xD, 0x0} // 62.5%})// Throttling Present Capabilities// The values shown are for exemplary purposes onlyMethod(_TPC) {If(\_SB.AC) {Return(0) // All throttle states available} Else {Return(2) // Throttle states >= 2 are available} } } // end of CPU0 scope......
}
《Advanced Configuration and Power Interface (ACPI) Specification》
11.7.3 Example: Thermal Zone with Multiple Devices
8 通过设备树(DTS)指定硬件信息的温控驱动分析
8.1 thermal_zone_device
8.1.1 数据结构
//drivers/thermal/of-thermal.c
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,
};
8.1.2 初始化流程
thermal_init(); //fs_initcall(thermal_init);-> of_parse_thermal_zones();-> of_find_node_by_name(NULL, "thermal-zones");-> thermal_of_build_thermal_zone();-> of_property_read_u32(np, "polling-delay-passive", &prop);-> of_property_read_u32(np, "polling-delay", &prop);-> of_property_read_u32_array(np, "coefficients", coef, 2);-> child = of_get_child_by_name(np, "trips");-> thermal_of_populate_trip();-> of_property_read_u32(np, "temperature", &prop); //获取trip point温度值-> of_property_read_u32(np, "hysteresis", &prop);-> thermal_of_get_trip_type();-> of_property_read_string(np, "type", &t); //获取trip point类型(active、passive、hot和critical)-> child = of_get_child_by_name(np, "cooling-maps");-> thermal_of_populate_bind_params();-> of_property_read_u32(np, "contribution", &prop);-> of_count_phandle_with_args(np, "cooling-device", "#cooling-cells");-> of_parse_phandle_with_args(np, "cooling-device", "#cooling-cells", i, &cooling_spec);-> __tcbp[i].cooling_device = cooling_spec.np;-> __tcbp[i].min = cooling_spec.args[0]; //minimum cooling state-> __tcbp[i].max = cooling_spec.args[1]; //maximum cooling state-> __tbp->tcbp = __tcbp;-> thermal_zone_device_register();
温度传感器的初始化
thermal_zone_of_sensor_register();-> np = of_find_node_by_name(NULL, "thermal-zones");-> of_parse_phandle_with_args(child, "thermal-sensors", "#thermal-sensor-cells", 0, &sensor_specs);-> thermal_zone_of_add_sensor();-> tz->ops = ops;-> tzd->ops->get_temp = of_thermal_get_temp;-> tzd->ops->get_trend = of_thermal_get_trend;
8.1.3 设备树示例
thermal-zones {cpu_thermal: cpu-thermal {polling-delay-passive = <250>; /* milliseconds */polling-delay = <1000>; /* milliseconds */thermal-sensors = <&bandgap0>;trips {cpu_alert0: cpu-alert0 {temperature = <90000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "active";}; cpu_alert1: cpu-alert1 {temperature = <100000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "passive";}; cpu_crit: cpu-crit {temperature = <125000>; /* millicelsius */hysteresis = <2000>; /* millicelsius */type = "critical";}; }; cooling-maps {map0 {trip = <&cpu_alert0>;cooling-device = <&fan0 THERMAL_NO_LIMIT 4>; }; map1 {trip = <&cpu_alert1>;cooling-device = <&fan0 5 THERMAL_NO_LIMIT>, <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;}; }; };
};
Documentation/devicetree/bindings/thermal/thermal.txt
8.2 cooling_device: pwm-fan
8.2.1 数据结构
//drivers/hwmon/pwm-fan.c
static const struct thermal_cooling_device_ops pwm_fan_cooling_ops = {.get_max_state = pwm_fan_get_max_state,.get_cur_state = pwm_fan_get_cur_state,.set_cur_state = pwm_fan_set_cur_state,
};
8.2.2 初始化
pwm_fan_probe();-> pwm_fan_of_get_cooling_data();-> of_property_read_u32_array(np, "cooling-levels", ctx->pwm_fan_cooling_levels, num); //获取cooling device支持的state-> devm_thermal_of_cooling_device_register(..., "pwm-fan", ctx, &pwm_fan_cooling_ops);
8.2.3 设备树示例
请看“3.3.2.1” 小节
8.3 cooling_device: thermal-cpufreq
8.3.1 数据结构
//drivers/thermal/cpu_cooling.c
static struct thermal_cooling_device_ops cpufreq_cooling_ops = { .get_max_state = cpufreq_get_max_state,.get_cur_state = cpufreq_get_cur_state,.set_cur_state = cpufreq_set_cur_state,
};
8.3.2 初始化流程
cpufreq_online();-> of_cpufreq_cooling_register();-> __cpufreq_cooling_register();-> freq_qos_add_request();-> thermal_of_cooling_device_register();
8.3.3 设备树示例
请看 “3.3.2.2” 小节
9 调试
9.1 /sys/kernel/debug/tracing/events/thermal
9.2 /sys/kernel/debug/tracing/events/thermal_power_allocator/
9.3 tmon
tomon工具可以显示下面的信息:
- 实时显示Thermal Zone(温度传感器)获取的温度
- Thermal Zone的trip points
- Cooling device的states信息(current state和 max state)
- Cooling device 和 Thermal zone的绑定关系
⼯具源码:<kernel_src>/tools/thermal/tmon
执行命令“ ./tools/thermal/tmon/tmon” 后会显示下面的界面