Android thermal (4)_cooling device(上)

前言

前面介绍过thermal子系统中部分重要结构,本次来到cooling device。在 Linux thermal framework 中,cooling device(冷却设备)是一个 可控的散热/限功耗单元。

它不一定是物理的“风扇”,也可以是 降低 CPU 频率、降低 GPU 频率、关掉 NPU 模块 等。对 thermal framework 来说,它就是一个“温控手段”的抽象。换句话说:sensor 负责测温 → thermal zone 定义温度策略 → cooling device 执行降温动作。

1 cooling device介绍

1.1 cooling device 的作用

(1)连接 温度 和 动作
当 thermal zone 检测温度超过某个 trip point(阈值),它会调用 cooling device 的接口。比如:

  • 温度 > 80℃ → CPU 频率降低一个档位(cpufreq cooling device)
  • 温度 > 90℃ → GPU 降频(devfreq cooling device)
  • 温度 > 95℃ → 打开风扇(fan cooling device)
  • 温度 > 100℃ → 强制关机(system shutdown cooling device)

(2)提供 可编程降温手段

  • 通过 thermal_cooling_device_ops,内核提供通用接口(get/set state, state2power, power2state)。
  • 各个厂商/驱动只需要实现自己的 cooling device,比如 Rockchip TSADC 驱动里 CPU 降频、高通TSENS 里的 GPU 降频,就可以被统一纳入 thermal framework 管理

1.2 cooling device 的分类(常见)

Linux 里已经有一些通用的 cooling device 驱动:

  • CPUFreq cooling device:通过 cpufreq 降低 CPU 频率 / 电压。
  • Devfreq cooling device:控制 GPU / NPU / DSP 的运行频率。
  • Fan cooling device:控制风扇转速(0 档 / 1 档 / 2 档 …)。
  • Power actor cooling device:直接限制模块的功率消耗(Watt)。
  • System shutdown cooling device:温度过高时,触发系统关机或重启。

1.3 cooling device 的工作机制

整体流程可以这样理解:

  • 传感器采样
    thermal sensor(如 Rockchip TSADC / Qualcomm TSENS)测得温度。
  • thermal zone 判断
    thermal zone 定义了 trip points(阈值)。比如 trip0 = 70℃(轻度),trip1 = 90℃(严重)。
  • governor 策略决策
    governor(如 step_wise, power_allocator)根据温度,决定需要激活哪个 cooling device、到什么 level。
  • 调用 cooling device ops
    thermal core 调用 set_cur_state() 或 power2state() 来实际执行操作。
  • 执行降温动作
    风扇加速、CPU/GPU 降频、触发关机 …

1.4 结构关系

  • Thermal Sensor:负责实时采集温度(如 TSADC/TSENS)。
  • Thermal Zone:热管理核心,维护 trip point、governor 策略。
  • Cooling Device:执行降温动作(CPU 降频、风扇、关机等)
    在这里插入图片描述

thermal sensor ↔ thermal zone ↔ cooling device 在 Linux Thermal Framework 中的关系用时序/交互图:
在这里插入图片描述

2 结构体&dtsi

2.1 thermal_cooling_device

这个结构体表示 一个冷却设备 实例(device object)。

struct thermal_cooling_device {
    int id;                          // cooling device 的全局唯一 ID
    char *type;                      // cooling device 类型(字符串),例如 "processor", "gpu", "fan"
    unsigned long max_state;         // 冷却设备的最大状态值 (0 ~ max_state)
    
    struct device device;            // 对应的内核 device 结构,挂到设备模型
    struct device_node *np;          // 设备树中的节点指针(如果有的话)
    
    void *devdata;                   // 私有数据,驱动注册时传入
    void *stats;                     // 统计数据(比如 cooling device 的使用情况)
    
    const struct thermal_cooling_device_ops *ops; // 操作函数表 (回调接口)
    
    bool updated;                    // cooling device 是否需要更新
    
    struct mutex lock;               // 保护 thermal_instances 链表
    struct list_head thermal_instances; // 挂载在此 cooling device 上的 thermal_instance 列表
    struct list_head node;           // cooling device 全局链表节点(thermal_cdev_list)

    ANDROID_KABI_RESERVE(1);         // Android 内核兼容性保留字段
};

2.2 thermal_cooling_device_ops

这是 冷却设备驱动 的一组回调函数(ops),用于告诉 thermal framework 如何操作该 cooling device。

