2. Runtime PM 核心概念
Runtime PM(Runtime Power Management,运行时电源管理)是 Linux 内核提供的一套 设备级动态电源管理框架。它允许设备在系统整体运行期间,根据自身的使用状态独立、自动地进入或退出低功耗模式,而无需依赖系统级的挂起/唤醒操作。
2.1 Runtime PM 的定义与目标
定义:Runtime PM 是一种细粒度的、基于设备使用情况的电源管理机制。它通过跟踪每个设备的 活跃度(idle/busy),在设备空闲时自动将其置于低功耗状态(suspended),并在需要使用时快速恢复(resumed)。整个过程对用户空间和大部分内核子系统透明。
核心目标:
- 降低静态功耗:在设备不工作时,切断或降低其时钟、电源域,减少漏电流和静态功耗。
- 提升能效比:只在设备真正需要工作时消耗能量,避免“空转”浪费。
- 延长电池寿命:对于移动和嵌入式设备,这是最直接的收益。
- 保持系统响应性:挂起和恢复操作是异步、低延迟的,不影响系统整体运行。
💡 关键理解:Runtime PM 不是“省电模式”,而是“按需供电”。它让电源管理从“全有或全无”的系统级策略,进化到“设备级、动态、自动”的精细化管理。
2.2 与传统电源管理的区别
传统电源管理(如系统挂起 Suspend-to-RAM、Suspend-to-Disk)与 Runtime PM 在粒度、触发方式和影响范围上有本质区别。下表总结了关键差异:
| 对比维度 | 传统电源管理(System PM) | Runtime PM |
|---|---|---|
| 管理粒度 | 系统级(整个 SoC / 整机) | 设备级(单个外设、IP 核) |
| 触发方式 | 用户主动触发(按键、合盖、定时器)或内核策略 | 设备自身空闲/忙碌状态自动触发 |
| 影响范围 | 所有设备、CPU、内存均受影响 | 仅目标设备及其依赖的电源域 |
| 延迟要求 | 较高(秒级),可接受较长的恢复时间 | 极低(微秒~毫秒级),必须快速响应 |
| 对用户透明性 | 不透明,用户能感知到系统暂停/恢复 | 完全透明,用户无感知 |
| 典型场景 | 笔记本合盖、手机待机 | I2C 总线空闲时关闭时钟、USB 设备自动挂起 |
📌 实践要点:在驱动开发中,不要将 Runtime PM 视为系统挂起的替代品。它们是互补的:Runtime PM 负责设备级的动态节能,系统挂起负责整体深度睡眠。一个设计良好的驱动应同时支持两者。
2.3 设备状态模型
Runtime PM 使用一个 三态有限状态机 来管理每个设备。理解这三个状态是编写 Runtime PM 驱动的基础。
- ACTIVE(活跃状态):设备正在工作或准备工作。电源已开启,时钟已使能,寄存器可正常访问。这是设备的默认初始状态。
- SUSPENDED(挂起状态):设备处于低功耗模式。电源可能被关闭,时钟被门控,寄存器上下文可能丢失。设备无法直接响应外部请求,需要先恢复。
- SUSPENDING(正在挂起):设备正在从 ACTIVE 向 SUSPENDED 过渡的中间状态。内核正在执行驱动的
runtime_suspend回调函数。在此状态下,设备可能正在执行最后的清理工作(如保存寄存器、刷新 FIFO)。
下图展示了设备状态之间的转换关系:
2.4 状态转换的触发机制
设备状态的转换并非随意发生,而是由内核的 Runtime PM 核心通过 引用计数 机制自动管理。每个设备维护一个 usage_count:
- usage_count > 0:设备被“引用”,必须保持 ACTIVE 状态。
- usage_count == 0:设备空闲,允许进入 SUSPENDED 状态。
驱动通过以下 API 操作引用计数:
// 增加引用计数,请求设备保持活跃
int pm_runtime_get_sync(struct device *dev);
int pm_runtime_get(struct device *dev); // 异步版本
// 减少引用计数,释放设备,允许挂起
int pm_runtime_put_sync(struct device *dev);
int pm_runtime_put(struct device *dev); // 异步版本
// 强制设置设备状态
int pm_runtime_set_active(struct device *dev);
int pm_runtime_set_suspended(struct device *dev);
⚠️ 常见陷阱:引用计数必须成对使用。如果
pm_runtime_get 和 pm_runtime_put 不匹配,设备将永远无法进入 SUSPENDED 状态,导致功耗异常。建议在驱动的 open/close、read/write 等对称操作中配对调用。
2.5 回调函数与状态机交互
驱动需要实现三个核心回调函数,内核在状态转换时自动调用它们:
| 回调函数 | 触发时机 | 驱动职责 |
|---|---|---|
runtime_suspend |
设备从 ACTIVE → SUSPENDING 时 | 保存必要上下文,关闭时钟/电源 |
runtime_resume |
设备从 SUSPENDED → ACTIVE 时 | 恢复上下文,重新使能时钟/电源 |
runtime_idle |
设备引用计数降为 0 时 | 判断是否真的可以挂起,返回 0 则触发 suspend |
下图展示了回调函数在状态机中的位置:
🔧 驱动开发要点:
runtime_suspend和runtime_resume必须成对实现,且保证幂等性(多次调用不产生副作用)。runtime_idle通常返回 0 让内核自动挂起,或返回 -EBUSY 阻止挂起(例如设备仍有未处理的数据)。- 回调函数中不能调用可能导致睡眠的函数(如
msleep),因为它们可能在原子上下文中被调用。
2.6 小结
Runtime PM 的核心思想可以概括为:
- 设备自治:每个设备独立管理自己的电源状态。
- 引用计数驱动:通过
get/put机制自动触发状态转换。 - 三态模型:ACTIVE ↔ SUSPENDING ↔ SUSPENDED,回调函数实现具体硬件操作。
- 透明高效:对用户空间透明,延迟极低,适合嵌入式实时场景。