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 中正确关联。

复盘要点:电源管理问题大多不是“某个 API 用错”,而是顺序、依赖、计数三者不一致。建议在驱动中显式维护上电/下电序列,并用 runtime_status 与 dmesg 中的 PM 日志交叉验证。

综上,系统睡眠状态解决“整机怎么睡”,Runtime PM 解决“设备空闲怎么省”,时钟与稳压器框架解决“资源怎么供”,设备电源域解决“设备怎么分组”。四者通过引用计数与依赖顺序协同,构成 Linux 设备电源与生命周期管理的底层基础。

Linux 电源管理四层协同关系 系统睡眠状态(整机级) freeze → standby → mem(S3) → disk(S4) | /sys/power/state 冻结进程 → 设备 suspend 回调 → 关时钟/稳压器 → 平台低功耗态 运行时电源管理 Runtime PM(设备级) pm_runtime_get_sync() / pm_runtime_put_sync() | 引用计数 + 空闲回调 runtime_suspend / runtime_resume | autosuspend 延迟 时钟框架 CCF clk_get / clk_prepare_enable 父子级联 · 先开后关 稳压器框架 Regulator regulator_get / regulator_enable 电压约束 · 上下电时序 设备电源域 Power Domain(genpd) power-domains 属性 · 域内设备整体上电/掉电 · 与 Runtime PM 引用计数联动 上电顺序 稳压器 ↓ 时钟 ↓ 电源域 ↓ 设备 下电顺序 设备 ↓ 电源域 ↓ 时钟 ↓ 稳压器