struct thermal_cooling_device_ops {
	int (*get_max_state) (struct thermal_cooling_device *, unsigned long *);
	int (*get_cur_state) (struct thermal_cooling_device *, unsigned long *);
	int (*set_cur_state) (struct thermal_cooling_device *, unsigned long);
	int (*get_requested_power)(struct thermal_cooling_device *, u32 *);
	int (*state2power)(struct thermal_cooling_device *, unsigned long, u32 *);
	int (*power2state)(struct thermal_cooling_device *, u32, unsigned long *);
};

详细说明如下:

  • get_max_state(struct thermal_cooling_device *, unsigned long *)
    返回该冷却设备的 最大冷却状态(多少个等级)。比如风扇有 0–3 档,那最大 state = 3

  • get_cur_state(struct thermal_cooling_device *, unsigned long *)
    获取当前冷却设备的状态。比如风扇目前在 2 档,GPU 降频到一半。

  • set_cur_state(struct thermal_cooling_device *, unsigned long)
    设置冷却设备的状态。这是 thermal framework 调用 cooling device 实际动作的入口。比如当温度超过 trip point,就调用这个函数把风扇调到高档,或者降低 CPU 频率。

  • get_requested_power(…)
    可以获取当前冷却设备能够消耗/限制的功率。适用于 power actor 模式(动态功率限制)。

  • state2power(…)
    将冷却设备的 “state” 映射到实际功率限制。例如 CPU 降频到 state=2,功耗从 10W → 6W。

  • power2state(…)
    功率到状态的映射。Thermal governor 可以直接基于功率约束来决定需要的冷却 state。

【重要】简单总结
* xxx_state 系列是 状态 控制接口(风扇转速、CPU 降频级别)。
* xxx_power 系列是 功率 控制接口(高端芯片常见,支持智能功率管理)。

3 关键函数

继续thermal zone之后的调用,thermal zone在注册过程,调用 thermal_of_for_each_cooling_maps、thermal_zone_bind_cooling_device等函数,关联到绑定 thermal zone 与cooling device。

2.1 thermal_of_for_each_cooling_maps

thermal_of_for_each_cooling_maps 遍历一个 thermal_zone_device 的 所有 cooling-maps 节点,并对每个 cooling map 调用 action 函数(比如 bind/unbind)。

static int thermal_of_for_each_cooling_maps(struct thermal_zone_device *tz,
					    struct thermal_cooling_device *cdev,
					    int (*action)(struct device_node *, int, int,
							  struct thermal_zone_device *, struct thermal_cooling_device *))
{
	struct device_node *tz_np, *cm_np, *child;
	int ret = 0;

	// 根据 thermal zone 的名字找到对应的 Device Tree 节点。
	tz_np = thermal_of_zone_get_by_name(tz);
	if (IS_ERR(tz_np)) {
		pr_err("Failed to get node tz by name\n");
		return PTR_ERR(tz_np);
	}

	// 找 thermal zone 节点下的 cooling-maps 子节点
	cm_np = of_get_child_by_name(tz_np, "cooling-maps");
	if (!cm_np)
		goto out;

	// 遍历 cooling-maps 下的每个子节点,for_each_child_of_node是一个循环遍历的宏定义
	for_each_child_of_node(cm_np, child) {
		// 对每一个 map 节点,调用 thermal_of_for_each_cooling_device
		ret = thermal_of_for_each_cooling_device(tz_np, child, tz, cdev, action);
		if (ret) {
			of_node_put(child);
			break;
		}
	}

	// 最后释放引用计数:of_node_put(cm_np); of_node_put(tz_np);
	of_node_put(cm_np);
out:
	of_node_put(tz_np);

	return ret;
}

3.2 thermal_of_for_each_cooling_device

thermal_of_for_each_cooling_device 在**一个 map_np(某个 cooling-map 节点)**中,找到它引用的 trip 点和 cooling-device 列表,然后调用 action。
action 是一个指向目标函数的指针,通过action封装。在之前的调用关系,action等通过bind 或 unbind操作,具体需要看调用位置。

static int thermal_of_for_each_cooling_device(struct device_node *tz_np, struct device_node *map_np,
					      struct thermal_zone_device *tz, struct thermal_cooling_device *cdev,
					      int (*action)(struct device_node *, int, int,
							    struct thermal_zone_device *, struct thermal_cooling_device *))
{
	struct device_node *tr_np;
	int count, i, trip_id;

