thermal 代码分析

thermal 代码分析

基础知识

1.thermal 的框架

使用场景:

当cpu 温度过高时,适当的进行降频,从而控制稳定cpu 温度。

代码分析:

从Makefile 开始

# SPDX-License-Identifier: GPL-2.0
#
# Makefile for sensor chip drivers.
#

obj-$(CONFIG_THERMAL)		+= thermal_sys.o
thermal_sys-y			+= thermal_core.o thermal_sysfs.o \
					thermal_helpers.o                             # 核心代码

# interface to/from other layers providing sensors
thermal_sys-$(CONFIG_THERMAL_HWMON)		+= thermal_hwmon.o
thermal_sys-$(CONFIG_THERMAL_OF)		+= of-thermal.o            # dts 解析

# governors
thermal_sys-$(CONFIG_THERMAL_GOV_FAIR_SHARE)	+= fair_share.o     # governors 也是五种
thermal_sys-$(CONFIG_THERMAL_GOV_BANG_BANG)	+= gov_bang_bang.o
thermal_sys-$(CONFIG_THERMAL_GOV_STEP_WISE)	+= step_wise.o
thermal_sys-$(CONFIG_THERMAL_GOV_USER_SPACE)	+= user_space.o
thermal_sys-$(CONFIG_THERMAL_GOV_POWER_ALLOCATOR)	+= power_allocator.o

# cpufreq cooling
thermal_sys-$(CONFIG_CPU_THERMAL)	+= cpu_cooling.o              # cooling device

# clock cooling
thermal_sys-$(CONFIG_CLOCK_THERMAL)	+= clock_cooling.o            

# devfreq cooling
thermal_sys-$(CONFIG_DEVFREQ_THERMAL) += devfreq_cooling.o

# platform thermal drivers
obj-$(CONFIG_SUN8I_THS)		+= sun8i_ths.o                      # 平台相关代码,提供读取温度的接口

1.thermal 的初始化

static int __init thermal_init(void)
{
    
    
	int result;

	mutex_init(&poweroff_lock);
	result = thermal_register_governors();     // 1.注册五种governors
	
	result = class_register(&thermal_class);   // 2.注册 /sys/class/thermal 	

	result = of_parse_thermal_zones();         // 3.解析dts 的"thermal-zones"节点,并注册
                                                // thermal_zone_device
	

	result = register_pm_notifier(&thermal_pm_nb);  // 4.notifier
	
}
fs_initcall(thermal_init);

其中最重要的是 of_parse_thermal_zones

先看看 相关dts:

 thermal-zones {
    
    
                cpu_thermal: cpu_thermal {
    
    
                        polling-delay-passive = <330>;
                        polling-delay = <1000>;
                        thermal-sensors = <&ths 0>;
                        trips {
    
    
	                        cpu_warm: cpu_warm {
    
    
	                            temperature = <65000>;
	                            hysteresis = <2000>;
	                            type = "passive";
	                        };
	                        cpu_hot: cpu_hot {
    
    
	                            temperature = <75000>;
	                            hysteresis = <2000>;
	                            type = "passive";
	                        };
	                        cpu_very_hot: cpu_very_hot {
    
    
	                            temperature = <90000>;
	                            hysteresis = <2000>;
	                            type = "passive";
	                        };
	                        cpu_crit: cpu_crit {
    
    
	                            temperature = <105000>;
	                            hysteresis = <2000>;
	                            type = "critical";
	                        };
	                    };

	                    cooling-maps {
    
    
	                        cpu_warm_limit_cpu {
    
    
	                            trip = <&cpu_warm>;
	                            cooling-device = <&cpu0 THERMAL_NO_LIMIT 1>;
	                        };
	                        cpu_hot_limit_cpu {
    
    
	                            trip = <&cpu_hot>;
	                            cooling-device = <&cpu0 2 3>;
	                        };
	                        cpu_very_hot_limit_cpu {
    
    
	                            trip = <&cpu_very_hot>;
	                            cooling-device = <&cpu0 5 THERMAL_NO_LIMIT>;
	                        };
	                    };
                };
        };

int __init of_parse_thermal_zones(void)
{
    
    	
	np = of_find_node_by_name(NULL, "thermal-zones");    //  查找 "thermal-zones"	

	for_each_available_child_of_node(np, child) {
    
            // 遍历子节点
		
		tz = thermal_of_build_thermal_zone(child);       // 解析关键词,填充tz 数据结构
		
		ops = kmemdup(&of_thermal_ops, sizeof(*ops), GFP_KERNEL);
		if (!ops)
			goto exit_free;

		tzp = kzalloc(sizeof(*tzp), GFP_KERNEL);	

		zone = thermal_zone_device_register(child->name, tz->ntrips,
						    mask, tz,
						    ops, tzp,
						    tz->passive_delay,
						    tz->polling_delay);        // 注册一个 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)
{
    
    	

	dev_set_name(&tz->device, "thermal_zone%d", tz->id);
	result = device_register(&tz->device);	          // 注册设备

	result = thermal_set_governor(tz, governor);      // set governor
	
	list_add_tail(&tz->node, &thermal_tz_list);	

	/* Bind cooling devices for this zone */
	bind_tz(tz);               // 将thermal_cdev_list 中的所有cooling devices 与之绑定

	INIT_DELAYED_WORK(&tz->poll_queue, thermal_zone_device_check); // 初始化工作队列

	if (atomic_cmpxchg(&tz->need_update, 1, 0))
		thermal_zone_device_update(tz, THERMAL_EVENT_UNSPECIFIED);
}

cpufreq cooling device 的注册,位于 cpufreq_driver -> reay 阶段,

static void cpufreq_ready(struct cpufreq_policy *policy)
{
    
    	
	if (of_find_property(np, "#cooling-cells", NULL)) {
    
    	// 包含#cooling-cells  属性的
		
		priv->cdev = of_cpufreq_power_cooling_register(np,
				policy, power_coefficient, NULL);		// register
	}
}
else {
    
    
		cooling_ops = &cpufreq_cooling_ops;    
	}

	cdev = thermal_of_cooling_device_register(np, dev_name, cpufreq_cdev,
						  cooling_ops);
list_add(&cdev->node, &thermal_cdev_list);        //  就是添加到 thermal_cdev_list 中

需要关心的是 cpufreq_cooling_ops

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,
};

.set_cur_state 被 thermal governor 调用

static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
    
    
	clip_freq = cpufreq_cdev->freq_table[state].frequency;  //根据state 设置新的频率
	cpufreq_cdev->cpufreq_state = state;
	cpufreq_cdev->clipped_freq = clip_freq;         // 

	cpufreq_update_policy(cpufreq_cdev->policy->cpu);
	return 0;
}

时序图:

在这里插入图片描述

Guess you like

Origin blog.csdn.net/agave7/article/details/105140240