# Thermal 系统

## Thermal 系统概述

Thermal 系统是一个用于监控和管理设备温度的机制，旨在防止系统因过热而发生硬件损坏或性能下降。它通过读取温度传感器的数据，并根据预定义的阈值采取不同的响应措施，如调整设备性能或触发热降频等操作。该系统为设备提供温度控制和保护功能，确保硬件在安全的温度范围内运行。

## Thermal 系统功能描述

### Thermal 系统的典型应用

在板端系统上有三个温度传感器，分别用于显示 DDR、BPU 以及 CPU 的温度。thermal 系统相关的典型应用示例如下：

- 降频：通过减少 CPU 或 GPU 频率来降低功耗，从而减少热量的产生。
- 风扇加速：如果设备有风扇，系统可以通过调整风扇转速来加强散热。
- 系统关闭或重启：当温度过高且无法通过降频等措施控制时，系统可能会进行自动关闭或重启。

### Thermal 系统功能原理

Thermal 系统的核心原理是通过读取硬件系统中的温度传感器值，并与预设的温度阈值进行对比。当温度达到某个临界值时，系统会启动响应机制，采取措施防止硬件过热。

Thermal 系统功能原理如下图所示：

![thermal_function_principle](./_static/_images/Thermal_system_zh_CN/thermal_function_principle_zh_CN.jpg)

### Thermal 系统工作方式

Thermal 系统的工作方式通常分为以下几个步骤：

1. **温度采集**：通过硬件温度传感器获取实时温度数据。这些传感器可以是内建于处理器的温度传感器，或者是外部的热电偶或热敏电阻。
2. **阈值设置**：内核或用户空间应用定义温度的安全范围和响应措施。通常包括高温阈值（Critical）、中等温度阈值（Warning）等。
3. **响应策略**：当温度超出预设阈值时，系统会启动相应的策略。响应可以是：
   - 降低频率
   - 增加风扇转速
   - 关闭不必要的硬件或服务
   - 触发热保护（如系统关机）
4. **温度调整**：根据策略调整后，系统会继续监测温度变化，并重复上述过程。

## Thermal 驱动代码

### Thermal 相关代码路径

```bash
kernel/drivers/thermal/thermal_of.c
kernel/drivers/thermal/thermal_core.c
kernel/include/linux/thermal.h
```

### Thermal 设备树配置

Thermal 系统的设备树定义位于 BSP 包的 kernel 文件夹下 的 `arch/arm64/boot/dts/hobot/x5.dtsi` 文件内：

```bash
thermal-zones {
thermal_cpu: thermal-cpu {
polling-delay-passive = <1000>;
polling-delay = <1000>;

		thermal-sensors = <&pvt 2>;

		trips {
			cpu_alert0: cpu-alert0 {
				temperature = <110000>;
				hysteresis = <500>;
				type = "passive";
			};
			cpu_alert1: cpu-alert1 {
				temperature = <95000>;
				hysteresis = <1000>;
				type = "passive";
			};
			cpu_crit: cpu-crit {
				temperature = <110000>;
				hysteresis = <0>;
				type = "critical";
			};
		};

		cooling-maps {
			cpu_map0: cpu-map0 {
				trip = <&cpu_alert1>;
				cooling-device =
						<&cpu_0 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_1 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_2 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_3 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_4 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_5 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_6 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>,
						<&cpu_7 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
			cpu_map1: cpu-map1 {
				trip = <&cpu_alert1>;
				cooling-device = <&bpu THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
			cpu_map2: cpu-map2 {
				trip = <&cpu_alert1>;
				cooling-device = <&gc8000 THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
		};
	};

	thermal_ddr: thermal-ddr {
		polling-delay-passive = <1000>;
		polling-delay = <1000>;

		thermal-sensors = <&pvt 0>;

		trips {
			ddr_alert0: ddr-alert0 {
				temperature = <95000>;
				hysteresis = <1000>;
				type = "passive";
			};
		};

		cooling-maps {
			ddr_map0: ddr-map0 {
				trip = <&ddr_alert0>;
				cooling-device = <&ddrc_freq THERMAL_NO_LIMIT THERMAL_NO_LIMIT>;
			};
		};
	};
};
```

这段设备树描述了 CPU 和 DDR 热区的温度管理配置，主要包含以下几部分：

- **温度传感器**：为每个热区指定了相关的温度传感器实例。
- **温度阈值（trips）**：定义了不同的温度阈值（`cpu_alert0`、`cpu_alert1` 和 `cpu_crit` 等），每个阈值在达到时触发不同的行为（如被动冷却或临界保护）。
- **冷却映射**：定义了当温度阈值触发时，如何启用不同的冷却设备（如 CPU 核心、BPU、GC8000、DDR 等）。

下面是详细分析：

- **thermal-zones**
`thermal-zones` 节点列出了所有的热区配置。在这个文件中，有两个热区：thermal_cpu（对应 CPU 的温度管理）和 thermal_ddr（对应 DDR 内存的温度管理）。

- **thermal_cpu: thermal-cpu**
这个部分定义了与 CPU 相关的热区，包含了以下关键配置：

  - **polling-delay-passive** 和 **polling-delay**：这两个属性设定了热区的轮询延时，单位是毫秒。`polling-delay-passive` 是在处于被动模式下的轮询延时，而 `polling-delay` 是在正常模式下的轮询延时。这里它们都设置为 1000 毫秒（即 1 秒）。

  - **thermal-sensors**：这个属性定义了与此热区相关的温度传感器。在此处，`< &pvt 2 >` 表示与 CPU 温度监控相关的传感器，它指向一个名为 `pvt` 的传感器，并且选择了其第二个实例。

- **trips (CPU)**
`trips` 描述了不同的温度阈值和行为（触发条件），即温度达到某个水平时系统如何反应。在这个文件中，有三个阈值：

  - **cpu_alert0**：触发温度为 110,000 毫度（110°C），属于被动类型（`passive`）。这表示当 CPU 温度超过 110°C 时，采取被动散热措施，减少功耗等。

  - **cpu_alert1**：触发温度为 95,000 毫度（95°C），也是被动类型。低于 `cpu_alert0`，但仍会采取类似的被动散热措施。

  - **cpu_crit**：临界温度为 110,000 毫度（110°C），触发类型为临界（`critical`）。通常，临界温度会触发更强的热保护措施，如关闭 CPU 或其他硬件。

