本文采用知识共享署名 4.0 国际许可协议进行许可,转载时请注明原文链接,图片在使用时请保留全部内容,可适当缩放并在引用处附上图片所在的文章链接。
Thermal是内核开发者定义的⼀套⽀持根据指定governor控制系统温度,以防⽌芯⽚过热的框架模型。Thermal framework由governor、core、cooling device、sensor driver组成,软件架构如下:
<*> Generic Thermal sysfs driver --->
--- Generic Thermal sysfs driver
[*] APIs to parse thermal data out of device tree
[*] Enable writable trip points
Default Thermal governor ( power_allocator ) ---> /* default thermal governor */
[ ] Fair -share thermal governor
[ ] Step_wise thermal governor /* step_wise governor */
[ ] Bang Bang thermal governor
[*] User_space thermal governor /* user_space governor */
-*- Power allocator thermal governor /* power_allocator governor */
[*] generic cpu cooling support /* cooling device */
[ ] Generic clock cooling support
[*] Generic device cooling support /* cooling device */
[ ] Thermal emulation mode support
< > Temperature sensor driver for Freescale i.MX SoCs
<*> Rockchip thermal driver /* thermal sensor driver */
< > rk_virtual thermal driver
<*> rk3368 thermal driver legacy /* thermal sensor driver */
通过“Default Thermal governor”配置项,可以选择温控策略,开发者可以根据实际产品需求进⾏修改。
Tsadc在温控中作为thermal sensor,⽤于获取温度,通常需要在DTSI和DTS都做配置。
以RK3399为例,DTSI包括如下配置:
tsadc : tsadc@ff260000 {
compatible = "rockchip,rk3399-tsadc" ;
reg = <0x0 0xff260000 0x0 0x100 >; /* 寄存器基地址和寄存器地址总⻓度 */
interrupts = <GIC_SPI 97 IRQ_TYPE_LEVEL_HIGH 0>; /* 中断号及中断触发⽅式 */
assigned -clocks = <& cru SCLK_TSADC >; /* ⼯作时钟,750KHz */
assigned -clock -rates = <750000 >;
clocks = <& cru SCLK_TSADC >, <& cru PCLK_TSADC >; /* ⼯作时钟和配置时钟 */
clock -names = "tsadc" , "apb_pclk" ;
resets = <& cru SRST_TSADC >; /* 复位信号 */
reset -names = "tsadc-apb" ;
rockchip ,grf = <& grf >; /* 引⽤grf 模块,部分平台需要 */
rockchip ,hw -tshut -temp = <120000 >; /* 过温重启阀值,120 摄⽒度 */
/* tsadc 输出引脚配置,⽀持两种模式:gpio 和otpout */
pinctrl -names = "gpio" , "otpout" ;
pinctrl -0 = <& otp_gpio >;
pinctrl -1 = <& otp_out >;
/*
* thermal sensor 标识,表⽰tsadc 可以作为⼀个thermal sensor ,
* 并指定了引⽤tsadc 节点的时候需要带⼏个参数。
* 如果SoC ⾥⾯只有⼀个tsadc ,可以设置为0,超过⼀个必须设置为1。
*/
#thermal-sensor-cells = <1>;
status = "disabled" ;
};
/* IO 口配置 */
pinctrl : pinctrl {
...
tsadc {
/* 配置为gpio 模式 */
otp_gpio : otp -gpio {
rockchip ,pins = <1 6 RK_FUNC_GPIO &pcfg_pull_none >;
};
/* 配置为over temperature protection 模式 */
otp_out : otp -out {
rockchip ,pins = <1 6 RK_FUNC_1 &pcfg_pull_none >;
};
};
....
}
DTS的配置,主要⽤于选择通过CRU复位还是GPIO复位,低电平复位还是⾼电平复位。需要特别注意的是如果配置成GPIO复位,硬件上需要否把tsadc输出引脚连到PMIC的复位脚,否则只能配置成CRU复位。
&tsadc {
rockchip ,hw -tshut -mode = <1>; /* tshut mode 0:CRU 1:GPIO */
rockchip ,hw -tshut -polarity = <1>; /* tshut polarity 0:LOW 1:HIGH */
status = "okay" ;
};
CPU在温控中作为cooling device,节点中需要包含#cooling-cells、dynamic-power-coefficient属性。
以RK3399为例:
cpu_l0 : cpu@0 {
device_type = "cpu" ;
compatible = "arm,cortex-a53" , "arm,armv8" ;
reg = <0x0 0x0 >;
enable -method = "psci" ;
#cooling-cells = <2>; /* cooling device 标识,表⽰该设备可以作为⼀个cooling device */
clocks = <& cru ARMCLKL >;
cpu -idle -states = <& CPU_SLEEP &CLUSTER_SLEEP >;
dynamic -power -coefficient = <100 >; /* 动态功耗常数C,动态功耗公式为Pdyn=C*V^2*F */
};
...
cpu_b0 : cpu@100 {
device_type = "cpu" ;
compatible = "arm,cortex-a72" , "arm,armv8" ;
reg = <0x0 0x100 >;
enable -method = "psci" ;
#cooling-cells = <2>; /* cooling device 标识,表⽰该设备可以作为⼀个cooling device */
clocks = <& cru ARMCLKB >;
cpu -idle -states = <& CPU_SLEEP &CLUSTER_SLEEP >;
dynamic -power -coefficient = <436 >; /* ⽤于计算动态功耗的参数 */
};
GPU在温控中作为cooling device,节点需要包含#cooling-cells属性和power_model⼦节点。
以RK3399为例:
gpu : gpu@ff9a0000 {
compatible = "arm,malit860" ,"arm,malit86x" ,"arm,malit8xx" , "arm,mali-midgard" ;
reg = <0x0 0xff9a0000 0x0 0x10000 >;
interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH 0>, <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH 0>, <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH 0>;
interrupt -names = "GPU" , "JOB" , "MMU" ;
clocks = <& cru ACLK_GPU >;
clock -names = "clk_mali" ;
#cooling-cells = <2>; /* cooling device 标识,表⽰该设备可以作为⼀个cooling device */
power -domains = <& power RK3399_PD_GPU >;
power -off -delay -ms = <200 >;
status = "disabled" ;
gpu_power_model : power_model {
compatible = "arm,mali-simple-power-model" ;
static -coefficient = <411000 >; /* ⽤于计算静态功耗的参数 */
dynamic -coefficient = <733 >; /* ⽤于计算动态功耗的参数 */
ts = <32000 4700 ( -80 ) 2>; /* ⽤于计算静态功耗的参数 */
thermal -zone = "gpu-thermal" ; /* 从gpu-thermal 获取温度,⽤于计算静态功耗 */
};
};
Termal zone节点主要⽤于配置温控策略相关的参数并⽣成对应的⽤⼾态接口。
以RK3399为例:
thermal_zones : thermal -zones {
/* ⼀个节点对应⼀个thermal zone ,并包含温控策略相关参数 */
soc_thermal : soc -thermal {
/* 温度⾼于trip-point-0 指定的值,每隔20ms 获取⼀次温度 */
polling -delay -passive = <20 >; /* milliseconds */
/* 温度低于trip-point-0 指定的值,每隔1000ms 获取⼀次温度 */
polling -delay = <1000 >; /* milliseconds */
/* 温度等于trip-point-1 指定的值时,系统分配给cooling device 的能量 */
sustainable -power = <1000 >; /* milliwatts */
/* 当前thermal zone 通过tsadc0 获取温度 */
thermal -sensors = <& tsadc 0>;
/* trips 包含不同温度阀值,不同的温控策略,配置不⼀定相同 */
trips {
/*
* 温控阀值,超过该值温控策略开始⼯作,但不⼀定⻢上限制频率,
* power 小到⼀定程度才开始限制频率
*/
threshold : trip -point -0 {
/* 超过70 摄⽒度,温控策略开始⼯作,并且70 摄⽒度也是tsadc 触发中断的⼀个阀值 */
temperature = <70000 >; /* millicelsius */
/* 温度低于temperature-hysteresis 时触发中断,当前未实现,但是框架要求必须填 */
hysteresis = <2000 >; /* millicelsius */
type = "passive" ; /* 表⽰超过该温度值时,使⽤polling-delay-passive */
};
/* 温控⽬标温度,期望通过降频使得芯⽚不超过该值 */
target : trip -point -1 {
/* 期望通过降频使得芯⽚不超过85 摄⽒度,并且85 摄⽒度也是tsadc 触发中断的⼀个阀值 */
temperature = <85000 >; /* millicelsius */
/* 温度低于temperature-hysteresis 时触发中断,当前未实现,但是框架要求必须填 */
hysteresis = <2000 >; /* millicelsius */
type = "passive" ; /* 表⽰超过该温度值时,使⽤polling-delay-passive */
};
/* 过温保护阀值,如果降频后温度仍然上升,那么超过该值后,让系统重启 */
soc_crit : soc -crit {
/* 超过115 摄⽒度重启,并且115 摄⽒度也是tsadc 触发中断的⼀个阀值 */
temperature = <115000 >; /* millicelsius */
/* 温度低于temperature-hysteresis 时触发中断,当前未实现,但是框架要求必须填 */
hysteresis = <2000 >; /* millicelsius */
type = "critical" ; /* 表⽰超过该温度值时,重启 */
};
};
/* cooling device 配置节点,每个⼦节点代表⼀个cooling device */
cooling -maps {
map0 {
/*
* 表⽰在target trip 下,该cooling device 才起作⽤,
* 对于power allocater 策略必须填target
*/
trip = <& target >;
/* A53 做为cooloing device , THERMAL_NO_LIMIT 不起作⽤,但必须填 */
cooling -device = <& cpu_l0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT >;
contribution = <4096 >; /* 计算功耗时乘以4096/1024 倍,⽤于调整降频顺序和尺度 */
};
map1 {
/*
* 表⽰在target trip 下,该cooling device 才起作⽤,
* 对于power allocater 策略必须填target
*/
trip = <& target >;
/* A72 做为cooloing device , THERMAL_NO_LIMIT 不起作⽤,但必须填 */
cooling -device = <& cpu_b0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT >;
contribution = <1024 >;/* 计算功耗时乘以1024/1024 倍,⽤于调整降频顺序和尺度 */
};
map2 {
/*
* 表⽰在target trip 下,该cooling device 才起作⽤,
* 对于power allocater 策略必须填target
*/
trip = <& target >;
/* GPU 做为cooloing device , THERMAL_NO_LIMIT 不起作⽤,但必须填 */
cooling -device = <& gpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT >;
contribution = <4096 >;/* 计算功耗时乘以4096/1024 倍,⽤于调整降频顺序和尺度 */
};
};
};
/* ⼀个节点对应⼀个thermal zone ,并包含温控策略相关参数,当前thermal zone 只⽤于获取温度 */
gpu_thermal : gpu -thermal {
/* 包含温控策略配置的情况下才起作⽤,架要求必须填 */
polling -delay -passive = <100 >; /* milliseconds */
/* 每隔1000ms 获取⼀次温度 */
polling -delay = <1000 >; /* milliseconds */
/* 当前thermal zone 通过tsadc1 获取温度 */
thermal -sensors = <& tsadc 1>;
};
};
⽤⼾态接口在/sys/class/thermal/⽬录下,具体内容和DTSI中thermal zone节点的配置对应。有的平台thermalzone节点下只有⼀个⼦节点,对应/sys/class/thermal/⽬录下也只有thermal_zone0⼦⽬录;有的平台有两个⼦节点,对应/sys/class/thermal/⽬录下就会有thermal_zone0和thermal_zone1⼦⽬录。通过⽤⼾态接口可以切换温控策略,查看当前温度等。
以RK3399为例⼦,/sys/class/thermal/thermal_zone0/⽬录下包含如下常⽤的信息:
temp /* 当前温度 */
available_policies /* ⽀持的温控策略 */
policy /* 当前使⽤的温控策略 */
sustainable_power /* 期望的最⾼温度下对应的power 值 */
integral_cutoff /* PID 算法中I的触发条件:当前温度-期望的最⾼温度
直接查看⽤⼾态接口thermal_zone0或者thermal_zone1⽬录下的temp节点即可。
以RK3399为例,获取CPU温度,在串口中输⼊如下命令:
cat /sys /class /thermal /thermal_zone0 /temp
获取GPU温度,在串口中输⼊如下命令:
cat /sys /class /thermal /thermal_zone1 /temp
⽅法⼀:menuconfig中默认温控策略设置为user_space。
<*> Generic Thermal sysfs driver --->
--- Generic Thermal sysfs driver
[*] APIs to parse thermal data out of device tree
[*] Enable writable trip points
Default Thermal governor ( user_space ) ---> /* power_allocator 改为user_space */
⽅法⼆:开机后通过命令关温控。
⾸先,把温控策略切换到user_space,即把⽤⼾态接口下的policy节点改成user_space;或者把mode设置成
disabled状态;然后,解除频率限制,即将⽤⼾态接口下的所有cdev的cur_state设置为0。
以RK3399为例,策略切换到user_space:
echo user_space > /sys /class /thermal /thermal_zone0 /policy
或者把mode设置成disabled状态:
echo disabled > /sys /class /thermal /thermal_zone0 /mode
解除频率限制:
/* 具体有多少个cdev ,根据实际情况修改 */
echo 0 > /sys /class /thermal /thermal_zone0 /cdev0 /cur_state
echo 0 > /sys /class /thermal /thermal_zone0 /cdev1 /cur_state
echo 0 > /sys /class /thermal /thermal_zone0 /cdev2 /cur_state
Thermal开发指南