	// 从 map_np 读取 trip 属性(即 thermal trip 节点的 phandle)。
	tr_np = of_parse_phandle(map_np, "trip", 0);
	if (!tr_np)
		return -ENODEV;

	// 根据 trip 节点,找到对应的 trip id。
	trip_id = of_find_trip_id(tz_np, tr_np);
	if (trip_id < 0)
		return trip_id;

	// 统计 cooling-device 属性里有多少个 cooling device 被引用
	count = of_count_phandle_with_args(map_np, "cooling-device", "#cooling-cells");
	// 如果 ≤0,说明 DT 有问题(至少要有一个 cooling device)。
	if (count <= 0) {
		pr_err("Add a cooling_device property with at least one device\n");
		return -ENOENT;
	}

	/*
	 * At this point, we don't want to bail out when there is an
	 * error, we will try to bind/unbind as many as possible
	 * cooling devices
	 */
	// 遍历每个 cooling device
	for (i = 0; i < count; i++)
		// 对 map_np 里的每一个 cooling device 调用 action(即 bind 或 unbind)。
		action(map_np, i, trip_id, tz, cdev);

	return 0;
}

例如,当 thermal_of_unbind(tz, cdev) 调用时:

  • 它会传入 __thermal_of_unbind 作为 action。
  • thermal_of_for_each_cooling_maps 遍历 tz 下所有 cooling-maps
  • thermal_of_for_each_cooling_device 遍历 cooling-maps 里的每个 cooling-device。
  • 对于每个 cooling-device,调用 __thermal_of_unbind(…),去检查它是否是当前要 unbind 的 cdev,如果是,则调用 thermal_zone_unbind_cooling_device。

函数 (thermal_of_for_each_cooling_maps + thermal_of_for_each_cooling_device) 就是 thermal 框架里的 迭代器模式,专门遍历 DT 里定义的 cooling-maps,并对每个 (trip, cooling-device) 绑定关系执行 action(bind/unbind)。也就是说:

  • thermal_of_for_each_cooling_maps = 遍历 所有 map 节点
  • thermal_of_for_each_cooling_device = 遍历 map 节点里的所有 cooling device
  • action = 具体操作(bind/unbind)

3.3 thermal_of_zone_get_by_name

thermal_of_zone_get_by_name 的作用就是根据 thermal_zone_device 的名字(tz->type)找到对应的 device tree 节点。

static struct device_node *thermal_of_zone_get_by_name(struct thermal_zone_device *tz)
{
	struct device_node *np, *tz_np;

	// 1. 找到 dts 里的 "thermal-zones" 根节点
	np = of_find_node_by_name(NULL, "thermal-zones");
	if (!np)
		return ERR_PTR(-ENODEV);

	 // 2. 在 thermal-zones 下面找到名字为 tz->type 的子节点
	tz_np = of_get_child_by_name(np, tz->type);

	// 3. 释放对 np 的引用
	of_node_put(np);

	// 4. 没找到子节点,就返回错误
	if (!tz_np)
		return ERR_PTR(-ENODEV);

	return tz_np;
}

逻辑解读

(1)找到根节点 “thermal-zones”

  • DTS 中通常会有一个类似这样的定义:thermal-zones {…}
  • of_find_node_by_name(NULL, “thermal-zones”) 就是找到这个根节点。

(2)找到具体的 thermal zone

  • 每个 thermal_zone_device 都有一个 type 字符串,例如 “cpu-thermal”、“gpu-thermal”。
  • of_get_child_by_name(np, tz->type) 就是去找对应的 zone 节点。

(3)引用计数管理

  • of_node_put(np) 用来释放 thermal-zones 根节点的引用(因为后续我们只需要具体的子节点)。

(4)错误处理

  • 如果找不到对应的 zone,就返回 ERR_PTR(-ENODEV)。

(5)返回正确的节点

  • 返回的 tz_np 后续会被用来解析 cooling-maps、trips 等属性。

3.4 thermal_zone_bind_cooling_device

thermal_zone_bind_cooling_device() 用来把 一个 cooling device(降温手段) 绑定到 一个 thermal zone(热区) 的 某个 trip point(温度阈值) 上。绑定完成后,governor 在该 trip 被触发时就能对这个 cooling device 下达调节指令(例如升降风扇档位、CPU/GPU 降频)。

