蜗窝科技

linux thermal framework(3)_thermal cooling device

huowj · · 电源管理子系统 · 阅读 3072 · 2025年4月存档

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

继续讨论

新讨论请用 GitHub 账号在下方发言——评论会保存在本仓库的 Discussions 里,与站点同寿; 再下方的「历史评论」是原站的只读存档。