4. 设备树与ACPI中的电源描述
在Linux设备电源与生命周期协同中,硬件描述是驱动能够正确管理电源的前提。设备树(Device Tree,DT)与ACPI是Linux内核识别硬件的两大描述机制:ARM/ARM64、RISC-V等嵌入式平台普遍使用设备树,而x86服务器、笔记本及部分ARM服务器则依赖ACPI。二者虽然语法与解析路径不同,但目标一致——把“设备依赖哪些电源、时钟、复位、唤醒资源”以数据形式交给内核,让驱动在probe、suspend、resume、remove各阶段有据可依。
核心原理:电源描述的本质是“资源依赖图”
一个SoC内部往往有多个电源域(Power Domain),例如CPU域、GPU域、显示域、音频域、存储域。每个域可能由独立稳压器(Regulator)供电,域内设备共享时钟(Clock)、复位线(Reset)和唤醒中断(Wakeup IRQ)。设备树与ACPI要表达的正是这种“设备—资源—域”的依赖关系:设备属于哪个电源域、由哪个稳压器供电、需要哪路时钟、复位由谁控制、能否作为唤醒源。内核据此在系统休眠时按域断电、按需保留唤醒路径。
方法流程:从硬件描述到驱动资源获取
无论DT还是ACPI,内核都会把描述解析为统一的struct device及其资源。驱动侧通过标准API获取资源,典型流程如下:
- 解析阶段:DT由
of_platform_populate()展开节点,ACPI由acpi_bus_scan()枚举设备,生成platform_device或acpi_device。 - 绑定阶段:驱动probe时通过
device_property_read_*()、devm_regulator_get()、devm_clk_get()、devm_reset_control_get()等获取资源句柄。 - 电源域关联:调用
dev_pm_domain_attach()把设备挂到genpd域,域内设备共享上电/断电时序。 - 唤醒配置:通过
device_set_wakeup_capable()与enable_irq_wake()把中断注册为唤醒源。 - 生命周期协同:Runtime PM在空闲时关闭时钟/稳压器,System PM在休眠时按域断电,仅保留唤醒路径。
/* 驱动中获取电源相关资源的典型片段 */
struct regulator *vdd;
struct clk *core_clk;
struct reset_control *rst;
vdd = devm_regulator_get(dev, "vdd");
core_clk = devm_clk_get(dev, "core");
rst = devm_reset_control_get_exclusive(dev, "rst");
ret = dev_pm_domain_attach(dev, true); /* 关联电源域 */
device_set_wakeup_capable(dev, true); /* 声明可唤醒 */
案例场景:显示控制器休眠唤醒
以某ARM64平台显示控制器为例,设备树中它归属pd_display电源域,由reg_lcd供电,使用clk_disp像素时钟,复位线为rst_disp,并声明wakeup-source。系统进入suspend时,genpd按域关闭显示电源,时钟框架关闭clk_disp,稳压器进入低功耗模式;当触摸中断触发时,唤醒源把系统拉起,resume阶段按相反顺序恢复稳压器、时钟、复位释放,显示控制器重新初始化。若设备树漏写power-domains,设备可能被错误地留在上电域,导致休眠功耗偏高;若漏写wakeup-source,触摸中断无法唤醒系统。
对比与清单:DT与ACPI电源描述对照
| 资源类型 | 设备树写法 | ACPI写法 | 驱动获取API |
|---|---|---|---|
| 电源域 | power-domains = <&pd> | _PR0/_PR3 + PowerResource | dev_pm_domain_attach() |
| 稳压器 | vdd-supply = <®> | _PR0 引用的电源资源 | devm_regulator_get() |
| 时钟 | clocks / clock-names | _DSD 或平台固件表 | devm_clk_get() |
| 复位 | resets / reset-names | _DSD 扩展 | devm_reset_control_get() |
| 唤醒源 | wakeup-source; + interrupts | _PRW / _S0W | device_set_wakeup_capable() |
| 电源状态 | operating-points-v2 | _PS0/_PS3 | pm_runtime_*() |
cat /proc/device-tree/... 查看DT节点,使用 acpidump 与 acpixtract 反编译ACPI表;通过 /sys/kernel/debug/pm_genpd/ 观察电源域状态,通过 /sys/kernel/debug/clk/clk_summary 核对时钟开关。误区与复盘
- 只写供电不写域:稳压器与电源域是两个层次,漏写
power-domains会导致域级断电时序缺失。 - 时钟名不匹配:
clock-names与驱动devm_clk_get()的字符串必须一致,否则返回-ENOENT。 - 唤醒源未使能:仅声明
wakeup-source不够,还需在驱动中调用enable_irq_wake()。 - ACPI与DT混用:同一平台不应同时依赖两套描述,否则资源可能重复或冲突。
- 复位顺序错误:上电应先稳压器、再时钟、最后释放复位;下电顺序相反,否则可能损坏器件。
dtc反编译核对DT,用acpiexec验证ACPI方法,并在休眠前后对比功耗与唤醒日志,形成闭环验证。