- **cooling-maps**
`cooling-maps` 描述了在特定温度触发条件下，如何调节冷却设备。具体有三个冷却映射：

  - **cpu_map0**：当 `cpu_alert1` 温度阈值被触发时，会激活以下冷却设备：
    - 8 个 CPU 核心的冷却设备（CPU 0 到 CPU 7）。这里的 `THERMAL_NO_LIMIT` 表示没有具体的温度限制。

  - **cpu_map1**：当 `cpu_alert1` 温度阈值被触发时，会激活名为 `bpu` 的冷却设备。`THERMAL_NO_LIMIT` 同样表示没有温度限  制。

  - **cpu_map2**：当 `cpu_alert1` 温度阈值被触发时，会激活名为 `gc8000` 的冷却设备。

- **thermal_ddr: thermal-ddr**
这个部分定义了与 DDR 内存相关的热区。与 `thermal_cpu` 类似，包含了温度轮询和传感器的设置，但它只定义了一个温度阈值。

  - **thermal-sensors**：与 DDR 相关的温度传感器为 `< &pvt 0 >`，它指向名为 `pvt` 的第一个传感器实例。

- **trips (DDR)**
DDR 热区也定义了一个温度阈值：

  - **ddr_alert0**：触发温度为 95,000 毫度（95°C），类型为被动（`passive`）。当 DDR 温度超过此阈值时，采取被动的散热措施。

- **cooling-maps**
  - **ddr_map0**：当 `ddr_alert0` 被触发时，激活名为 `ddrc_freq` 的冷却设备。同样，`THERMAL_NO_LIMIT` 表示没有具体的温度限制。

这段设备树代码可以被内核解析，指导系统在不同的温度条件下有效管理硬件温度，避免过热问题，并确保硬件在安全的温度范围内工作。

### Thermal 内核配置

Thermal 系统内核的相关配置可以在 menuconfig 中看到，当前系统中配置如下：

```bash
--- Thermal drivers
[ ]   Thermal netlink management
[ ]   Thermal state transition statistics
(0)   Emergency poweroff delay in milli-seconds
[*]   Expose thermal sensors as hwmon device
[*]   APIs to parse thermal data out of device tree
[*]   Enable writable trip points
      Default Thermal governor (step_wise)  --->
[ ]   Fair-share thermal governor
-*-   Step_wise thermal governor
[ ]   Bang Bang thermal governor
[*]   User_space thermal governor
[*]   Generic cpu cooling support
[*]     CPU frequency cooling device
[*]   Generic device cooling support
[ ]   Thermal emulation mode support
< >   Generic Thermal MMIO driver
< >   Generic ADC based thermal sensor
```

以下是当前系统中已经开启的选项及其解析：

- **Expose thermal sensors as hwmon device**
  - 启用该选项后，系统的热传感器将作为硬件监控（**hwmon**）设备暴露给用户空间。通过这个功能，用户可以通过 `/sys/class/hwmon/` 目录访问温度传感器数据。例如，`lm_sensors` 等工具可以使用这些数据来监控系统的温度情况，便于调试和优化系统的热管理。

- **APIs to parse thermal data out of device tree**
  - 启用该选项后，内核将具备解析设备树中热管理相关数据的能力。设备树是描述硬件的配置文件，通常在 ARM 架构的系统中使用。通过启用这个选项，内核可以从设备树中读取有关温度传感器、热阈值、冷却设备等信息，并据此做出相应的热管理操作。

- **Enable writable trip points**
  - 启用该选项后，系统允许用户在运行时修改热管理的 **trip points**（温度阈值）。**Trip points** 是预设的温度阈值，超过这些阈值时，系统会采取措施（如降低频率或启动风扇）。启用此选项后，用户可以通过 **sysfs** 接口动态修改这些阈值，以更好地适应不同的工作负载和环境条件。

- **Step_wise thermal governor**
  - 这是系统默认的热管理调度器（thermal governor）。`step_wise` 调度器会根据温度的变化逐步调整冷却策略。例如，当温度逐渐上升时，系统会采取逐步增加冷却措施的方式。相较于简单的 `bang-bang` 或其他策略，`step_wise` 更加细致，能够提供平稳的温度控制。

- **User_space thermal governor**
  - 启用该选项后，系统允许用户空间应用程序控制热管理策略。这意味着用户可以通过编写自己的应用程序来动态调整温度控制措施，例如启动/停止冷却设备或修改冷却策略等。这个功能适用于需要自定义热管理行为的系统，尤其是在一些嵌入式或高性能应用中，可以根据特定需求调节冷却方式。

- **Generic cpu cooling support**
  - 启用该选项后，系统将支持通用的 CPU 冷却机制，通常通过调整 CPU 的频率、调整功耗或启动/停止冷却设备来降低温度。这项功能对于需要高效管理 CPU 温度的系统非常重要，尤其是在负载较高时，能够通过减少功耗来避免过热。

- **CPU frequency cooling device**
  - 启用该选项后，系统会支持基于 **CPU 频率** 调整的冷却设备。这意味着当温度升高时，系统可以动态降低 CPU 的频率，以减少功耗和热量，从而帮助系统降低温度。此功能通常与 CPU 动态频率调节（DVFS）相关，能够在高温情况下自动降低 CPU 性能来保护硬件。

- **Generic device cooling support**
  - 启用该选项后，系统将支持通用的设备冷却机制。这包括 CPU 以外的设备（例如 GPU、内存控制器、硬盘等）的冷却措施。系统会根据温度变化调整相关设备的功耗或启动冷却装置，以提高系统的整体热管理能力。

## Thermal 系统的功能和使用

Linux Thermal 是 Linux 系统下温度控制相关的模块，主要用来控制系统运行过程中芯片产生的热量，使芯片温度和设备外壳温度维持在一个安全、舒适的范围。

