📺B站 嵌入式孙老师:博主个人介绍
📘博主书籍-京东购买链接:Yocto项目实战教程
📘加博主微信,进技术交流群:jerrydev
在 RK3576 上调试低功耗时,最常用的命令是:
echomem>/sys/power/state它看起来只是向 sysfs 写入一个字符串,实际会启动完整的 System Suspend 流程:
Linux PM Core ↓ 设备驱动挂起 ↓ Regulator 配置 PMIC ↓ genpd 关闭部分 Power Domain ↓ PSCI SYSTEM_SUSPEND ↓ SMC 进入 BL31 ↓ RK3576 PMU 执行整机低功耗我沿着当前 RK3576 Linux 6.1 内核代码把这条链路重新梳理了一遍。理解它的关键,不是记住所有函数,而是分清三条电源控制路径:
| 路径 | 管理对象 | 关键代码 |
|---|---|---|
| Regulator | RK806 的 BUCK、LDO 等真实电压 | drivers/regulator/ |
| Power Domain | RK3576 内部 GPU、NPU、GMAC 等功能域 | drivers/soc/rockchip/pm_domains.c |
| PSCI / BL31 / PMU | CPU、Logic、DDR、PLL 和整机待机时序 | drivers/firmware/psci/psci.c |
这三条路径由 Linux PM 框架串联起来,但控制对象并不相同。本文中的函数和代码位置基于当前 RK3576 Linux 6.1 仓库整理。
一、mem是休眠入口,不是固定深度
/sys/power/state表示什么
设备上查看:
cat/sys/power/state输出:
freeze mem表示内核支持两个系统级休眠入口:
| 状态 | 含义 |
|---|---|
freeze | Suspend-to-Idle,主要冻结软件和设备 |
mem | 内存挂起入口,具体模式由mem_sleep决定 |
继续查看:
cat/sys/power/mem_sleep当前系统输出:
s2idle [deep]方括号表示当前选中的模式,因此现在执行:
echomem>/sys/power/state实际进入的是deep。
echo deep为什么不会立即休眠
下面这条命令:
echodeep>/sys/power/mem_sleep只是在选择mem后续采用哪种模式,不会立即触发系统挂起。
二者分工很明确:
/sys/power/mem_sleep └─ 选择 s2idle 或 deep /sys/power/state └─ 真正触发休眠当前已经显示:
s2idle [deep]所以无需重复设置,直接执行即可:
echomem>/sys/power/statemem在内核中如何转换
入口位于:
kernel/power/main.c核心逻辑如下:
staticssize_tstate_store(...){state=decode_state(buf,n);if(state<PM_SUSPEND_MAX){if(state==PM_SUSPEND_MEM)state=mem_sleep_current;error=pm_suspend(state);}}关键代码是:
if(state==PM_SUSPEND_MEM)state=mem_sleep_current;实际处理过程为:
字符串 "mem" ↓ decode_state() ↓ PM_SUSPEND_MEM ↓ 替换成 mem_sleep_current ↓ pm_suspend()所以,mem本身不是一个固定的硬件深度,它是一个通用入口。
在当前系统中:
mem_sleep_current = deep因此这次会进入 RK3576 平台级 System Suspend。
deep与关机的区别
deep通常可理解为 Suspend-to-RAM:
CPU 停止运行或断电 Linux 暂停执行 普通外设 挂起或断电 DDR 进入自刷新 DDR数据 继续保留 唤醒后 从原来的运行位置继续它不是关机。
关机:DDR数据丢失,下次重新启动Linux deep:DDR数据保留,唤醒后继续执行二、RK3576 低功耗中的三条控制路径
Regulator:控制板上的真实电压
Regulator 是 Linux 通用电压管理框架。
RK806 内部的每一路 BUCK、LDO,都会作为一个 regulator 注册到内核,例如:
VDD_CPU VDD_GPU VDD_NPU VDD_LOGIC DDR相关电源 VCC_3V3 VCC_1V8设备树可以配置某路电源在mem状态下保持或关闭:
regulator-state-mem { regulator-off-in-suspend; };表示进入 Suspend 时关闭。
保持供电则配置:
regulator-state-mem { regulator-on-in-suspend; };Regulator 的 PM 回调位于:
drivers/regulator/core.c核心结构为:
staticconststructdev_pm_opsregulator_pm_ops={.suspend=regulator_suspend,};在设备 PM 的dpm_suspend()阶段,内核会调用:
regulator_suspend()它根据regulator-state-mem配置,通过对应的 PMIC 驱动设置 RK806 寄存器。
因此,这条路径管理的是:
某一路真实电压在休眠期间保持、关闭或切换到低功耗状态。
Rockchip PM 驱动还提供:
rockchip,regulator-on-in-mem; rockchip,regulator-off-in-mem; rockchip,regulator-on-before-mem;例如:
rockchip,regulator-on-before-mem = <&vdd_npu_s0>;它会在进入深睡前的suspend_late阶段临时打开指定 regulator,用来满足特定电源时序。
这不表示 NPU 在整个休眠期间继续工作。
Power Domain:控制 RK3576 内部功能域
Power Domain 管理的是 RK3576 芯片内部的功能模块,例如:
GPU NPU ISP VPU USB SDGMAC PHP 显示和视频模块Rockchip 驱动位于:
drivers/soc/rockchip/pm_domains.c核心关断函数是:
rockchip_pd_power(pd,false);典型处理过程为:
设备停止业务 ↓ 等待总线空闲 ↓ 关闭模块时钟 ↓ 设置隔离 ↓ 关闭内部 Power Domain这条路径操作的是 RK3576 内部 PMU 寄存器,不会直接通过 I²C 控制 RK806。
例如:
关闭 RK3576_PD_SDGMAC只表示 SoC 内部的 GMAC 控制器域被关闭,不表示外部 Ethernet PHY 的 3.3V 电源一定断开。
两者属于不同层次:
GMAC Power Domain └─ RK3576芯片内部 PHY供电 └─ PMIC、MOS或Load SwitchPower Domain 通常在系统挂起后期参与。部分设备驱动在.suspend_noirq中调用:
pm_runtime_force_suspend();随后由 genpd 判断域内设备是否已经全部空闲,再决定是否调用:
rockchip_pd_power(pd,false);BL31 与 PMU:完成整机级休眠
Regulator 和普通 Power Domain 能关闭大量设备,但还不足以让整个 RK3576 进入深度待机。
系统级动作包括:
DDR进入自刷新 关闭CPU 关闭vdd_arm 关闭vdd_logic 关闭PLL 切换到32K 配置IO Retention 配置Sleep Pin 配置系统唤醒源这些动作由 PSCI、BL31 和 RK3576 PMU 完成。
职责可以概括为:
| 层次 | 主要职责 |
|---|---|
| 设备驱动 | 停止业务、保存寄存器状态 |
| Regulator | 配置 RK806 各路输出电压 |
| Power Domain | 关闭 RK3576 内部功能域 |
| BL31 / PMU | 完成 CPU、Logic、DDR、PLL 等系统级休眠 |
它们不是重复控制,而是从设备到整机逐层收尾。
三、沿着echo mem跟踪内核调用链
完整代码路径
echo mem > /sys/power/state ↓ state_store() ↓ pm_suspend() ↓ enter_state() ↓ suspend_prepare() ↓ dpm_suspend_start() ├─ dpm_prepare() └─ dpm_suspend() ↓ dpm_suspend_late() ↓ dpm_suspend_noirq() ↓ syscore_suspend() ↓ suspend_ops->enter() ↓ psci_system_suspend_enter() ↓ cpu_suspend() ↓ invoke_psci_fn() ↓ SMC进入BL31Linux 先冻结进程
pm_suspend()进入:
suspend_prepare()主要完成:
发送 PM_SUSPEND_PREPARE 通知 ↓ 冻结用户空间进程 ↓ 冻结可冻结的内核线程这样可以防止休眠过程中,应用继续访问网络、存储、摄像头、USB 等设备。
设备 PM 分阶段执行
设备挂起不是一次完成,而是分四个阶段:
dpm_prepare() ↓ dpm_suspend() ↓ dpm_suspend_late() ↓ dpm_suspend_noirq()| 阶段 | 主要任务 |
|---|---|
prepare | 休眠前准备 |
suspend | 停止设备业务、保存状态 |
suspend_late | 处理依赖和电源时序 |
suspend_noirq | 普通中断关闭后的最终处理 |
Rockchip PM、Regulator 和 Power Domain,分别在这些阶段参与。
dpm_prepare():Rockchip 下发休眠策略
RK3576 的平台配置驱动位于:
drivers/soc/rockchip/rockchip_pm_config.c它注册了:
staticconststructdev_pm_opsrockchip_pm_ops={.prepare=pm_config_prepare,.complete=pm_config_complete,.suspend_late=pm_config_suspend_late,.resume_early=pm_config_resume_early,};在dpm_prepare()阶段,调用:
pm_config_prepare()它读取 DTS 中的:
rockchip,sleep-mode-config; rockchip,wakeup-config; rockchip,sleep-io-ret-config; rockchip,sleep-pin-config;| 属性 | 作用 |
|---|---|
sleep-mode-config | 决定系统休眠深度 |
wakeup-config | 配置可用唤醒源 |
sleep-io-ret-config | 配置需要保持状态的 IO 域 |
sleep-pin-config | 配置休眠时 Sleep Pin 状态 |
随后通过:
sip_smc_set_suspend_mode(...)把这些参数提前交给 BL31。
简化后的逻辑如下:
sip_smc_set_suspend_mode(SUSPEND_MODE_CONFIG,sleep_mode,0);sip_smc_set_suspend_mode(WKUP_SOURCE_CONFIG,wakeup_config,0);这里仅仅是在告诉 BL31:
真正进入休眠时应该关闭哪些资源、保留哪些唤醒源。
此时 Linux 和 CPU 仍然在运行,系统还没有真正睡下去。
dpm_suspend():应用 PMIC 休眠配置
进入:
dpm_suspend()Linux 会遍历所有设备并调用.suspend()。
Regulator 设备在这里调用:
regulator_suspend()应用:
regulator-state-mem所以,这一步可能真正修改 RK806 的休眠寄存器配置。
需要区分两件事:
pm_config_prepare() └─ 把平台低功耗参数交给BL31 regulator_suspend() └─ 配置PMIC各路BUCK/LDO状态两者不是同一条代码路径。
dpm_suspend_late():处理最后阶段电源时序
随后进入:
dpm_suspend_late()Rockchip 的:
pm_config_suspend_late()会在这里执行。
例如:
rockchip,regulator-on-before-mem = <&vdd_npu_s0>;指定的 regulator 会在这里临时打开。
这类配置用于满足进入深睡前的特殊硬件时序,不应理解为对应模块在待机期间仍然正常工作。
dpm_suspend_noirq():处理 Power Domain
接下来进入:
dpm_suspend_noirq()此时普通设备中断基本已经关闭。
设备驱动可能通过:
pm_runtime_force_suspend();触发 genpd 关闭对应功能域,最终进入:
rockchip_pd_power(pd,false);这一步控制的是 RK3576 内部 Power Domain,不直接控制外部 PMIC 电压。
PSCI:从 Linux 进入 BL31
Linux 完成设备、Regulator 和普通 Power Domain 的处理后,会调用:
suspend_ops->enter(state);在 RK3576 ARM64 平台中,对应:
psci_system_suspend_enter()代码位于:
drivers/firmware/psci/psci.c核心结构:
staticconststructplatform_suspend_opspsci_suspend_ops={.valid=suspend_valid_only_mem,.enter=psci_system_suspend_enter,};进入函数:
staticintpsci_system_suspend_enter(suspend_state_tstate){returncpu_suspend(0,psci_system_suspend);}cpu_suspend()保存 CPU 上下文,然后执行:
staticintpsci_system_suspend(unsignedlongunused){phys_addr_tpa_cpu_resume=__pa_symbol(cpu_resume);returninvoke_psci_fn(PSCI_FN_NATIVE(1_0,SYSTEM_SUSPEND),pa_cpu_resume,0,0);}关键位置是:
invoke_psci_fn(PSCI_FN_NATIVE(1_0,SYSTEM_SUSPEND),...);它最终触发 SMC:
Linux Kernel,EL1 │ │ PSCI SYSTEM_SUSPEND ▼ BL31,EL3从这里开始,Linux 内核暂停运行。
后面的 DDR 自刷新、PLL 关闭、CPU 和 Logic 断电、唤醒等待,都由 BL31 和 RK3576 PMU 负责。Linux 内核代码跟踪到这里结束。
四、DTS 决定系统最终睡多深
三档核心休眠模式
RK3576 的定义位于:
include/dt-bindings/suspend/rockchip-rk3576.h三档核心模式是:
#defineRKPM_SLP_ARMOFF_DDRPDBIT(2)#defineRKPM_SLP_ARMOFF_LOGOFFBIT(3)#defineRKPM_SLP_ARMOFF_PMUOFFBIT(4)| 模式 | 主要关闭范围 | 功耗 | 唤醒能力 |
|---|---|---|---|
ARMOFF_DDRPD | ARM及DDR控制相关部分 | 较高 | 较多 |
ARMOFF_LOGOFF | ARM、Logic | 较低 | 较少 |
ARMOFF_PMUOFF | ARM、Logic、PMU1 | 最低 | 最少 |
核心规律是:
关闭资源越多 ↓ 待机功耗越低 ↓ 保留的硬件越少 ↓ 可用唤醒源越少真正决定休眠深度的是:
rockchip,sleep-mode-config而不是echo mem本身。
echo mem负责触发流程,BL31 最终执行哪种低功耗策略,由 DTS 配置决定。
时钟和 PMIC 附加配置
常见选项还有:
RKPM_SLP_PMU_PMUALIVE_32K RKPM_SLP_PMU_DIS_OSC RKPM_SLP_PMIC_LP RKPM_SLP_32K_EXT| 配置 | 作用 |
|---|---|
PMUALIVE_32K | PMU Alive 域切换到 32K |
DIS_OSC | 关闭 24M 晶振 |
PMIC_LP | 配置 PMIC 低功耗状态 |
32K_EXT | 使用外部 32K 时钟 |
关闭 24M 可以进一步降低功耗,但依赖 24M 的 USB、UART、PWM 等唤醒方式可能受到影响。
因此低功耗配置并不是“关闭得越多越好”,而是要在功耗、唤醒能力和恢复稳定性之间做取舍。
唤醒由 PMU 检测
进入深度休眠后:
CPU 停止运行或断电 Linux 停止执行 DDR 保持自刷新 PMU Always-On 继续工作 唤醒检测逻辑 继续工作真正等待唤醒事件的不是 Linux,而是 RK3576 PMU。
以 GPIO0 唤醒为例:
外部 GPIO 事件 ↓ GPIO0 唤醒信号进入 PMU ↓ PMU 恢复电源和时钟 ↓ BL31 恢复 CPU 上下文 ↓ 跳转到 cpu_resume ↓ 返回 Linux恢复阶段按相反顺序展开:
syscore_resume() ↓ dpm_resume_noirq() ↓ dpm_resume_early() ↓ dpm_resume() ↓ dpm_complete() ↓ 解冻用户进程五、实战中如何确认低功耗是否生效
确认当前模式
cat/sys/power/statecat/sys/power/mem_sleep预期输出:
freeze mem s2idle [deep]执行休眠
echomem>/sys/power/state查看 Linux 日志
唤醒后执行:
dmesg|grep-iE"PM:|suspend|resume|wakeup"重点关注:
PM: suspend entry (deep) Freezing user space processes Suspending console(s) PM: suspend exit查看 BL31 日志
DTS 中打开:
rockchip,sleep-debug-en = <1>;串口中可能看到:
INFO: deep INFO: armoff_logoff INFO: pmu_pmualive_32k INFO: io_ret (...) INFO: sleep_pin: ... INFO: wake up status: ...这些日志可以确认:
- 是否进入
deep; - 实际使用哪种 ARMOFF 模式;
- 是否切换到 32K;
- IO Retention 是否配置;
- Sleep Pin 是否生效;
- 最终由哪个事件唤醒。
软件日志不能代替电压测量
日志只能说明软件流程执行到了哪里,不能证明所有板级电压都按预期变化。
仍需要实际测量:
VDD_CPU VDD_LOGIC DDR电源 24M晶振 Sleep Pin VCC_3V3_S0 VCC_3V3_S3 外部PHY和无线模块电源特别是板级 MOS、Load Switch 和_S0、_S3电源命名,最终都应以原理图和实测波形为准。
总结
RK3576 执行:
echomem>/sys/power/state背后的完整过程是:
echo mem ↓ Linux PM Core冻结进程 ↓ 设备驱动停止业务 ↓ Rockchip PM驱动下发休眠参数 ↓ Regulator配置RK806电压状态 ↓ genpd关闭RK3576内部功能域 ↓ PSCI SYSTEM_SUSPEND ↓ SMC进入BL31 ↓ RK3576 PMU完成整机深度休眠其中:
Regulator └─ 管理板级真实电压 Power Domain └─ 管理SoC内部功能模块 BL31 / PMU └─ 管理CPU、Logic、DDR、PLL和系统唤醒echo mem不是一条直接关闭 PMIC 的命令,也不是单独触发某个 Power Domain。
它的作用是启动 Linux System Suspend,让软件、设备、电源和固件按照既定顺序逐层进入低功耗,最终由 BL31 和 RK3576 PMU 完成整机级休眠。