2. Linux电源管理基础
Linux 电源管理并不是一个单一开关,而是一套从系统级睡眠到单设备运行时、从时钟与稳压器到电源域的分层协同机制。理解这些基本术语与原理,是后续做设备电源与生命周期协同设计的前提。本节围绕四个核心概念展开:系统睡眠状态、运行时电源管理、时钟与稳压器框架、设备电源域。
一、系统睡眠状态:整机进入低功耗的层级
系统睡眠状态描述的是整台 SoC 或整机在空闲时进入的低功耗模式。Linux 通过 /sys/power/state 暴露可写状态,常见取值包括 freeze、standby、mem、disk。它们对应 ACPI 的 S0~S4 概念,但具体实现由平台代码决定。
- freeze(S0 空闲):冻结用户进程与内核线程,但 CPU 与内存仍供电,唤醒极快,常用于 suspend-to-idle。
- standby(S1):CPU 停止执行,部分外设掉电,内存保持刷新,唤醒延迟较低。
- mem(S3,suspend-to-RAM):除内存外大部分电源域关闭,上下文保存在内存中,唤醒需重新初始化外设。
- disk(S4,suspend-to-disk):系统上下文写入磁盘后整机断电,唤醒相当于重新启动并恢复镜像。
进入睡眠的流程可概括为:用户写入 /sys/power/state → 内核冻结进程 → 各设备 suspend 回调 → 关闭非必要时钟与稳压器 → 进入平台低功耗态。唤醒时按相反顺序执行 resume 回调。这里的关键是设备回调顺序与依赖关系,若顺序错误,可能出现设备未就绪就被访问的情况。
mem 当成“所有设备都断电”。实际上内存必须保持自刷新,部分唤醒源(如 RTC、GPIO 按键)仍需供电,否则无法唤醒。二、运行时电源管理:设备空闲即省电
运行时电源管理(Runtime PM,RPM)关注的是系统正常运行期间,单个设备在空闲时自动进入低功耗状态。它不依赖整机睡眠,而是由设备驱动在合适时机调用 pm_runtime_get_sync() 与 pm_runtime_put_sync() 来管理引用计数。
/* 驱动访问硬件前获取电源 */
pm_runtime_get_sync(dev);
/* 访问寄存器、发起 DMA */
...
/* 访问完成后释放,允许空闲时挂起 */
pm_runtime_put_sync(dev);
Runtime PM 的核心是引用计数 + 空闲回调。当计数归零且设备空闲,内核调用驱动的 runtime_suspend;再次访问时调用 runtime_resume。它常与 autosuspend 延迟配合,避免频繁开关电源造成抖动。
- 使用场景:I2C/SPI 外设、USB 控制器、显示背光、传感器等间歇性工作的设备。
- 关键接口:
pm_runtime_enable()、pm_runtime_get_sync()、pm_runtime_put_autosuspend()。 - 调试入口:
/sys/devices/.../power/runtime_status与runtime_usage。
三、时钟与稳压器框架:电源管理的“供血系统”
时钟(Clock)与稳压器(Regulator)是设备工作的两个基础资源。Linux 分别用 Common Clock Framework(CCF) 和 Regulator Framework 统一管理,驱动通过 clk_get()、regulator_get() 获取句柄,再用 clk_prepare_enable()、regulator_enable() 打开资源。
两者都支持引用计数与级联依赖:一个时钟可能由父时钟分频得到,一个稳压器可能由父稳压器供电。关闭时必须先关子节点,再关父节点,否则会破坏其他设备。设备树中通常这样描述:
/* 设备节点中引用时钟与稳压器 */
vdd_core: regulator-core {
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1100000>;
};
uart0: serial@10000000 {
clocks = <&clk_uart0>;
vdd-supply = <&vdd_core>;
};
在系统睡眠与 Runtime PM 中,时钟和稳压器的开关顺序必须与设备状态一致:先开稳压器,再开时钟;先关时钟,再关稳压器。这是硬件上电时序的基本要求,也是驱动 suspend/resume 回调中常见的处理逻辑。
四、设备电源域:把设备按供电关系分组
设备电源域(Power Domain)是 SoC 内部按供电区域划分的逻辑分组。同一电源域内的设备共享电源开关,可以整体上电或掉电。Linux 用 Generic Power Domain(genpd) 框架描述这种层级关系,设备树中通过 power-domains 属性关联。
电源域通常呈树形结构:根域(Always-On)→ 显示域 → GPU 域 → 摄像头域。当域内所有设备都空闲时,域可以整体关闭;任一设备需要工作时,域必须先上电。这与 Runtime PM 的引用计数天然配合:设备计数归零 → 域计数归零 → 域掉电。
| 概念 | 作用范围 | 典型接口/文件 | 关键点 |
|---|---|---|---|
| 系统睡眠状态 | 整机/SoC | /sys/power/state | 冻结进程、设备 suspend/resume 顺序 |
| Runtime PM | 单个设备 | pm_runtime_* | 引用计数、空闲回调、autosuspend |
| 时钟框架 CCF | 时钟树 | clk_get/enable | 父子级联、先开后关 |
| 稳压器框架 | 供电轨 | regulator_get/enable | 电压约束、上下电时序 |
| 设备电源域 | 供电分组 | power-domains、genpd | 域内设备整体上电/掉电 |
五、案例与复盘:一次摄像头唤醒失败的排查
某平台在系统从 mem 唤醒后,摄像头无法出图。排查发现:摄像头驱动在 resume 中先打开了 MIPI 时钟,但对应的 vdd_cam 稳压器尚未使能,导致时钟有效而供电缺失。修正方法是调整 resume 顺序:先 regulator_enable(),再 clk_prepare_enable(),并确认摄像头电源域已在 genpd 中正确关联。
runtime_status 与 dmesg 中的 PM 日志交叉验证。综上,系统睡眠状态解决“整机怎么睡”,Runtime PM 解决“设备空闲怎么省”,时钟与稳压器框架解决“资源怎么供”,设备电源域解决“设备怎么分组”。四者通过引用计数与依赖顺序协同,构成 Linux 设备电源与生命周期管理的底层基础。