整个 Thermal 框架可以分为四部分：

- **Thermal Driver**： 负责将获取温度设备，注册成 `struct thermal_zone_device`，Thermal Zone Device 本质是待获取温度设备的抽象，本板端系统上有两个 thermal zone，分别是 `thermal_zone0` 和 `thermal_zone1`。
- **Thermal Governor**： 则负责如何控制温度，注册成 `struct thermal_governor`，比如 Step Wise、User space 等等。Thermal Governor 本质是控制温度策略。
- **Thermal Cooling**： 负责将控制温度设备，注册成 `struct thermal_cooling_device`，实际就是需要降温的设备，在板端系统上一共有四个 cooling device：
  - cooling_device0: cpu
  - cooling_device1: bpu
  - cooling_device2: gpu
  - cooling_device3: ddr
其中，cooling 设备 DDR 与 `thermal_zone0` 关联，cooling 设备 CPU/BPU/GPU 与 `thermal_zone1` 关联。
- **Thermal Core**： 负责把 governor、cooling device、zone device 等部分关联在一起，同时提供了用户空间 sysfs 节点等通用功能，Thermal Core 是 thermal的核心。

以上模块的信息和控制状态都可以在 `/sys/class/thermal` 目录下获取。

Thermal 的工作流程是通过 Thermal Driver 获取温度，然后经过 Thermal Governor 决策，最后通过 Thermal Cooling 执行温度控制。这几个模块之间的关系可以描述为下图：

![thermal_kernel_framwork](./_static/_images/Thermal_system/thermal_kernel_framwork.png)

加入设备组件后，Thermal 框架与设备组件之间的关系，以及温度变化时的数据处理流程如下所示：

![thermal_kernel_framwork](./_static/_images/Thermal_system_zh_CN/thermal_framework_zh_CN.png)

### Thermal 驱动关键结构体说明

#### thermal_zone_device

```c
struct thermal_zone_device {
	int id;
	char type[THERMAL_NAME_LENGTH];
	struct device device;
	struct attribute_group trips_attribute_group;
	struct thermal_attr *trip_temp_attrs;
	struct thermal_attr *trip_type_attrs;
	struct thermal_attr *trip_hyst_attrs;
	enum thermal_device_mode mode;
	void *devdata;
	struct thermal_trip *trips;
	int num_trips;
	unsigned long trips_disabled;	/* bitmap for disabled trips */
	unsigned long passive_delay_jiffies;
	unsigned long polling_delay_jiffies;
	int temperature;
	int last_temperature;
	int emul_temperature;
	int passive;
	int prev_low_trip;
	int prev_high_trip;
	atomic_t need_update;
	struct thermal_zone_device_ops *ops;
	struct thermal_zone_params *tzp;
	struct thermal_governor *governor;
	void *governor_data;
	struct list_head thermal_instances;
	struct ida ida;
	struct mutex lock;
	struct list_head node;
	struct delayed_work poll_queue;
	enum thermal_notify_event notify_event;
};
```

`struct thermal_zone_device` 是一个用于描述热区设备的结构体，包含了管理和监控热管理相关信息的各个字段。热区设备（thermal zone）是操作系统中的一种抽象，用于表示具有热管理功能的硬件区域。该结构体涉及到温度传感器、热管理策略、温控阈值和各种系统参数等。

下面是对各个成员变量的详细解析：

1. **`id`**:
   - 类型：`int`
   - 该字段用于为每个热区分配一个唯一的标识符，用于区分不同的热区。

2. **`type`**:
   - 类型：`char[THERMAL_NAME_LENGTH]`
   - 该字段表示热区设备的类型名称，通常是一个字符串，指示热区设备的类型（例如 CPU 热区，GPU 热区等）。

3. **`device`**:
   - 类型：`struct device`
   - 该字段表示热区设备本身，它是一个 `device` 结构体，允许与设备模型进行交互，便于管理热区相关的硬件。

4. **`trips_attribute_group`**:
   - 类型：`struct attribute_group`
   - 用于管理热区的属性组（例如通过 sysfs 访问）。`trips_attribute_group` 包含与温度阈值（trip points）相关的属性。

5. **`trip_temp_attrs`**, **`trip_type_attrs`**, **`trip_hyst_attrs`**:
   - 类型：`struct thermal_attr *`
   - 这些字段分别指向与热区温度阈值相关的属性：
     - `trip_temp_attrs`：阈值温度的属性；
     - `trip_type_attrs`：阈值类型的属性（如高温、低温等）；
     - `trip_hyst_attrs`：阈值滞后的属性，用于控制温度上下限的间隔。

6. **`mode`**:
   - 类型：`enum thermal_device_mode`
   - 表示当前热区的工作模式。热区设备的模式决定了它如何响应温度变化（如工作、休眠、关机等模式）。

7. **`devdata`**:
   - 类型：`void *`
   - 指向与热区设备相关的私有数据，驱动程序可以使用该字段来存储特定的设备私有数据。

8. **`trips`**:
   - 类型：`struct thermal_trip *`
   - 该字段是一个指向 `struct thermal_trip` 结构体的指针数组，用于存储热区的各个温度阈值（trip points）。每个阈值定义了一个温度点，当系统温度达到这些点时会触发特定的操作（如启用风扇、降低频率等）。

9. **`num_trips`**:
   - 类型：`int`
   - 表示当前热区设备支持的温度阈值的数量。每个阈值可能会触发不同的冷却措施。

10. **`trips_disabled`**:
    - 类型：`unsigned long`
    - 位图，用于表示哪些温度阈值已被禁用。如果某个阈值被禁用，则不会触发与该阈值相关的任何事件。

11. **`passive_delay_jiffies`**:
    - 类型：`unsigned long`
    - 当系统执行被动冷却时，需要等待的延迟时间，以 jiffies 为单位。被动冷却通常是指通过降低 CPU 或其他设备的频率来降低温度。

12. **`polling_delay_jiffies`**:
    - 类型：`unsigned long`
    - 用于检查温度是否超出某些阈值的延迟时间，以 jiffies 为单位。如果系统是中断驱动的，这个值可能为 0。

