thermal的cpu cool device

在drivers/thermal/lmx_thermal.c 中的imx_thermal_probe中有注册cpu cool device
	data->cdev = cpufreq_cooling_register(cpu_present_mask);
	if (IS_ERR(data->cdev)) {
		ret = PTR_ERR(data->cdev);
		if (ret != -EPROBE_DEFER)
			dev_err(&pdev->dev,
				"failed to register cpufreq cooling device: %d\n",
				ret);
		return ret;
	}
cpufreq_cooling_register->__cpufreq_cooling_register
static struct thermal_cooling_device *
__cpufreq_cooling_register(struct device_node *np,
			const struct cpumask *clip_cpus, u32 capacitance,
			get_static_t plat_static_func)
{
	struct cpufreq_policy *policy;
	struct thermal_cooling_device *cool_dev;
	struct cpufreq_cooling_device *cpufreq_dev;
	char dev_name[THERMAL_NAME_LENGTH];
	struct cpufreq_frequency_table *pos, *table;
	cpumask_var_t temp_mask;
	unsigned int freq, i, num_cpus;
	int ret;
	struct thermal_cooling_device_ops *cooling_ops;
	bool first;
//通过alloc_cpumask_var 申请一段空间,用来保存cpu_mask
	if (!alloc_cpumask_var(&temp_mask, GFP_KERNEL))
		return ERR_PTR(-ENOMEM);
//将要进行降温控制的cpu和所有在线的cpu相与,结果还是要控制的cpu
	cpumask_and(temp_mask, clip_cpus, cpu_online_mask);
//得到cpu 调频的几种governer。调频的governer分别是cpufreq_performance/cpufreq_powersave/cpufreq_userspace/cpufreq_ondemand/cpufreq_conservative
	policy = cpufreq_cpu_get(cpumask_first(temp_mask));
	if (!policy) {
		pr_debug("%s: CPUFreq policy not found\n", __func__);
		cool_dev = ERR_PTR(-EPROBE_DEFER);
		goto free_cpumask;
	}
//得到调频的governer后就可以得到这可以调整的频率,毕竟调频governer就是调整cpu 频率的,所以肯定保存有cpu的频率
	table = policy->freq_table;
	if (!table) {
		pr_debug("%s: CPUFreq table not found\n", __func__);
		cool_dev = ERR_PTR(-ENODEV);
		goto put_policy;
	}

	cpufreq_dev = kzalloc(sizeof(*cpufreq_dev), GFP_KERNEL);
	if (!cpufreq_dev) {
		cool_dev = ERR_PTR(-ENOMEM);
		goto put_policy;
	}
//使用cpumask_weight计算clip_cpus里面有几个bit为1,从而得到有几个cpu可以降温
	num_cpus = cpumask_weight(clip_cpus);
	cpufreq_dev->time_in_idle = kcalloc(num_cpus,
					    sizeof(*cpufreq_dev->time_in_idle),
					    GFP_KERNEL);
	if (!cpufreq_dev->time_in_idle) {
		cool_dev = ERR_PTR(-ENOMEM);
		goto free_cdev;
	}

	cpufreq_dev->time_in_idle_timestamp =
		kcalloc(num_cpus, sizeof(*cpufreq_dev->time_in_idle_timestamp),
			GFP_KERNEL);
	if (!cpufreq_dev->time_in_idle_timestamp) {
		cool_dev = ERR_PTR(-ENOMEM);
		goto free_time_in_idle;
	}
//找到总共有几个频率可以调整
	/* Find max levels */
	cpufreq_for_each_valid_entry(pos, table)
		cpufreq_dev->max_level++;

	cpufreq_dev->freq_table = kmalloc(sizeof(*cpufreq_dev->freq_table) *
					  cpufreq_dev->max_level, GFP_KERNEL);
	if (!cpufreq_dev->freq_table) {
		cool_dev = ERR_PTR(-ENOMEM);
		goto free_time_in_idle_timestamp;
	}

	/* max_level is an index, not a counter */
	cpufreq_dev->max_level--;

	cpumask_copy(&cpufreq_dev->allowed_cpus, clip_cpus);
//通过目前flow,这里的电流capacitance 为0,所以cpu cool device调整使用的ops就是cpufreq_cooling_ops
	if (capacitance) {
		cpufreq_dev->plat_get_static_power = plat_static_func;

		ret = build_dyn_power_table(cpufreq_dev, capacitance);
		if (ret) {
			cool_dev = ERR_PTR(ret);
			goto free_table;
		}

		cooling_ops = &cpufreq_power_cooling_ops;
	} else {
		cooling_ops = &cpufreq_cooling_ops;
	}

	ret = ida_simple_get(&cpufreq_ida, 0, 0, GFP_KERNEL);
	if (ret < 0) {
		cool_dev = ERR_PTR(ret);
		goto free_power_table;
	}
	cpufreq_dev->id = ret;
//将频率按从打到小排序保存在freq_table
	/* Fill freq-table in descending order of frequencies */
	for (i = 0, freq = -1; i <= cpufreq_dev->max_level; i++) {
		freq = find_next_max(table, freq);
		cpufreq_dev->freq_table[i] = freq;

		/* Warn for duplicate entries */
		if (!freq)
			pr_warn("%s: table has duplicate entries\n", __func__);
		else
			pr_debug("%s: freq:%u KHz\n", __func__, freq);
	}

	snprintf(dev_name, sizeof(dev_name), "thermal-cpufreq-%d",
		 cpufreq_dev->id);
//注册cool deivce
	cool_dev = thermal_of_cooling_device_register(np, dev_name, cpufreq_dev,
						      cooling_ops);
	if (IS_ERR(cool_dev))
		goto remove_ida;

	cpufreq_dev->clipped_freq = cpufreq_dev->freq_table[0];
	cpufreq_dev->cool_dev = cool_dev;

	mutex_lock(&cooling_list_lock);
	/* Register the notifier for first cpufreq cooling device */
	first = list_empty(&cpufreq_dev_list);
	list_add(&cpufreq_dev->node, &cpufreq_dev_list);
	mutex_unlock(&cooling_list_lock);

	if (first)
		cpufreq_register_notifier(&thermal_cpufreq_notifier_block,
					  CPUFREQ_POLICY_NOTIFIER);


}
前面的分析我们知道对cool device来所调节是通过thermal_cooling_device_ops来进行,本例中的thermal_cooling_device_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,
};
最终在governer中通过set_cur_state来调节
static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev,
				 unsigned long state)
{
	struct cpufreq_cooling_device *cpufreq_device = cdev->devdata;
	unsigned int cpu = cpumask_any(&cpufreq_device->allowed_cpus);
	unsigned int clip_freq;

	/* Request state should be less than max_level */
	if (WARN_ON(state > cpufreq_device->max_level))
		return -EINVAL;

	/* Check if the old cooling action is same as new cooling action */
	if (cpufreq_device->cpufreq_state == state)
		return 0;
//从freq_table中得到需要调整的频率
	clip_freq = cpufreq_device->freq_table[state];
	cpufreq_device->cpufreq_state = state;
	cpufreq_device->clipped_freq = clip_freq;
//通过cpu调频的框架来将更新cpu的频率
	cpufreq_update_policy(cpu);

	return 0;
}


你可能感兴趣的:(Linux,源码分析)