电源管理框架:从系统休眠到运行时PM的完整实战
嵌入式设备跑Linux,功耗控制是绕不开的课题。手机待机一晚上掉电5%还是30%,笔记本合盖后能不能真省电,工业网关空闲时CPU温度能不能降下来——全靠电源管理框架。Linux把电源管理拆成两套机制:系统休眠管整机状态切换,运行时PM管单个设备的按需开关。两套机制独立运作,但驱动回调有交叉。本文基于Linux 6.8内核,从架构讲起,过一遍Suspend/Resume流程和Runtime PM引用计数模型,再覆盖电源域、调试手段和常见坑点。
一、电源管理架构
Linux电源管理分四层,从用户空间到硬件逐级传递:
用户空间通过sysfs节点触发系统休眠,内核PM核心协调各子系统的冻结和恢复,驱动层实现具体的硬件操作,最底层是时钟框架、regulator框架和PSCI固件接口。
两大核心机制:
| 机制 | 粒度 | 触发方式 | 典型场景 |
|:-----|:-----|:-------|:---------|
| 系统休眠(Suspend/Resume) | 整机 | 用户空间主动触发 | 手机息屏、笔记本合盖 |
| 运行时PM(Runtime PM) | 单设备 | 内核自动(引用计数驱动) | 传感器空闲关时钟、GPU无任务降频 |
两套机制正交:系统休眠时Runtime PM的状态会被保存,唤醒后恢复。驱动可以只实现其中一套,也可以两套都实现。
二、系统休眠(Suspend/Resume)
休眠状态
ACPI定义了S0~S5六个状态,嵌入式平台不一定全部实现:
| 状态 | 名称 | CPU | 内存 | 外设 | 功能 | 唤醒延迟 |
|---|---|---|---|---|---|---|
| S0 | 运行 | 运行 | 保持 | 工作 | 最低 | 无延迟 |
| S1 | 待机(Standby) | 停止时钟 | 保持 | 部分关闭 | 较低 | 极快(~10ms) |
| S2 | 浅睡 | 断电 | 保持 | 关闭 | 较低 | (~50ms) |
| S3 | 挂起到内存(Suspend to RAM) | 断电 | 保持(自刷新) | 关闭 | 低 | (~100ms) |
| S4 | 挂起到磁盘(Hibernation) | 断电 | 内容写入磁盘 | 关闭 | 极低 | (~1s) |
| S5 | 软关机 | 断电 | 断电 | 断电 | 近零 | 需重启 |
嵌入式平台最常用S3(echo mem > /sys/power/state),S1和S2很多SoC不实现。S4在嵌入式上少见,桌面和笔记本用得多。S5就是关机,不算真正的休眠。
休眠流程
关键步骤说明:
1. 冻结进程:用户进程先冻,内核线程后冻结。冻结方式是设置PF_FROZEN标志,进程调度时检查该标志,发现被冻就主动睡眠
2. 同步文件系统:脏页写回磁盘,防止休眠后掉电丢数据
3. 逐设备suspend:按设备树从叶子到根的顺序调用,先挂起子设备再挂起父设备。这样保证父设备(比如I2C控制器)在子设备(I2C从设备)之后才关
4. 关闭中断:最后一步,关中断后系统不再响应外部事件,只等唤醒源
唤醒流程
唤醒是休眠的逆过程,但有个区别得注意:唤醒中断必须提前注册。休眠前内核会检查哪些中断是唤醒源(enable_irq_wake()),只有这些中断能唤醒系统。
驱动回调
驱动通过dev_pm_ops结构体注册回调:
// include/linux/pm.h
struct dev_pm_ops {
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze)(struct device *dev); // hibernation。 int (*thaw)(struct device *dev); // hibernation。 int (*poweroff)(struct device *dev); // hibernation。 int (*restore)(struct device *dev); // hibernation。 // Runtime PM回调
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
回调调用顺序和适用场景:
| 回调 | 调用时机 | 典型操作 |
|:-----|:---------|:---------|
| suspend | 休眠时,中断还开着 | 保存寄存器、关闭时钟、通知子设备 |
| suspend_late | 休眠时,中断已关 | 关闭最后遗留的硬件资源 |
| resume_early | 唤醒时,中断还没开 | 恢复最基础的硬件状态 |
| resume | 唤醒时,中断已开 | 恢复寄存器、重新初始化硬件 |
大多数驱动只实现suspend和resume就够了。suspend_late/resume_early是给需要在中断关闭。开启前做操作的驱动用的,比如中断控制器自己。
完整代码示例:Platform驱动的suspend/resume
// my_sensor_drv.c - 带PM回调的Platform驱动
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#include <linux/clk.h>
#include <linux/regulator/consumer.h>
#include <linux/io.h>
struct my_sensor_data {
void __iomem *base;
struct clk *clk;
struct regulator *vdd;
u32 saved_ctrl; // 休眠前保存控制寄存器
u32 saved_cfg; // 休眠前保存配置寄存器
};
static int my_sensor_suspend(struct device *dev)
{
struct my_sensor_data *data = dev_get_drvdata(dev);
// 保存寄存器,唤醒后恢。 data->saved_ctrl = readl(data->base + CTRL_REG);
data->saved_cfg = readl(data->base + CFG_REG);
// 关闭时钟
clk_disable_unprepare(data->clk);
// 关闭电源(如果设备有独立供电。 if (data->vdd)
regulator_disable(data->vdd);
dev_info(dev, "suspended\n");
return 0;
}
static int my_sensor_resume(struct device *dev)
{
struct my_sensor_data *data = dev_get_drvdata(dev);
int ret;
// 恢复电源
if (data->vdd) {
ret = regulator_enable(data->vdd);
if (ret) {
dev_err(dev, "failed to enable vdd: %d\n", ret);
return ret;
}
}
// 恢复时钟
ret = clk_prepare_enable(data->clk);
if (ret) {
dev_err(dev, "failed to enable clk: %d\n", ret);
return ret;
}
// 恢复寄存。 writel(data->saved_ctrl, data->base + CTRL_REG);
writel(data->saved_cfg, data->base + CFG_REG);
dev_info(dev, "resumed\n");
return 0;
}
// 用SET_SYSTEM_SLEEP_PM_OPS宏简化定。static const struct dev_pm_ops my_sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(my_sensor_suspend, my_sensor_resume)
};
static int my_sensor_probe(struct platform_device *pdev)
{
struct my_sensor_data *data;
struct resource *res;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
data->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(data->base))
return PTR_ERR(data->base);
data->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(data->clk))
return PTR_ERR(data->clk);
data->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
if (IS_ERR(data->vdd)) {
if (PTR_ERR(data->vdd) != -ENODEV)
return PTR_ERR(data->vdd);
data->vdd = NULL; // 没有独立供电,不影响
}
platform_set_drvdata(pdev, data);
return clk_prepare_enable(data->clk);
}
static void my_sensor_remove(struct platform_device *pdev)
{
struct my_sensor_data *data = platform_get_drvdata(pdev);
clk_disable_unprepare(data->clk);
}
static const struct of_device_id my_sensor_of_match[] = {
{ .compatible = "myvendor,my-sensor" },
{ }
};
MODULE_DEVICE_TABLE(of, my_sensor_of_match);
static struct platform_driver my_sensor_driver = {
.probe = my_sensor_probe,
.remove = my_sensor_remove,
.driver = {
.name = "my-sensor",
.of_match_table = my_sensor_of_match,
.pm = &my_sensor_pm_ops, // 挂载PM操作
},
};
module_platform_driver(my_sensor_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Sensor driver with suspend/resume support");
SET_SYSTEM_SLEEP_PM_OPS宏展开后给suspend和resume赋值,freeze/thaw/poweroff/restore设为NULL。如果驱动要支持hibernation,光靠这个宏不够,得手动填freeze和thaw。
三、运行时电源管理(Runtime PM)
系统休眠。一刀。——整台机器一起睡一起醒。但很多时候只需要让某个空闲设备省电,其他设备照常工作。Runtime PM就是干这个的:设备不用时自动挂起,要用时自动恢复,驱动只需要维护引用计数。
引用计数模型
Runtime PM的核心是一个引用计数器dev->power.usage_count。
| API | 作用 | 说明 |
|---|---|---|
pm_runtime_get_sync(dev) |
usage_count++,唤醒设备 | 同步等待resume完成,返。成功 |
pm_runtime_put_sync(dev) |
usage_count--,可能挂起 | count归零时触发runtime_idle→runtime_suspend |
pm_runtime_get_noresume(dev) |
usage_count++,不唤醒 | 只加计数,不触发resume |
pm_runtime_put_noidle(dev) |
usage_count--,不挂起 | 只减计数,不触发suspend |
pm_runtime_set_active(dev) |
标记设备为active | probe时调用,告诉PM核心设备已就绪 |
pm_runtime_set_suspended(dev) |
标记设备为suspended | 设备初始状态为挂起时使用 |
autosuspend:延迟挂。
设备刚idle又立刻被访问是常见场景——传感器读完一个数据,过几毫秒又来一个请求。如果每次put都立刻suspend,下次get又要resume,频繁开关反而更费电。autosuspend就是加个延迟:put后等一段时间,如果期间没有get请求,才真正suspend。
// 设置autosuspend延迟。00ms
pm_runtime_set_autosuspend_delay(dev, 200);
// 启用autosuspend
pm_runtime_use_autosuspend(dev);
// 用autosuspend版本的put
pm_runtime_put_sync_autosuspend(dev); // 200ms内没有get才真正suspend
autosuspend的延迟值需要根据实际场景调。太短没效果,太长又浪费电。一般传感器100~500ms,GPU这类重型设备可以。~2秒。
完整代码示例:I2C传感器的Runtime PM
// my_i2c_sensor.c - 带Runtime PM的I2C传感器驱动#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/pm_runtime.h>
#include <linux/delay.h>
struct my_i2c_sensor {
struct i2c_client *client;
struct mutex lock;
};
// 读传感器数据前先唤醒设备
static int my_i2c_sensor_read(struct my_i2c_sensor *sensor, u8 reg, u8 *val)
{
struct device *dev = &sensor->client->dev;
int ret;
// 唤醒设备,usage_count++
ret = pm_runtime_get_sync(dev);
if (ret < 0) {
pm_runtime_put_noidle(dev); // get失败也要减计数 return ret;
}
mutex_lock(&sensor->lock);
ret = i2c_smbus_read_byte_data(sensor->client, reg);
mutex_unlock(&sensor->lock);
if (ret >= 0)
*val = (u8)ret;
// 用完放回,usage_count--。00ms后自动suspend
pm_runtime_put_sync_autosuspend(dev);
return ret < 0 ? ret : 0;
}
static int my_i2c_sensor_runtime_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
// 进入低功耗模式,传感器停止采。 i2c_smbus_write_byte_data(client, 0x10, 0x00);
dev_dbg(dev, "runtime suspended\n");
return 0;
}
static int my_i2c_sensor_runtime_resume(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
// 退出低功耗模式,恢复采样
i2c_smbus_write_byte_data(client, 0x10, 0x01);
// 等待传感器稳。 msleep(10);
dev_dbg(dev, "runtime resumed\n");
return 0;
}
static int my_i2c_sensor_runtime_idle(struct device *dev)
{
// idle回调可选,返回0表示让PM核心继续走suspend流程
// 返回调表示阻止suspend
return 0;
}
static const struct dev_pm_ops my_i2c_sensor_pm_ops = {
SET_RUNTIME_PM_OPS(my_i2c_sensor_runtime_suspend,
my_i2c_sensor_runtime_resume,
my_i2c_sensor_runtime_idle)
// 系统休眠时也要处。 SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static int my_i2c_sensor_probe(struct i2c_client *client)
{
struct device *dev = &client->dev;
struct my_i2c_sensor *sensor;
int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
return -EIO;
sensor = devm_kzalloc(dev, sizeof(*sensor), GFP_KERNEL);
if (!sensor)
return -ENOMEM;
sensor->client = client;
mutex_init(&sensor->lock);
i2c_set_clientdata(client, sensor);
// 初始化Runtime PM
pm_runtime_set_active(dev);
pm_runtime_set_autosuspend_delay(dev, 200); // 200ms延迟挂起
pm_runtime_use_autosuspend(dev);
pm_runtime_enable(dev);
// probe期间设备是active的,probe结束后可以put让它idle
pm_runtime_put_sync_autosuspend(dev);
return 0;
}
static void my_i2c_sensor_remove(struct i2c_client *client)
{
struct device *dev = &client->dev;
// remove时确保设备active,禁用Runtime PM
pm_runtime_get_sync(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
}
static const struct of_device_id my_i2c_sensor_of_match[] = {
{ .compatible = "myvendor,i2c-sensor" },
{ }
};
MODULE_DEVICE_TABLE(of, my_i2c_sensor_of_match);
static struct i2c_driver my_i2c_sensor_driver = {
.probe = my_i2c_sensor_probe,
.remove = my_i2c_sensor_remove,
.driver = {
.name = "my-i2c-sensor",
.of_match_table = my_i2c_sensor_of_match,
.pm = &my_i2c_sensor_pm_ops,
},
};
module_i2c_driver(my_i2c_sensor_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("I2C sensor driver with Runtime PM");
几个要点。
1. pm_runtime_force_suspend/pm_runtime_force_resume。.8内核提供的辅助函数,系统休眠时自动调用Runtime PM的suspend/resume回调,避免写两套逻辑
2. pm_runtime_get_sync失败时必须调用pm_runtime_put_noidle,否则usage_count会泄。3. probe末尾调用pm_runtime_put_sync_autosuspend让设备进入idle状态,否则设备一直active,Runtime PM形同虚设
4. remove时先pm_runtime_get_sync确保设备active,再pm_runtime_disable禁用Runtime PM
四、电源域(Power Domain)
genpd框架
SoC上多个设备经常共享同一路电源。比如视频编解码器和显示控制器挂在同一个电源域上,任何一个设备在工作,这个电源域就不能关。Linux用generic power domain(genpd)框架管理这种共享关系。
genpd内部维护每个电源域的引用计数。域下任何一个设备active,整个域就on;所有设备都suspended,域才off。驱动不用手动操作电源域,Runtime PM和系统休眠框架会自动和genpd交互。
电源域驱动核心结。
// include/linux/pm_domain.h
struct generic_pm_domain {
const char *name; // 电源域名。 int (*power_off)(struct generic_pm_domain *pd);
int (*power_on)(struct generic_pm_domain *pd);
unsigned int device_count; // 域下设备。 unsigned int suspended_count; // 已挂起设备数
// ...锁、链表等内部字段
};
驱动需要实现power_on和power_off,里面写具体的硬件操作——通常是写SoC的PMU寄存器来开关某路电源。
设备树绑。
// 设备树中声明电源。power-domains {
pd_video: power-domain@0 {
compatible = "myvendor,power-domain";
reg = <0 0x10>; // PMU寄存器偏。 #power-domain-cells = <0>;
};
pd_gpu: power-domain@1 {
compatible = "myvendor,power-domain";
reg = <1 0x20>;
#power-domain-cells = <0>;
};
};
// 设备引用电源。&vpu {
power-domains = <&pd_video>;
};
&display {
power-domains = <&pd_video>; // 和VPU共享同一个域
};
&gpu {
power-domains = <&pd_gpu>;
};
设备树里power-domains属性指向电源域节点,内核解析后自动把设备挂到对应域下。驱动不用写额外代码,Runtime PM框架在suspend/resume时会自动通知genpd更新引用计数。
何时需要写电源域驱动
不是所有平台都需要自己写电源域驱动。判断标准:
| 场景 | 是否需要写 | 原因 |
|---|---|---|
| SoC厂商已提供genpd驱动(如RK、NXP BSP | 不需要 | 直接在设备树里引用即 |
| 自研SoC或FPGA平台 | 需要 | 没人帮你写,得自己实现power_on/power_off |
| 多设备共享电源且需要精细控制 | 需 | 标准regulator框架不够 |
| 设备有独立供电,不共享 | 不需 | 用regulator框架就行 |
Rockchip、NXP这些厂商的BSP里一般都有现成的genpd驱动,设备树配好power-domains属性就能用。自研平台才需要从头写。
五、系统休眠操。
手动触发休眠
# 挂起到内存S3)——最常用
echo mem > /sys/power/state
# 挂起到磁盘(S4)
echo disk > /sys/power/state
# 待机(S1)——部分平台支。echo freeze > /sys/power/state
# 查看当前支持的状态cat /sys/power/state
# 输出示例: freeze mem disk
freeze是内存.9引入的轻量级休眠,只冻结进程和设备,不关CPU和内存,唤醒最快但省电最少。适合桌面系统"合盖即走"的场景:
pm-test模式
内核提供PM测试模式,可以在不真正休眠的情况下验证驱动的suspend/resume回调是否正确。
# 查看可用测试级别
cat /sys/power/pm_test
# 输出: [none] core processors platform devices freezer
# 设置测试级别
echo devices > /sys/power/pm_test
# 触发休眠——只会走suspend/resume流程,不会真正断。echo mem > /sys/power/state
| 测试级别 | 测试内容 | 适用场景 |
|---|---|---|
none |
真正休眠 | 最终验 |
core |
核心PM子系 | 验证时钟、中断控制器 |
processors |
CPU热插 | 验证多核冻结/恢复 |
platform |
平台相关回调 | 验证SoC特定PM操作 |
devices |
设备suspend/resume | 验证驱动回调 |
freezer |
进程冻结 | 验证进程冻结/解冻 |
开发阶段用devices级别测试驱动回调,不用等真机休眠那么久。
RTC唤醒
调试休眠流程时,RTC唤醒比按电源键方便——可以设定自动唤醒时间:
# 清除之前的闹。echo 0 > /sys/class/rtc/rtc0/wakealarm
# 设置5秒后唤醒
echo +5 > /sys/class/rtc/rtc0/wakealarm
# 触发休眠。秒后自动唤醒
echo mem > /sys/power/state
RTC唤醒的前提是RTC驱动支持唤醒功能,且设备树里RTC节点配置了wakeup-source属性。
六、电源管理调试
/sys/power/目录
# 查看当前PM状态cat /sys/power/state
# 查看上次休眠成功/失败
cat /sys/power/suspend_stats/success
cat /sys/power/suspend_stats/fail
# 查看哪个设备导致休眠失败
cat /sys/power/suspend_stats/last_failed_dev
# 查看休眠耗时
cat /sys/power/suspend_stats/last_hardware
# Runtime PM状态cat /sys/devices/platform/soc/xxx/power/runtime_status
# 输出: active / suspended / unsupported
pm_trace:追踪休眠卡住位。
休眠卡死是最头疼的问题——系统冻住了,没任何输出,不知道卡在哪个驱动。pm_trace就是干这事的。
# 开启pm_trace
echo 1 > /sys/power/pm_trace
# 触发休眠
echo mem > /sys/power/state
# 如果卡死,重启后查看
# pm_trace把设备哈希写入RTC寄存器,重启后能读出。dmesg | grep "hash matches"
输出类似。
PM: hash matches drivers/clk/clk.c:245
这就告诉你卡在时钟框架的某个位置,排查范围一下就小了。
CONFIG_PM_DEBUG
内核配置选项,开启后提供更多PM调试信息。
CONFIG_PM_DEBUG=y # PM调试总开销CONFIG_PM_SLEEP_DEBUG=y # 休眠调试信息
CONFIG_PM_TRACE=y # pm_trace支持
CONFIG_PM_TRACE_RTC=y # pm_trace写RTC寄存```
开启`CONFIG_PM_SLEEP_DEBUG`后,`/sys/power/`下会多出`suspend_stats`的详细信息和每个设备的PM耗时统计数
### ftrace跟踪PM事件
```bash
# 使能PM相关tracepoint
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_frequency/enable
echo 1 > /sys/kernel/debug/tracing/events/power/cpu_idle/enable
echo 1 > /sys/kernel/debug/tracing/events/power/pm_qos/enable
# 使能设备suspend/resume trace
echo 1 > /sys/kernel/debug/tracing/events/device/dev_suspend/enable
echo 1 > /sys/kernel/debug/tracing/events/device/dev_resume/enable
# 抓取trace
echo > /sys/kernel/debug/tracing/trace
echo mem > /sys/power/state
cat /sys/kernel/debug/tracing/trace
ftrace能记录每个设备的suspend/resume耗时,快速定位哪个驱动拖慢了休眠。
七、常见问题
Q1:休眠后无法唤醒。
第一步查唤醒源。不是所有中断都能唤醒系统,只有通过enable_irq_wake()注册的中断才行。检查设备树里有没有配wakeup-source。
&gpio_keys {
wakeup-source; // 声明该设备可以唤醒系。};
第二步查驱动的resume回调。resume回调里如果操作了还没恢复的时钟或电源,会直接panic。resume顺序是先子后父,子设备的resume如果依赖父设备,得确保父设备先恢复。
第三步查regulator。有些电源域在休眠时被关了,唤醒后驱动得重新使能regulator。
Q2:Runtime PM导致设备不可用?
这是最常见的坑——设备已经suspended,驱动直接去读写寄存器,拿到00xFF。规则就一条:访问硬件前必须pm_runtime_get_sync。
// 错误:直接读寄存器,设备可能已suspended
val = readl(data->base + DATA_REG);
// 正确:先唤醒再读
pm_runtime_get_sync(dev);
val = readl(data->base + DATA_REG);
pm_runtime_put_sync_autosuspend(dev);
中断处理函数里也要注意。设备suspended时来了中断,需要在中断里先resume设备再处理数据:
static irqreturn_t my_isr(int irq, void *dev_id)
{
struct device *dev = dev_id;
// 中断上下文不能用pm_runtime_get_sync(会睡眠。 // 用pm_runtime_get_if_in_use检查设备状态 if (!pm_runtime_get_if_in_use(dev))
return IRQ_NONE; // 设备已suspended,忽略中。
// 处理中断...
pm_runtime_put_sync_autosuspend(dev);
return IRQ_HANDLED;
}
Q3:休眠耗时过长。
用pm_trace或ftrace定位卡在哪个驱动。常见原因:
- *驱动的suspend回调里调了睡眠函数:
msleep、usleep_range在suspend后期(中断已关)会死等。改用mdelay/udelay,或者把耗时操作移到suspend回调(中断还开着时) - 设备resume时重新初始化太慢:某些传感器上电后需要几百毫秒稳定,resume回调里等这么久会拖慢整个唤醒流程。可以只恢复寄存器,把传感器稳定放在工作队列里异步处。3. 文件系统同步太慢:休眠前sync写回大量脏页。可以在休眠前手动sync一次,减少休眠时的写回调
八、总结
速查看
| 类别 | 项目 | 关键字 |
|---|---|---|
| 系统休眠 | 触发 | echo mem/disk > /sys/power/state |
| 驱动回调 | suspend/resume/suspend_late/resume_early |
|
| 宏定 | SET_SYSTEM_SLEEP_PM_OPS(suspend_fn, resume_fn) |
|
| 测试 | echo devices > /sys/power/pm_test |
|
| Runtime PM | 引用计数 | pm_runtime_get_sync / pm_runtime_put_sync |
| autosuspend | pm_runtime_set_autosuspend_delay + pm_runtime_use_autosuspend |
|
| 宏定 | SET_RUNTIME_PM_OPS(suspend_fn, resume_fn, idle_fn) |
|
| 系统休眠复用 | pm_runtime_force_suspend / pm_runtime_force_resume |
|
| *电源 | 框架 | genpd,power_on/power_off |
| 设备 | power-domains = <&pd_xxx> |
|
| 调试 | 状态查看 | /sys/power/state、/sys/power/suspend_stats/ |
| 卡死追踪 | pm_trace:echo 1 > /sys/power/pm_trace |
|
| 耗时分析 | ftrace:events/device/dev_suspend |
|
| RTC唤醒 | echo +5 > /sys/class/rtc/rtc0/wakealarm |
本文首发于linuxros.cn,转载请注明出处。