13. **`temperature`**:
    - 类型：`int`
    - 当前温度。这个字段主要供核心代码使用，驱动程序应该使用 `thermal_zone_get_temp()` 函数来获取当前温度。

14. **`last_temperature`**:
    - 类型：`int`
    - 上一次读取的温度值。

15. **`emul_temperature`**:
    - 类型：`int`
    - 如果启用了 `CONFIG_THERMAL_EMULATION`（热管理仿真），则这个字段表示模拟的温度值。它在开发或测试时非常有用，尤其是在没有真实硬件的情况下。

16. **`passive`**:
    - 类型：`int`
    - 如果设备已经通过某个被动阈值，则该字段为 1；否则为 0。被动冷却通常是通过调整设备频率来降低温度。

17. **`prev_low_trip`**, **`prev_high_trip`**:
    - 类型：`int`
    - 这些字段分别存储当被动冷却阈值被触发时的温度范围。`prev_low_trip` 是上一个低温阈值，`prev_high_trip` 是上一个高温阈值。

18. **`need_update`**:
    - 类型：`atomic_t`
    - 用于标记热区设备是否需要更新。该字段通常由核心代码设置为 1，如果设备的温度或状态需要更新，则触发更新。

19. **`ops`**:
    - 类型：`struct thermal_zone_device_ops *`
    - 指向热区设备操作函数集合的指针。这些操作函数定义了热区设备的行为，包括如何获取温度、设置模式、触发阈值等。

20. **`tzp`**:
    - 类型：`struct thermal_zone_params *`
    - 指向热区参数结构体的指针。这个结构体包含了与热区相关的其他配置参数。

21. **`governor`**:
    - 类型：`struct thermal_governor *`
    - 指向热区管理器的指针。热区管理器负责根据当前温度和热区策略决定如何调整温度（例如通过改变 CPU 或其他硬件的频率来实现冷却）。

22. **`governor_data`**:
    - 类型：`void *`
    - 用于存储与热区管理器相关的私有数据。

23. **`thermal_instances`**:
    - 类型：`struct list_head`
    - 热区的所有实例的链表。每个实例表示热区设备中的一个具体的硬件或功能。

24. **`ida`**:
    - 类型：`struct ida`
    - `IDA`（ID allocator）结构体，用于生成唯一的 ID，通常用于为每个热区的冷却设备分配唯一 ID。

25. **`lock`**:
    - 类型：`struct mutex`
    - 用于保护 `thermal_instances` 链表的锁，防止多线程或中断竞争。

26. **`node`**:
    - 类型：`struct list_head`
    - 用于将 `thermal_zone_device` 添加到全局热区列表中的节点。`thermal_tz_list` 是一个包含所有热区设备的链表。

27. **`poll_queue`**:
    - 类型：`struct delayed_work`
    - 用于延迟工作任务的队列，在周期性检查温度或触发阈值时使用。

28. **`notify_event`**:
    - 类型：`enum thermal_notify_event`
    - 上一个通知事件的类型，用于指示温度触发了什么样的通知事件（例如温度过高等）。

上述成员中 `ops` 是对该 Thermal Zone 操作的抽象，`governor` 是该 Thermal Zone 所使用的调温策略，`thermal_instances` 是该 Thermal Zone 下的 Cooling Device 列表。

#### thermal_governor

```c
struct thermal_governor {
	char name[THERMAL_NAME_LENGTH];
	int (*bind_to_tz)(struct thermal_zone_device *tz);
	void (*unbind_from_tz)(struct thermal_zone_device *tz);
	int (*throttle)(struct thermal_zone_device *tz, int trip);
	struct list_head	governor_list;
};
```

结构体 `thermal_governor` 用于表示一个“热管制器”（thermal governor）及其与热区（thermal zone）设备之间的关系。热管制器是一种用于控制温度并对系统进行温控调节的机制。它通过与热区设备（`thermal_zone_device`）和相应的温度阈值（trip points）进行交互，帮助避免系统过热或确保温度维持在一定范围内。该结构体包含以下成员：

1） `name` (char name[THERMAL_NAME_LENGTH])

```c
char name[THERMAL_NAME_LENGTH];
```

- **功能**：存储热管制器的名称。`THERMAL_NAME_LENGTH` 是名称的最大长度。这个名称用于标识不同的热管制器（例如“用户模式”或“硬件控制”）。

2） `bind_to_tz` (int (*bind_to_tz)(struct thermal_zone_device *tz))

```c
int (*bind_to_tz)(struct thermal_zone_device *tz);
```

- **功能**：一个回调函数，用于在热管制器与热区设备（`thermal_zone_device`）绑定时调用。
- **参数**：
  - `tz`：指向要绑定的热区设备的指针。
- **返回值**：如果返回 0，表示绑定成功；如果返回非零值，表示绑定失败。
- **说明**：这个回调函数是热管制器与热区设备关联的过程。成功绑定后，热管制器将能够控制该热区设备的温度。

3） `unbind_from_tz` (void (*unbind_from_tz)(struct thermal_zone_device *tz))

```c
void (*unbind_from_tz)(struct thermal_zone_device *tz);
```

- **功能**：一个回调函数，用于在热管制器与热区设备解除绑定时调用。
- **参数**：
  - `tz`：指向已解除绑定的热区设备的指针。
- **返回值**：该函数没有返回值。
- **说明**：当热管制器不再需要控制某个热区设备时，调用此函数来解除绑定。

4） `throttle` (int (*throttle)(struct thermal_zone_device *tz, int trip))

```c
int (*throttle)(struct thermal_zone_device *tz, int trip);
```

- **功能**：一个回调函数，用于在温度触发某个阈值时（无论温度是否已达到阈值），进行热管制的调节操作。通常，这用于在温度接近或超过某个阈值时，调节系统的功率或性能。
- **参数**：
  - `tz`：指向热区设备的指针。
  - `trip`：热区设备的温度阈值索引，表示当前温度是否接近或已超过某个阈值。
