Wakeup源基础:什么是Wakeup源
各位同学,咱们今天聊聊Wakeup源。说白了,Wakeup源就是能让系统从睡眠状态醒过来的那根「神经末梢」。我刚开始接触Android电源管理时,总觉得系统休眠了就彻底「死」了,后来才发现——它其实一直在「假寐」,耳朵竖着听呢。
系统进入suspend状态后,CPU停了,外设大部分断电了,但总得留几个「哨兵」吧?这些哨兵就是Wakeup源。它们能在特定事件发生时,比如你按一下电源键、插上充电器、收到一条网络唤醒包,把整个系统从睡梦中叫醒。
Wakeup源的类型
我在项目中见过的Wakeup源,大致可以分为以下几类。每种都有自己的脾气,配置不对的话,系统要么睡不踏实,要么醒不过来。
1. GPIO Wakeup源
这是最常用的一种。说白了,就是某个GPIO引脚的电平变化能唤醒系统。比如手机侧边的音量键、电源键,本质上都是GPIO。
配置时要注意一个关键点:中断触发方式。上升沿、下降沿、还是双边沿?我踩过一个坑——某款平板用下降沿唤醒,结果用户按电源键时手抖了一下,产生了两次下降沿,系统刚醒又睡了。后来改成双边沿触发,问题解决。
wakeup-source 属性,否则内核默认不会把它当作唤醒源处理。我曾经因为这个属性漏写,排查了整整两天。
2. RTC Wakeup源
RTC(实时时钟)唤醒,就是定时闹钟。系统可以设定一个未来时间点,到点自动唤醒。这个在嵌入式设备里用得特别多——比如安防摄像头,平时休眠,每5分钟醒来拍一张照片。
RTC唤醒的精度取决于硬件。大部分RTC支持到秒级,有些高精度的能到毫秒。但说实话,Android系统从suspend到完全恢复,本身就需要几百毫秒,所以秒级精度完全够用。
3. 网络Wakeup源
这个就高级一点了。网卡或Wi-Fi模块在系统休眠时,仍然保持低功耗监听状态。一旦收到特定的网络包(比如魔术包Magic Packet),就能唤醒系统。
我记得有个项目,客户要求手机在休眠状态下能被远程唤醒。我们用的是Wi-Fi的WoWLAN(Wake on Wireless LAN)功能。配置起来挺折腾的——不光要改内核驱动,还得在用户态配好网络过滤规则。不然的话,随便一个广播包都能把系统叫醒,那电池根本扛不住。
4. USB Wakeup源
USB唤醒也很常见。插上充电器、或者插入USB设备时唤醒系统。这个在车机上用得特别多——你开车门,车载系统就该醒了。
USB唤醒的配置要点是控制器必须保持供电。有些低功耗设计会把USB控制器整个断电,那插USB就没反应了。我建议在设备树中给USB控制器加上 wakeup-source,同时确保对应的电源域(power domain)在suspend时不断电。
5. 其他Wakeup源
| 类型 | 典型场景 | 注意事项 |
|---|---|---|
| PMIC Wakeup | 电源管理芯片检测到充电器插入 | PMIC的中断必须直连到SoC的唤醒引脚 |
| 传感器Wakeup | 加速度计检测到运动 | 传感器本身要支持低功耗模式 |
| 按键Wakeup | 专用唤醒按键 | 按键消抖时间要合理,太短容易误触发 |
Wakeup源在设备树中的配置
设备树(Device Tree)是描述硬件配置的「户口本」。Wakeup源的配置,说白了就是在设备树里告诉内核:这个设备能唤醒系统,以及怎么唤醒。
咱们来看一个实际的例子。这是一个GPIO按键作为Wakeup源的设备树配置:
// 设备树片段:GPIO按键作为Wakeup源
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&key_pins>;
power-key {
label = "Power Key";
gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
linux,code = <KEY_POWER>;
gpio-key,wakeup; // 关键属性:标记为唤醒源
wakeup-source; // 标准唤醒源属性
debounce-interval = <10>; // 消抖时间10ms
};
};
这里有两个关键属性:gpio-key,wakeup 和 wakeup-source。前者是GPIO按键子系统的专有属性,后者是内核通用的唤醒源标记。我建议两个都写上,兼容性更好。
wakeup-source。比如:
&gpio1 {
wakeup-source;
};
否则,即使按键节点配了,底层GPIO控制器也不会响应唤醒。
再看一个RTC唤醒的例子:
&rtc {
status = "okay";
wakeup-source; // RTC作为唤醒源
rtc-wakealarm-enable; // 启用闹钟唤醒功能
};
RTC的配置相对简单。但要注意,有些SoC的RTC模块在suspend时会被关闭时钟,这时候需要检查RTC是否位于「always-on」电源域。我遇到过一款芯片,RTC的电源域默认是关的,得在pinctrl里单独配一个保持供电的引脚。
网络Wakeup的配置稍微复杂一些:
ð {
status = "okay";
wakeup-source;
magic-packet; // 支持魔术包唤醒
phy-mode = "rgmii";
phy-handle = <&phy0>;
phy0: ethernet-phy@0 {
reg = <0>;
wakeup-source; // PHY芯片也要支持唤醒
};
};
这里有个坑:PHY芯片的唤醒支持。很多以太网PHY本身有低功耗监听模式,但需要驱动在suspend时正确配置。我建议在驱动里加上 dev_pm_set_wake_irq() 来注册唤醒中断,否则设备树配得再好也没用。
- 在设备节点中加上
wakeup-source属性 - 确保父节点(如GPIO控制器、电源域)也支持唤醒
- 驱动中调用
device_init_wakeup()和dev_pm_set_wake_irq()完成软件注册
嗯,到这里Wakeup源的基础知识就讲完了。记住一句话:Wakeup源是系统休眠时的「哨兵」,配置好了,系统该睡睡、该醒醒;配不好,要么睡死过去,要么频繁被误唤醒,电池哗哗掉电。我在项目中吃过不少亏,希望你们能少走弯路。