参数解释:
thermal_zone_device *tz:要绑定的 thermal zone
int trip_index:thermal zone 的 trip point 索引(比如第 0 个高温报警点,第 1 个临界点等)
thermal_cooling_device *cdev:要绑定的 cooling device
long upper:冷却设备能用的最高 state(即最多开多大风扇,或最多降多少频)
long lower:冷却设备能用的最低 state(即最小冷却级别)
int weight:当一个 trip 绑定了多个 cooling device 时,用来衡量不同设备的重要性或分配比例

int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
				     int trip_index,
				     struct thermal_cooling_device *cdev,
				     unsigned long upper, unsigned long lower,
				     unsigned int weight)
{
	struct thermal_instance *dev;          // 绑定关系的实例对象:把 tz + cdev + trip 这三者串起来
	struct thermal_instance *pos;          // 遍历时的临时指针(instance)
	struct thermal_zone_device *pos1;      // 用来在全局 tz 链表中查找 tz
	struct thermal_cooling_device *pos2;   // 用来在全局 cdev 链表中查找 cdev
	const struct thermal_trip *trip;       // 指向 tz 中的第 trip_index 个 trip point
	bool upper_no_limit;                   // 标记 upper 是否是“无上限”
	int result;                            // 通用错误码/返回值

	if (trip_index >= tz->num_trips || trip_index < 0)  // 边界检查:trip 下标必须合法
		return -EINVAL;

	trip = &tz->trips[trip_index];        // 取出对应的 trip 描述(只读的 trip 定义)

	// 在全局 thermal_tz_list 里确认 tz 确实已注册(防御性检查)
	list_for_each_entry(pos1, &thermal_tz_list, node) {
		if (pos1 == tz)
			break;
	}
	// 在全局 thermal_cdev_list 里确认 cdev 确实已注册(防御性检查)
	list_for_each_entry(pos2, &thermal_cdev_list, node) {
		if (pos2 == cdev)
			break;
	}

	if (tz != pos1 || cdev != pos2)       // 任意一方没在全局列表中,视为非法
		return -EINVAL;

	/* lower 默认 0,upper 默认 max_state */
	lower = lower == THERMAL_NO_LIMIT ? 0 : lower;   // 如果下限是 NO_LIMIT,等价于 0 档

	if (upper == THERMAL_NO_LIMIT) {                  // 上限 NO_LIMIT:等价于 cdev->max_state
		upper = cdev->max_state;
		upper_no_limit = true;                       // 记下“上限原本是 NO_LIMIT”的语义
	} else {
		upper_no_limit = false;
	}

	if (lower > upper || upper > cdev->max_state)     // 区间必须合法:0 <= lower <= upper <= max_state
		return -EINVAL;

	dev = kzalloc(sizeof(*dev), GFP_KERNEL);          // 分配一个 thermal_instance(绑定对象)
	if (!dev)
		return -ENOMEM;
	dev->tz = tz;                                     // 记录所属 thermal zone
	dev->cdev = cdev;                                 // 记录关联的 cooling device
	dev->trip = trip;                                 // 记录绑定到的 trip
	dev->upper = upper;                               // 绑定时为该 trip 指定的 cdev 上限 state
	dev->upper_no_limit = upper_no_limit;             // 是否来自 NO_LIMIT 的标记(便于后续逻辑)
	dev->lower = lower;                               // 绑定时为该 trip 指定的 cdev 下限 state
	dev->target = THERMAL_NO_TARGET;                  // 初始没有目标 state(由 governor 决定)
	dev->weight = weight;                             // 绑定时设置的权重(governor 会用)

	result = ida_alloc(&tz->ida, GFP_KERNEL);         // 为该 tz 分配一个“本 tz 内唯一”的 instance id
	if (result < 0)
		goto free_mem;

	dev->id = result;                                 // 保存 id
	sprintf(dev->name, "cdev%d", dev->id);            // 生成在 sysfs 下的符号链接名:cdevN
	result =
	    sysfs_create_link(&tz->device.kobj, &cdev->device.kobj, dev->name);
	// 在 /sys/class/thermal/thermal_zoneX/ 下创建到 cooling_deviceY 的符号链接
	// 便于用户从 tz 侧看到绑定了哪些 cdev
	if (result)
		goto release_ida;

	// 在 tz 设备下创建只读属性:cdevN_trip_point(展示该绑定对应的 trip index)
	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;                 // 读取函数:打印 trip 信息
	result = device_create_file(&tz->device, &dev->attr);
	if (result)
		goto remove_symbol_link;

	// 在 tz 设备下创建属性:cdevN_weight(读写;用户可调整该绑定的权重)
	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;   // 所有用户可读,owner 可写
	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;

	// 把该绑定实例挂入双方的实例链表(双向关联),注意加锁与去重
	mutex_lock(&tz->lock);
	mutex_lock(&cdev->lock);
	list_for_each_entry(pos, &tz->thermal_instances, tz_node)
		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
			// 该 tz-trip-cdev 组合已经存在,防止重复绑定
			result = -EEXIST;
			break;
		}
	if (!result) {
		// 尾插入 tz 的实例链表和 cdev 的实例链表(各维护一份)
		list_add_tail(&dev->tz_node, &tz->thermal_instances);
		list_add_tail(&dev->cdev_node, &cdev->thermal_instances);
		atomic_set(&tz->need_update, 1); // 标记 tz 需要更新(触发后续 governor 评估)
	}
	mutex_unlock(&cdev->lock);
	mutex_unlock(&tz->lock);

	if (!result)                                   // 成功到这里则返回 0
		return 0;

	// 下面是失败时的清理回滚路径(按创建的逆序回滚)
	device_remove_file(&tz->device, &dev->weight_attr); // 移除权重属性文件
