Wakeup源基础:什么是Wakeup源

各位同学,咱们今天聊聊Wakeup源。说白了,Wakeup源就是能让系统从睡眠状态醒过来的那根「神经末梢」。我刚开始接触Android电源管理时,总觉得系统休眠了就彻底「死」了,后来才发现——它其实一直在「假寐」,耳朵竖着听呢。

系统进入suspend状态后,CPU停了,外设大部分断电了,但总得留几个「哨兵」吧?这些哨兵就是Wakeup源。它们能在特定事件发生时,比如你按一下电源键、插上充电器、收到一条网络唤醒包,把整个系统从睡梦中叫醒。

核心定义: Wakeup源是能够将系统从低功耗状态(suspend/hibernate)唤醒到正常工作状态的硬件或软件触发机制。每个Wakeup源都对应一个中断信号,这个信号必须能穿透电源管理单元的层层封锁,直达唤醒逻辑。

Wakeup源的类型

我在项目中见过的Wakeup源,大致可以分为以下几类。每种都有自己的脾气,配置不对的话,系统要么睡不踏实,要么醒不过来。

1. GPIO Wakeup源

这是最常用的一种。说白了,就是某个GPIO引脚的电平变化能唤醒系统。比如手机侧边的音量键、电源键,本质上都是GPIO。

配置时要注意一个关键点:中断触发方式。上升沿、下降沿、还是双边沿?我踩过一个坑——某款平板用下降沿唤醒,结果用户按电源键时手抖了一下,产生了两次下降沿,系统刚醒又睡了。后来改成双边沿触发,问题解决。

经验之谈: GPIO作为Wakeup源时,建议在设备树中明确指定 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,wakeupwakeup-source。前者是GPIO按键子系统的专有属性,后者是内核通用的唤醒源标记。我建议两个都写上,兼容性更好。

注意: 有些SoC的GPIO控制器本身不支持唤醒,需要在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的配置稍微复杂一些:

&eth {
    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源的设备树配置,核心就三步:
  1. 在设备节点中加上 wakeup-source 属性
  2. 确保父节点(如GPIO控制器、电源域)也支持唤醒
  3. 驱动中调用 device_init_wakeup()dev_pm_set_wake_irq() 完成软件注册
少一步,系统都可能醒不过来。
Wakeup源知识体系 Wakeup源 Wakeup源类型 GPIO RTC 网络 USB 其他 设备树配置要点 ① 节点加 wakeup-source 标记唤醒能力 ② 父节点支持唤醒 GPIO控制器/电源域 ③ 驱动软件注册 device_init_wakeup() 少一步,系统都可能醒不过来

嗯,到这里Wakeup源的基础知识就讲完了。记住一句话:Wakeup源是系统休眠时的「哨兵」,配置好了,系统该睡睡、该醒醒;配不好,要么睡死过去,要么频繁被误唤醒,电池哗哗掉电。我在项目中吃过不少亏,希望你们能少走弯路。

公众号:蓝海资料掘金营,微信deep3321