- **返回值**：返回一个整数，通常是成功或错误码，0 表示成功，其他值表示失败。
- **说明**：该回调函数通过给定的阈值（`trip`）调整系统的行为。例如，降低 CPU 频率、调整风扇速度等。

5） `governor_list` (struct list_head governor_list)

```c
struct list_head governor_list;
```

- **功能**：一个链表节点，用于将不同的热管制器添加到全局的热管制器列表中。
- **说明**：该结构成员是 `list_head` 类型，它是 Linux 内核中实现链表的数据结构，允许热管制器在系统中作为链表元素进行管理和调度。

总的来说，`struct thermal_governor` 是一个用来管理和控制温度的结构体，通常在温度管理系统中与热区设备和温度阈值密切合作。它通过以下方式实现热管制：
1） **绑定/解绑**：通过 `bind_to_tz` 和 `unbind_from_tz` 回调函数，热管制器可以与热区设备绑定或解绑。
2） **温度调节**：通过 `throttle` 回调函数，热管制器根据温度阈值（`trip`）对设备进行调节，确保温度在安全范围内。

#### thermal_bind_params（Cooling Device）

结构体 `thermal_bind_params` ，用于描述 热区（thermal zone）与冷却设备（cooling device）之间的绑定参数。在系统的热管理框架中，热区和冷却设备可能需要根据不同的温度阈值（trip points）进行绑定，以有效地控制设备温度。该结构体就是对降温设备的抽象，对风扇设备就是不同的转速，对 CPU、DDR、GPU 就是不同的电压或者频率。

```c
struct thermal_bind_params {
    struct thermal_cooling_device *cdev;
    int weight;
    int trip_mask;
    unsigned long *binding_limits;
    int (*match)(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev);
};
```

1） `cdev` (`struct thermal_cooling_device *cdev`)

```c
struct thermal_cooling_device *cdev;
```

- **类型**：指向 `thermal_cooling_device` 类型的指针。
- **功能**：指向一个冷却设备（cooling device）。冷却设备是用来调节热区温度的硬件组件或软件控制机制。通过这个指针，系统可以操作冷却设备，启动、停止或者调整其冷却能力。

2） `weight` (`int weight`)

```c
int weight;
```

- **类型**：整数类型。
- **功能**：表示冷却设备对热区的冷却效果的权重。权重是由系统特性决定的，表示冷却设备的冷却能力。权重越高，冷却效果越好。例如，权重为某冷却设备的两倍的设备将比权重较低的设备更有效地降低温度。
- **说明**：冷却设备的 `weight` 是相对的。如果有多个冷却设备，它们的权重将帮助系统决定在某些温度阈值下哪个设备应该优先激活。

3） `trip_mask` (`int trip_mask`)

```c
int trip_mask;
```

- **类型**：整数类型。
- **功能**：这是一个位掩码（bitmask），用于指定冷却设备与热区之间的绑定关系，特别是对于特定的温度阈值（trip point）。它表明在某些温度阈值下，哪些冷却设备应该启用或者激活。
- **说明**：每个 `trip_mask` 中的每一位代表一个温度阈值对应的冷却设备状态。可以通过设置位掩码来确定该冷却设备在不同的温度范围下应该如何工作。

4） `binding_limits` (`unsigned long *binding_limits`)

```c
unsigned long *binding_limits;
```

- **类型**：指向 `unsigned long` 类型的指针。
- **功能**：这是一个数组，表示冷却设备与热区绑定时的冷却状态限制。该数组的大小为 `2 * thermal_zone.number_of_trip_points`，即每个温度阈值都有一个对应的限制范围。每个温度阈值会关联一对状态限制（下限和上限）。例如，在某个温度阈值下，冷却设备的工作状态可能被限制在一定的范围内。
- **说明**：如果 `binding_limits` 为 `NULL`，则表示没有对冷却设备的状态进行限制，所有温度阈值上的冷却设备都没有任何限制。

5） `match` (`int (*match)(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev)`)

```c
int (*match)(struct thermal_zone_device *tz, struct thermal_cooling_device *cdev);
```

- **类型**：一个函数指针，指向一个匹配函数。
- **功能**：这是一个回调函数，用于匹配冷却设备与热区设备是否适配。具体来说，`match` 函数会根据当前热区（`tz`）和冷却设备（`cdev`）的特性，返回一个整数值，通常是 `0` 表示匹配成功，非 `0` 表示匹配失败。
- **说明**：这个函数使得系统能够根据冷却设备与热区设备的具体特性，决定是否绑定它们。

#### thermal_instance

```c
struct thermal_instance {
	int id;
	char name[THERMAL_NAME_LENGTH];
	struct thermal_zone_device *tz;
	struct thermal_cooling_device *cdev;
	int 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 tz_node; /* node in tz->thermal_instances */
	struct list_head cdev_node; /* node in cdev->thermal_instances */
	unsigned int weight; /* The weight of the cooling device */
};
```

结构体 `thermal_instance`，用于描述 **冷却设备在特定温度阈值（trip point）下的行为**。它主要用于系统的热管理机制中，表示一个冷却设备在某个特定的热区（thermal zone）和温度阈值下的状态和配置。

1） `id` (`int id`)
```c
int id;
```
- **类型**：整数类型。
- **功能**：唯一标识一个 `thermal_instance`。它可以用来区分不同的冷却设备实例，即使它们处在同一个热区和相同的温度阈值下。

2） `name` (`char name[THERMAL_NAME_LENGTH]`)
```c
char name[THERMAL_NAME_LENGTH];
```
- **类型**：字符数组，用来存储实例的名称。
- **功能**：该名称用于标识当前冷却设备实例。`THERMAL_NAME_LENGTH` 是一个宏，定义了名称的最大长度。名称一般用于调试或日志中，帮助开发者理解当前的冷却设备实例。

3） `tz` (`struct thermal_zone_device *tz`)
```c
struct thermal_zone_device *tz;
```
- **类型**：指向 `thermal_zone_device` 结构的指针。
- **功能**：指向该冷却设备所绑定的热区（thermal zone）。热区代表一个监控和控制温度的区域，可能是一个物理区域或系统中的某个硬件组件。这个成员表示冷却设备在某个热区的行为。