remove_trip_file:
	device_remove_file(&tz->device, &dev->attr);        // 移除 trip_point 属性文件
remove_symbol_link:
	sysfs_remove_link(&tz->device.kobj, dev->name);     // 移除 sysfs 符号链接
release_ida:
	ida_free(&tz->ida, dev->id);                        // 释放本 tz 内分配的 instance id
free_mem:
	kfree(dev);                                         // 释放 instance 内存
	return result;                                      // 返回错误码
}
EXPORT_SYMBOL_GPL(thermal_zone_bind_cooling_device);    // 导出符号,供其它模块使用

关键点:
(1)thermal_instance 的角色
它是 “关系对象”:把 tz(热区)、trip(阈值)和 cdev(冷却手段)三者绑定起来,并在该绑定上携带 上下限 state、权重、目标 state 等运行时信息。这样一个 tz 可以绑定多个不同的 cdev;同一个 cdev 也可以被多个 tz 复用。

(2)上下限处理(THERMAL_NO_LIMIT)
upper = THERMAL_NO_LIMIT → 解释为 upper = cdev->max_state,并记录 upper_no_limit = true(以保留“原本没有上限”的语义)。
lower = THERMAL_NO_LIMIT → 解释为 lower = 0。
区间校验:0 <= lower <= upper <= max_state。

(3)sysfs 可视化
在 thermal_zoneX 下创建到 cooling_deviceY 的 符号链接:cdevN,便于用户空间查看绑定关系。
暴露两个属性:

  • cdevN_trip_point(只读):告诉你这个绑定挂在哪个 trip 上。
  • cdevN_weight(读写):可在线调整 governor 使用该 cdev 的权重(影响调度决策)。

结构图:
在这里插入图片描述

3.5 thermal_zone_unbind_cooling_device

thermal_zone_unbind_cooling_device 说明:
① 作用:解除 cooling device 与 thermal zone 某个 trip point 的绑定关系。
② 调用场景:通常在 thermal zone 的 .unbind 回调中调用。
③ 返回值:0 成功,负数失败(如设备不存在)。

/**
 * thermal_zone_unbind_cooling_device() - unbind a cooling device from a
 *					  thermal zone.
 * @tz:		pointer to a struct thermal_zone_device.
 * @trip_index:	indicates which trip point the cooling devices is
 *		associated with in this thermal zone.
 * @cdev:	pointer to a struct thermal_cooling_device.
 *
 * This interface function unbind a thermal cooling device from the certain
 * trip point of a thermal zone device.
 * This function is usually called in the thermal zone device .unbind callback.
 *
 * Return: 0 on success, the proper error value otherwise.
 */
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
				       int trip_index,
				       struct thermal_cooling_device *cdev)
{
	struct thermal_instance *pos, *next; // pos/next 用于遍历 thermal_instance 链表
	const struct thermal_trip *trip; // trip 指向对应的 trip point

