linux thermal framework(3)_thermal cooling device

作者:huowj 发布于:2025-4-14 10:33 分类:电源管理子系统

1. 介绍

linux使用thermal cooling device来描述一个平台上可以降温的设备。

2. thermal cooling device相关的API以及功能分析

2.1 struct thermal_cooling_device

一个thermal cooling device是用struct thermal_cooling_device来表示的

struct thermal_cooling_device {
	int id;
	const char *type;
	unsigned long max_state;
	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;
#ifdef CONFIG_THERMAL_DEBUGFS
	struct thermal_debugfs *debugfs;
#endif
};

max_state,这个cooling_device最大state的索引

thermal_instances, 这个节点和struct thermal_instance有关,我们之前说过,对thermal zone的某一个passive or active类型的trip,会有cooling device的策略介入,那么struct thermal_instance就是作为连接trip和相应的cooling device的,struct thermal_instance的定义如下:

/*
 * 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_cooling_device *cdev;
	const struct thermal_trip *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 trip_node; /* node in trip->thermal_instances */
	struct list_head cdev_node; /* node in cdev->thermal_instances */
	unsigned int weight; /* The weight of the cooling device */
	bool upper_no_limit;
};


1)注释:thermal_instance是用来描述一个cooling deivce在某一个thermal zone的一个trip上的行为
2)id, 这个thermal instance的编号
3)name, 这个thermal instance的名字
4)cdev,和这个thermal instance相关的cooling device
5)trip, 和这个thermal instance相关的trip
6)initialized, 是否初始化
7)upper/lower/targe, 这个trip对应的最高/最低/期望state
8)attr/attr_name, 属性一般为trip id
9)weight/weight_attr/weight_attr_name, weight是这个cooling_device在这个trip所占的比重

我们在下面的章节具体讲一下这个结构体是如何被管理的

2.2 thermal_cooling_device的注册

和thermal zone一样,cooling device也不是一个实际存在的物理设备,是由linux抽象出来表示一类可降温的设备,这些设备可以有cpufreq(通过限制cpu频率来降低cpu温度)、devfreq(通过限制设备频率来降低设备温度)、cpuidle(通过进入低功耗模式的idle来降低cpu温度),同样的,cooling device也是通过cpufreq/devfreq/cpuidle这些设备注册的时候,向thermal core注册的。我们以cpufreq为例来描述详细的注册过程。

和thermal zone一样,我们也通过dts中一个实际的例子来描述cooling device注册,这里再次使用mt8195.dtsi中的cpu作为例子,以下是cpu0的thermal_zone节点,只保留了cooling-map相关的信息:

thermal_zones: thermal-zones {
	cpu0-thermal {
  ...
		trips {
			cpu0_alert: trip-alert {
				temperature = <85000>;
				hysteresis = <2000>;
				type = "passive";
			};
		cpu0_crit: trip-crit {
			temperature = <100000>;
			hysteresis = <2000>;
			type = "critical";
		};
	};

	cooling-maps {
		map0 {
			trip = <&cpu0_alert>;
			cooling-device = <&cpu0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
					<&cpu1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
					<&cpu2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
					<&cpu3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
		};
	};

... other thermal zones

}

cpu0-thermal只有一个cooling-map,即map0,map0对应的trip是类型为passive的trip-alert,他的cooling-device引用了四个cpu,我们看下相同dts中的cpu0的节点描述:

cpu0: cpu@0 {
  device_type = "cpu";
  compatible = "arm,cortex-a55";
  reg = <0x000>;
  enable-method = "psci";
  performance-domains = <&performance 0>;
  clock-frequency = <1701000000>;
  capacity-dmips-mhz = <308>;
  cpu-idle-states = <&cpu_ret_l &cpu_off_l>;
  i-cache-size = <32768>;
  i-cache-line-size = <64>;
  i-cache-sets = <128>;
  d-cache-size = <32768>;
  d-cache-line-size = <64>;
  d-cache-sets = <128>;
  next-level-cache = <&l2_0>;
  #cooling-cells = <2>;
};

cpu0使用cooling-cells来描述使用几个u32来引用这个cooling device。

cpu的cooling device是在cpufreq policy初始化的时候通过调用of_cpufreq_cooling_register向thermal core注册的,这个函数会判断dts中是否有cooling-cells节点,如果存在的话,会调用__cpufreq_cooling_register

/**
 * thermal_bind_cdev_to_trip - bind a cooling device to a thermal zone
 * @tz:		pointer to struct thermal_zone_device
 * @td:		descriptor of the trip point to bind @cdev to
 * @cdev:	pointer to struct thermal_cooling_device
 * @cool_spec:	cooling specification for the trip point and @cdev
 *
 * 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.
 *
 * Return: 0 on success, the proper error value otherwise.
 */
static int thermal_bind_cdev_to_trip(struct thermal_zone_device *tz,
				     struct thermal_trip_desc *td,
				     struct thermal_cooling_device *cdev,
				     struct cooling_spec *cool_spec)
{
	struct thermal_instance *dev;
	bool upper_no_limit;
	int result;
/* lower default 0, upper default max_state */
if (cool_spec->lower == THERMAL_NO_LIMIT)
	cool_spec->lower = 0;

if (cool_spec->upper == THERMAL_NO_LIMIT) {
	cool_spec->upper = cdev->max_state;
	upper_no_limit = true;
} else {
	upper_no_limit = false;
}

if (cool_spec->lower > cool_spec->upper || cool_spec->upper > cdev->max_state)
	return -EINVAL;

dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev)
	return -ENOMEM;

dev->cdev = cdev;
dev->trip = &td->trip;
dev->upper = cool_spec->upper;
dev->upper_no_limit = upper_no_limit;
dev->lower = cool_spec->lower;
dev->target = THERMAL_NO_TARGET;
dev->weight = cool_spec->weight;

result = ida_alloc(&tz->ida, GFP_KERNEL);
if (result < 0)
goto free_mem;
dev->id = result;
sprintf(dev->name, "cdev%d", dev->id);
result =
    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
if (result)
	goto release_ida;

snprintf(dev->attr_name, sizeof(dev->attr_name), "cdev%d_trip_point",
	 dev->id);
sysfs_attr_init(&dev->attr.attr);
dev->attr.attr.name = dev->attr_name;
dev->attr.attr.mode = 0444;
dev->attr.show = trip_point_show;
result = device_create_file(&tz->device, &dev->attr);
if (result)
	goto remove_symbol_link;

snprintf(dev->weight_attr_name, sizeof(dev->weight_attr_name),
	 "cdev%d_weight", dev->id);
sysfs_attr_init(&dev->weight_attr.attr);
dev->weight_attr.attr.name = dev->weight_attr_name;
dev->weight_attr.attr.mode = S_IWUSR | S_IRUGO;
dev->weight_attr.show = weight_show;
dev->weight_attr.store = weight_store;
result = device_create_file(&tz->device, &dev->weight_attr);
if (result)
	goto remove_trip_file;

result = thermal_instance_add(dev, cdev, td);
if (result)
goto remove_weight_file;
thermal_governor_update_tz(tz, THERMAL_TZ_BIND_CDEV);

return 0;

remove_weight_file:

device_remove_file(&tz->device, &dev->weight_attr);

remove_trip_file:

device_remove_file(&tz->device, &dev->attr);

remove_symbol_link:

sysfs_remove_link(&tz->device.kobj, dev->name);

release_ida:

ida_free(&tz->ida, dev->id);

free_mem:

kfree(dev);

return result;

}
1)注释:这个接口将一个cooling device和一个thermal zone的某个固定的trip进行绑定
2)初始化一个thermal_instance,通过向它属于的thermal zone申请一个id,这个instance就叫做cdev+id,并在这个thermal zone的目录下面创建一个软连接,名字就是instance的名字,指向对应的cooling_device目录
3)初始化这个instance的attr和weight_attr,分别叫做cdev+id_trip_point和cdev+id_weight,并在这个thermal zone的目录下创建对应的文件
4)最后将这个instance分别加入thermal trip desc和cooling device的相应链表中

至此,thermal zone,trip,cooling device之间的关如下图,一个thermal zone包含若干个trip,thermal zone用数组来管理它的trip,一个trip可以对应若干个cooling device,一个cooling device也可以map若干个不同thermal zone的trip,trip和cooling device的对应关系通过thermal instance来描述

一个有两个trip的thermal zone的sysfs目录中trip相关的文件如下:

/sys/class/thermal/thermal_zone0 # ls -al
cdev0 -> ../cooling_device0
cdev0_trip_point
cdev0_weight
cdev1 -> ../cooling_device1
cdev1_trip_point
cdev1_weight


发表评论:

Copyright @ 2013-2015 蜗窝科技 All rights reserved. Powered by emlog