4） `cdev` (`struct thermal_cooling_device *cdev`)
```c
struct thermal_cooling_device *cdev;
```
- **类型**：指向 `thermal_cooling_device` 结构的指针。
- **功能**：指向冷却设备（cooling device）。冷却设备通常是硬件组件（如风扇、散热片等）或软件控制机制，用于降低热区的温度。

5） `trip` (`int trip`)
```c
int trip;
```
- **类型**：整数类型。
- **功能**：表示与该冷却设备实例关联的温度阈值（trip point）的索引。热区通常有多个温度阈值，每个阈值对应不同的冷却策略。`trip` 用来指定当前实例针对哪个阈值的冷却行为。

6） `initialized` (`bool initialized`)
```c
bool initialized;
```
- **类型**：布尔类型。
- **功能**：表示该冷却设备实例是否已被初始化。如果为 `true`，表示该实例已经初始化并准备使用。如果为 `false`，则说明该实例还没有初始化。

7） `upper` (`unsigned long upper`)
```c
unsigned long upper;
```
- **类型**：无符号长整型。
- **功能**：表示该冷却设备在指定温度阈值下的最高冷却状态。冷却状态通常表示设备的工作强度（例如，风扇的转速）。`upper` 表示该冷却设备能达到的最高状态。

8） `lower` (`unsigned long lower`)
```c
unsigned long lower;
```
- **类型**：无符号长整型。
- **功能**：表示该冷却设备在指定温度阈值下的最低冷却状态。与 `upper` 相对，`lower` 表示冷却设备在该温度阈值下的最小工作强度。

9） `target` (`unsigned long target`)
```c
unsigned long target;
```
- **类型**：无符号长整型。
- **功能**：表示当前预期的冷却状态。这个状态是根据温度阈值和热区的需求来动态计算的，系统会尝试使冷却设备达到该目标状态。

10） `attr_name` (`char attr_name[THERMAL_NAME_LENGTH]`)
```c
char attr_name[THERMAL_NAME_LENGTH];
```
- **类型**：字符数组。
- **功能**：存储与该冷却设备实例相关的属性名称。属性名称用于在系统的设备树或文件系统中创建文件，使用户可以访问和控制冷却设备的状态。

11） `attr` (`struct device_attribute attr`)
```c
struct device_attribute attr;
```
- **类型**：`device_attribute` 结构体。
- **功能**：表示与冷却设备实例相关的设备属性。这通常用于设备的 sysfs 接口，允许用户空间与内核空间之间交换信息。例如，通过 `attr` 用户可以控制冷却设备的工作状态。

12） `weight_attr_name` (`char weight_attr_name[THERMAL_NAME_LENGTH]`)
```c
char weight_attr_name[THERMAL_NAME_LENGTH];
```
- **类型**：字符数组。
- **功能**：存储冷却设备权重属性的名称。冷却设备的权重通常用于决定该设备在热管理中的优先级和冷却效果。

13） `weight_attr` (`struct device_attribute weight_attr`)
```c
struct device_attribute weight_attr;
```
- **类型**：`device_attribute` 结构体。
- **功能**：表示与冷却设备权重相关的设备属性。这通常在 sysfs 中作为一个接口提供给用户空间，让用户可以调整冷却设备的权重，从而影响系统的热管理策略。

14） `tz_node` (`struct list_head tz_node`)
```c
struct list_head tz_node;
```
- **类型**：`list_head` 结构。
- **功能**：该节点用于将当前 `thermal_instance` 添加到热区设备（`tz`）的实例链表中。每个热区可以包含多个冷却设备实例，这个节点帮助组织热区下所有冷却设备实例的链表结构。

15） `cdev_node` (`struct list_head cdev_node`)
```c
struct list_head cdev_node;
```
- **类型**：`list_head` 结构。
- **功能**：该节点用于将当前 `thermal_instance` 添加到冷却设备（`cdev`）的实例链表中。每个冷却设备可以在多个热区中使用，这个节点帮助组织冷却设备下所有实例的链表结构。

16） `weight` (`unsigned int weight`)
```c
unsigned int weight;
```
- **类型**：无符号整数。
- **功能**：表示该冷却设备的权重。权重值通常用于决策热管理中的冷却策略，较高的权重表示冷却效果较强。它影响该冷却设备在热区中的调度和优先级。

### Thermal 关键函数接口说明

Thermal core 是 Thermal Zone、Thermal Cooling 和 Thermal Governor 之间的协调者，通过提供统一的 API 将它们相互关联，实现温度控制的功能。

具体来说，Thermal core 通过以下方式工作：

1. **从 Thermal Zone 获取温度**：首先，通过 Thermal Zone 设备获取当前的温度值。
2. **选择对应的 Thermal Governor**：根据温度值，选择适当的 Thermal Governor 来管理温控策略。
3. **控制 Thermal Cooling 设备**：根据 Thermal Governor 的设置，调整 Thermal Cooling 设备的状态，从而达到温度调节的目的。

**设备注册与注销**：

- **注册 Thermal Zone 设备**：通过调用 `thermal_zone_device_register()`，注册 thermal zone 设备，并创建一系列 sysfs 节点，同时将其与相应的 governor 和 cooling 设备进行绑定。

- **注销 Thermal Zone 设备**：`thermal_zone_device_unregister()` 函数执行相反的操作，移除 thermal zone 设备，解除与 cooling 设备的绑定，并删除相应的 sysfs 节点。

**Cooling 设备的管理**：

- **注册 Cooling 设备**：通过 `thermal_cooling_device_register()` 创建 cooling 设备，将其加入到 `thermal_cdev_list` 中，并为 cooling 设备创建相关的 sysfs 节点。此时，cooling 设备与 thermal zone 设备进行绑定。

- **注销 Cooling 设备**：`thermal_cooling_device_unregister()` 执行相反的操作，解除 cooling 设备与 thermal zone 的绑定，并移除相关的 sysfs 节点。

**Governor 的管理**：