	// 加锁,保护 thermal zone 和 cooling device 的内部状态。
	// 保证解绑过程中不会有并发修改。
	mutex_lock(&tz->lock);
	mutex_lock(&cdev->lock);
	// 获取 tz 中对应索引的 trip point(即解绑的目标 trip)。
	trip = &tz->trips[trip_index];
	// 遍历 tz->thermal_instances 链表(保存所有绑定的 thermal_instance)。
	// list_for_each_entry_safe是个宏定义,用来遍历
	list_for_each_entry_safe(pos, next, &tz->thermal_instances, tz_node) {
		// 如果 thermal_instance 同时满足:
		// (1) 属于当前 tz
		// (2) trip 相同
		// (3) cooling device 相同
		if (pos->tz == tz && pos->trip == trip && pos->cdev == cdev) {
			list_del(&pos->tz_node); // list_del(&pos->tz_node):从 thermal zone 的链表里删掉。
			list_del(&pos->cdev_node); // list_del(&pos->cdev_node):从 cooling device 的链表里删掉。
			// 解锁,然后跳转到 unbind 执行资源清理。
			mutex_unlock(&cdev->lock);
			mutex_unlock(&tz->lock);
			goto unbind;
		}
	}
	// 如果遍历结束没找到匹配的绑定对象,则返回 -ENODEV(设备不存在)。
	mutex_unlock(&cdev->lock);
	mutex_unlock(&tz->lock);

	return -ENODEV;

unbind:
	// 移除 sysfs 接口(weight 属性、trip 属性)
	device_remove_file(&tz->device, &pos->weight_attr); 
	device_remove_file(&tz->device, &pos->attr);
	// 移除 sysfs 符号链接(之前 bind 时创建的 cdevX 软链接)
	sysfs_remove_link(&tz->device.kobj, pos->name);
	// 释放 thermal instance 的 ID
	ida_free(&tz->ida, pos->id);
	// 释放 thermal_instance 内存
	kfree(pos);
	return 0;
}
EXPORT_SYMBOL_GPL(thermal_zone_unbind_cooling_device);

【对比】
bind 时会:

  • 分配 thermal_instance
  • 链接到 tz 和 cdev 的链表
  • 在 sysfs 里创建属性和符号链接

unbind 时会:

  • 从链表中删除 thermal_instance
  • 删除 sysfs 节点和链接
  • 回收 ID 和内存

4 设备截图

基于rk Android设备截图:
在这里插入图片描述

// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2025 Oplus. All rights reserved. */ #include <linux/thermal.h> #include <trace/hooks/thermal.h> #include <linux/moduleparam.h> #include <linux/module.h> static int irq_wakeable_value = 0; module_param_named(irq_wakeable, irq_wakeable_value, int, 0644); static void oplus_thermal_pm_notify(void *unused, struct thermal_zone_device *tz, int *irq_wakeable) { *irq_wakeable = irq_wakeable_value; } /*static void disable_cdev_stats(void *unused, struct thermal_cooling_device *cdev, bool *disable) { *disable = true; } */ static int __init oplus_thermal_vendor_hook_driver_init(void) { int ret; //ret = register_trace_android_vh_disable_thermal_cooling_stats( // disable_cdev_stats, NULL); //if (ret) // pr_err("Failed to register disable cdev stats hook, err:%d\n", // ret); ret = register_trace_android_vh_thermal_pm_notify_suspend( oplus_thermal_pm_notify, NULL); if (ret) pr_err("Failed to register thermal_pm_notify hook, err:%d\n", ret); return 0; } static void __exit oplus_thermal_vendor_hook_driver_exit(void) { //unregister_trace_android_vh_disable_thermal_cooling_stats( // disable_cdev_stats, NULL); unregister_trace_android_vh_thermal_pm_notify_suspend( oplus_thermal_pm_notify, NULL); } #if IS_MODULE(CONFIG_OPLUS_THERMAL_VENDOR_HOOK) module_init(oplus_thermal_vendor_hook_driver_init); #else pure_initcall(oplus_thermal_vendor_hook_driver_init); #endif module_exit(oplus_thermal_vendor_hook_driver_exit); MODULE_DESCRIPTION("Oplus Thermal Vendor Hooks Driver"); MODULE_LICENSE("GPL"); cpu\thermal\oplus_thermal_vendor_hooks.c
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值