5. Wakeup源电源域:Power Domain概念

聊到Wakeup源,有个概念绕不开——电源域(Power Domain)

说白了,电源域就是芯片内部一块独立的供电区域。你可以把它想象成一座大楼里的独立电表。每个区域可以单独开关电,互不影响。

我刚开始接触电源管理时,觉得这玩意儿不就是个开关嘛。后来踩了坑才明白,电源域的划分直接决定了你的设备能不能正常唤醒。

5.1 什么是Power Domain?

在SoC设计中,芯片被划分成多个电源域。每个域有自己的供电轨(power rail)。

  • Always-on Domain:永远带电的区域。比如RTC、唤醒逻辑、一部分GPIO。
  • Core Domain:CPU核心所在的区域。休眠时可以完全断电。
  • Peripheral Domain:外设区域。比如USB、I2C、SPI控制器。
  • Memory Domain:内存控制器和缓存区域。

每个域之间通过电源开关(Power Switch)电平转换器(Level Shifter)连接。

举个例子:

// 电源域划分示意(伪代码)
power_domain pd_always_on {
    voltage = "1.8V";
    always_on;
    includes = <&rtc &wakeup_ctrl &gpio0>;
};

power_domain pd_core {
    voltage = "0.9V";
    power_switch = "sw_core";
    includes = <&cpu0 &cpu1 &l2_cache>;
};

power_domain pd_usb {
    voltage = "1.2V";
    power_switch = "sw_usb";
    parent = &pd_always_on;  // 依赖always-on域
};

嗯,这里要注意:Wakeup源必须位于Always-on域中。否则你设备都断电了,谁来检测唤醒信号?

5.2 Wakeup源与电源域的关系

这个关系其实很直接:

  • Wakeup源所在的电源域不能断电。至少它的检测逻辑必须带电。
  • 被唤醒的目标域可以断电。比如CPU Core域在休眠时完全断电,但Always-on域里的GPIO检测到按键后,再给Core域上电。
  • 跨域唤醒需要电平转换。Always-on域可能是1.8V,Core域是0.9V,信号传递必须经过电平转换。

我在项目中遇到过一个问题:某个GPIO作为Wakeup源,但它在设备树里配置的电源域是Core域。结果系统休眠后Core域断电,这个GPIO根本检测不到按键。折腾了两天才发现是电源域配错了。

警告:Wakeup源必须位于Always-on或不断电的电源域中。如果配错,休眠后无法唤醒,只能按Reset键。

5.3 设备树中power-domains属性配置

设备树里怎么配?看这个例子:

// 定义电源域
power-domains {
    pd_always_on: pd_always_on@0 {
        compatible = "mediatek,power-domain";
        reg = <0>;
        #power-domain-cells = <0>;
        domain-name = "always_on";
        always-on;
    };

    pd_core: pd_core@1 {
        compatible = "mediatek,power-domain";
        reg = <1>;
        #power-domain-cells = <0>;
        domain-name = "core";
        power-switch = &sw_core;
    };
};

// 设备绑定电源域
&uart0 {
    compatible = "mediatek,uart";
    reg = <0x11002000 0x1000>;
    interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_LOW>;
    clocks = &<&uart_clk>;
    power-domains = <&pd_always_on>;  // UART在Always-on域
    wakeup-source;                     // 标记为Wakeup源
};

&gpio_key {
    compatible = "gpio-keys";
    power-domains = <&pd_always_on>;  // 按键必须在Always-on域
    wakeup-source;
    
    key-vol-up {
        label = "Volume Up";
        gpios = <&gpio0 0 GPIO_ACTIVE_LOW>;
        linux,code = <KEY_VOLUMEUP>;
    };
};

关键点:

  • power-domains属性指定设备属于哪个电源域
  • wakeup-source属性标记该设备可以作为唤醒源
  • Always-on域的设备不需要power-domains属性?错!必须显式指定,否则内核不知道它属于哪个域
小技巧:我习惯在设备树里给所有Wakeup源加上注释,标明它所在的电源域。比如:
// Wakeup源 - Always-on域
这样review代码时一眼就能看出问题。

5.4 电源域配置的常见坑

我曾经踩过一个坑,分享给你:

某款平板电脑,休眠后无法用USB唤醒。查了很久,发现USB控制器在设备树里配了power-domains = &pd_usb,而pd_usb的父域是pd_core。休眠时Core域断电,USB域也跟着断了。USB的Wakeup信号根本传不到Always-on域。

解决办法:把USB的Wakeup检测逻辑放到Always-on域里,或者让USB域独立供电。

嗯,这里有个原则:Wakeup源的检测逻辑必须和它的供电域在同一个Always-on域中。如果做不到,至少保证检测逻辑的供电不依赖于休眠断电的域。

5.5 知识体系图

下面这张图展示了Wakeup源与电源域的关系:

Always-on Domain 始终带电 Wakeup源 GPIO / RTC / USB检测 唤醒检测逻辑 电平检测 / 边沿触发 电源开关控制 控制Core域上电/下电 Core Domain 休眠时可断电 CPU Core 执行唤醒处理流程 外设(USB/I2C) 需要重新初始化 内存控制器 自刷新模式 唤醒信号 电源开关控制信号 电平转换 Always-on域 Core域

从这张图可以看出:

  • Wakeup源和唤醒检测逻辑都在Always-on域中
  • 唤醒信号从Always-on域传到Core域
  • 电源开关控制信号从Always-on域控制Core域的上电/下电
  • 两个域之间需要电平转换

5.6 实际项目中的配置建议

最后,给你几个我在实际项目中总结的建议:

  1. 先画电源域拓扑图。别急着写代码,先搞清楚芯片里哪些域是Always-on,哪些可以断电。
  2. Wakeup源统一放在Always-on域。如果芯片不支持,至少保证检测逻辑在Always-on域。
  3. 设备树里显式配置power-domains。不要依赖默认值,默认值往往是错的。
  4. 验证时测试休眠唤醒。用echo mem > /sys/power/state进入休眠,然后触发Wakeup源,看能不能正常唤醒。
  5. 检查内核日志。如果唤醒失败,dmesg里通常会有电源域相关的错误信息。
核心要点:电源域是Wakeup源的基础。没有正确的电源域配置,Wakeup源就是空中楼阁。记住一句话:Wakeup源必须在Always-on域中

好了,这一章就到这里。电源域的概念其实不难,难的是在实际项目中把每个设备的电源域都配对。多画图、多验证,慢慢就有感觉了。