- **注册 Thermal Governor**：`thermal_register_governor()` 首先会检查 `thermal_governor_list` 中是否已有同名 governor，如果没有，则将其添加到列表中，并更新 `thermal_tz_list` 中未指定 governor 的 thermal zone。

- **注销 Thermal Governor**：通过 `thermal_unregister_governor()`，将指定的 governor 与其绑定的 thermal zone 解除绑定，并调用 `unbind_from_tz()` 清空相关数据，最后从 `thermal_go` 中移除该 governor。

**设备绑定与解绑**：

- **绑定 Cooling 设备到 Thermal Zone**：`thermal_zone_bind_cooling_device()` 通过创建 `thermal_instances` 设备，将 Thermal Zone 与 Thermal Cooling 设备进行绑定，从而实现基于温度的 Cooling 设备控制。

- **解绑 Cooling 设备**：`thermal_zone_unbind_cooling_device()` 将已绑定的 `thermal_instances` 从两者的链表中移除，解除绑定关系。

**温度更新与处理**：

- **更新 Thermal Zone 的温度**：`thermal_zone_device_update()` 一般由 Thermal 驱动触发，可能是通过 polling 或中断方式更新当前的温度值，并根据温度变化通过 `handle_thermal_trip()` 进行相应的处理。

**监控与延时处理**：

- **监控 Thermal Zone**：`monitor_thermal_zone()` 根据 passive 或 polling 设置决定是否启动 `thermal_zone_device->pool_queue` 这个延时工作项（delayed_work）。

- **Polling 流程**：整个 polling 流程由 `thermal_zone_device_update()` 触发，具体流程如下：
  1. 在 `handle_thermal_trip()` 中启动 `monitor_thermal_zone()`。
  2. 在 `monitor_thermal_zone()` 中，调用 `mod_delayed_work()` 更新 poll_queue 的延时值。如果 thermal zone 有多个 trip，poll_queue 的延时值可能会多次更新。
  3. poll_queue 被放入 `system_freezable_wq` 后，达到设定时间后，调用 `thermal_zone_device_check()`，进而调用 `thermal_zone_device_update()` 完成周期性更新和处理。

上述流程可以描述如下图：

![thermal_api](./_static/_images/Thermal_system_zh_CN/thermal_api_zh_CN.png)

下面是对一些关键函数的详细分析。

#### thermal_set_governor 函数

**功能：**
切换热区（thermal zone）的热政策（governor）。

**函数原型：**

```c
static int thermal_set_governor(struct thermal_zone_device *tz,
				struct thermal_governor *new_gov)
```

**参数：**

- `tz`: 指向热区设备的指针。
- `new_gov`: 新的热政策。

**逻辑：**

- 如果当前热区已有绑定的热政策，先调用其 `unbind_from_tz` 方法解除绑定。
- 如果新热政策存在且有 `bind_to_tz` 方法，尝试绑定到热区。
- 如果绑定失败，尝试恢复到之前的热政策。
- 最后，更新热区的热政策指针。

**返回值：**

- 成功时返回0，绑定新热政策失败时返回错误码。

#### thermal_register_governor 函数

**功能：**
注册一个新的热政策。

**函数原型：**

```c
int thermal_register_governor(struct thermal_governor *governor)
```

**参数：**

- `governor`: 要注册的热政策。

**逻辑：**

- 检查热政策是否已存在。
- 如果不存在，将其添加到热政策列表，并设置默认热政策（如果匹配）。
- 遍历所有热区，尝试将新热政策应用到每个热区。

**返回值：**

- 成功时返回0，失败时返回错误码。

#### thermal_unregister_governor 函数

**功能：**
注销一个热政策。

**函数原型：**

```c
void thermal_unregister_governor(struct thermal_governor *governor)
```

**参数：**

- `governor`: 要注销的热政策。

**逻辑：**

- 从热政策列表中移除指定的热政策。
- 遍历所有热区，将使用该热政策的热区恢复到默认热政策。

#### update_temperature 函数

**功能：**
更新热区设备的温度。

**函数原型：**

```c
static void update_temperature(struct thermal_zone_device *tz)
```

**参数：**

- `tz`: 指向热区设备的指针。

**逻辑：**

- 调用 `thermal_zone_get_temp` 函数读取温度。
- 更新热区设备的温度值。
- 如果温度有效，触发相应的通知。

#### thermal_zone_device_update 函数

**功能：**
更新热区设备的状态。

**函数原型：**

```c
void thermal_zone_device_update(struct thermal_zone_device *tz,
				enum thermal_notify_event event)
```

**参数：**

- `tz`: 指向热区设备的指针。
- `event`: 触发更新的事件类型。

**逻辑：**

- 检查系统是否挂起，如果是则直接返回。
- 更新温度。
- 处理所有的温度阈值（trip points）。
- 根据需要调整轮询频率。

#### thermal_zone_device_check 函数

**功能：**
作为工作队列的回调函数，用于检查和更新热区设备的状态。

**函数原型：**

```c
static void thermal_zone_device_check(struct work_struct *work)
```

**参数：**

- `work`: 工作队列的结构体指针。

**逻辑：**

- 调用 `thermal_zone_device_update` 函数更新热区设备的状态。

#### thermal_zone_bind_cooling_device 函数

**功能：**
将冷却设备绑定到热区设备的特定温度阈值。

**函数原型：**

```c
int thermal_zone_bind_cooling_device(struct thermal_zone_device *tz,
				     int trip,
				     struct thermal_cooling_device *cdev,
				     unsigned long upper, unsigned long lower,
				     unsigned int weight)
```

**参数：**

- `tz`: 热区设备。
- `trip`: 温度阈值索引。
- `cdev`: 冷却设备。
- `upper`: 最高冷却状态。
- `lower`: 最低冷却状态。
- `weight`: 冷却设备权重。

**逻辑：**

- 检查参数有效性。
- 创建一个新的 `thermal_instance` 结构体，表示冷却设备与热区的绑定。
- 更新 sysfs 接口以反映新的绑定关系。

**返回值：**

- 成功时返回0，失败时返回错误码。

#### thermal_zone_unbind_cooling_device 函数

**功能：**
从一个热区设备中解绑一个冷却设备。

**函数原型：**

```c
int thermal_zone_unbind_cooling_device(struct thermal_zone_device *tz,
				       int trip,
				       struct thermal_cooling_device *cdev)
```

**参数：**

- `tz`: 热区设备。
- `trip`: 温度阈值索引。
- `cdev`: 冷却设备。

**逻辑：**

- 找到对应的 `thermal_instance` 结构体，并从列表中移除。
- 更新 sysfs 接口以反映解绑关系。

**返回值：**

- 成功时返回0，失败时返回错误码。

#### thermal_cooling_device_unregister 函数

**功能：**
注销一个冷却设备。

**函数原型：**

```c
void thermal_cooling_device_unregister(struct thermal_cooling_device *cdev)
```

**参数：**

- `cdev`: 要注销的冷却设备。

**逻辑：**

- 从冷却设备列表中移除指定的冷却设备。
- 解绑所有与该冷却设备关联的热区。

#### bind_tz 函数

**功能：**
将冷却设备绑定到热区。

**函数原型：**

```c
static void bind_tz(struct thermal_zone_device *tz)
```

**参数：**

- `tz`: 热区设备。

**逻辑：**

- 遍历所有冷却设备，尝试将每个冷却设备绑定到热区。

#### thermal_zone_device_register_with_trips 函数

**功能：**
创建并注册一个新的热区设备，并为其指定热区（trip）信息。

**函数原型：**

```c
struct thermal_zone_device *
thermal_zone_device_register_with_trips(const char *type, struct thermal_trip *trips, int num_trips, int mask,
					void *devdata, struct thermal_zone_device_ops *ops,
					struct thermal_zone_params *tzp, int passive_delay,
					int polling_delay)
```

**参数：**

- `type`: 热区名称。
- `trips`: 温度阈值数组。
- `num_trips`: 温度阈值数量。
- `mask`: 温度阈值掩码。
- `devdata`: 私有数据。
- `ops`: 热区操作接口。
- `tzp`: 热区参数。
- `passive_delay`: 被动冷却延迟。
- `polling_delay`: 轮询延迟。

**逻辑：**

- 初始化热区设备。
- 注册设备到 sysfs。
- 绑定冷却设备。
- 设置轮询和延迟。

**返回值：**

- 成功时返回热区设备指针，失败时返回错误指针。

#### thermal_zone_device_unregister 函数

**功能：**
注销一个热区设备。

**函数原型：**

```c
void thermal_zone_device_unregister(struct thermal_zone_device *tz)
```

**参数：**

- `tz`: 热区设备。

**逻辑：**

- 从热区列表中移除热区。
- 解绑所有冷却设备。
- 注销设备。

#### thermal_init 函数

**功能：**
初始化热管理框架。

**函数原型：**

```c
static int __init thermal_init(void)
```

**逻辑：**

- 注册热政策。
- 注册热类（thermal class）。
- 注册 PM 通知器。

**返回值：**

- 成功时返回0，失败时返回错误码。

## <span id="thermal_debugging_instructions"/>驱动平台 thermal 系统调试说明

在板端系统 `/sys/class/hwmon/hwmon0` 目录下包含温度传感器的相关参数 `temp1_input` 是 DDR 的温度，`temp2_input` 是 BPU 的温度，`temp3_input` 是 CPU 的温度，具体如下：

```bash
root@buildroot:~# ls /sys/class/hwmon/hwmon0/ | grep temp
temp1_input
temp2_input
temp3_input
```

温度的精度为0.001摄氏度，查看一个 DDR 温度命令如下：

``` bash
cat /sys/class/hwmon/hwmon0/temp1_input
46643
```

上述日志表示当前 DDR 的温度是46.643℃。

**注意**：BPU 的温度传感器位于 bpu subsytem，bpu subsystem 只有在 bpu 运行时才会上电，所以只有 bpu 运行时，bpu 的温度才可以查看。

目前默认的策略通过以下命令可知是使用的 step_wise：

```bash
cat /sys/class/thermal/thermal_zone0/policy
step_wise
```

通过以下命令可看到支持的策略：user_space、step_wise 一共两种：

```bash
cat /sys/class/thermal/thermal_zone0/available_policies
user_space step_wise
```

- user_space 是通过 uevent 将温区当前温度，温控触发点等信息上报到用户空间，由用户空间软件制定温控的策略。

- step_wise 是每个轮询周期逐级提高冷却状态，是一种相对温和的温控策略

具体选择哪种策略是根据产品需要自己选择。可在编译的时候指定或者通过 sysfs 动态切换。
例如：动态切换 thermal_zone0 的策略为 user_space 模式

```bash
echo user_space > /sys/class/thermal/thermal_zone0/policy
```

在 `thermal_zone0` 中有1个 trip_point，用于控制 cooling 设备 DDR 的调频温度

可通过 sysfs 查看 DDR 的调频温度，当前配置的为95度

```bash
cat /sys/devices/virtual/thermal/thermal_zone0/trip_point_0_temp
```

若想调整 DDR 的调频温度，如85度，可通过如下命令：

```bash
echo 85000 > /sys/devices/virtual/thermal/thermal_zone0/trip_point_0_temp
```

在 thermal_zone1中有3个 trip_point，其中：

- trip_point_0_temp 为预留作用。
- trip_point_1_temp 是该 thermal zone 的调频温度，可控制 CPU/BPU/GPU 的频率，当前设置为95度。
- trip_point_2_temp 为关机温度，当前设置为105度。

例如想要结温到85摄氏度，CPU/BPU/GPU 开始调频：

```bash
echo 85000 > /sys/devices/virtual/thermal/thermal_zone1/trip_point_1_temp
```

如果想要调整关机温度为105摄氏度：

```bash
echo 105000 > /sys/devices/virtual/thermal/thermal_zone1/trip_point_2_temp
```

ps：以上设置断电重启后需要重新设置

## thermal参考文档

以下路径以kernel代码目录为根目录:

```bash
./Documentation/devicetree/bindings/thermal
./Documentation/driver-api/thermal
```
