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要表达的正是这种“设备—资源—域”的依赖关系:设备属于哪个电源域、由哪个稳压器供电、需要哪路时钟、复位由谁控制、能否作为唤醒源。内核据此在系统休眠时按域断电、按需保留唤醒路径。

设备树 / ACPI 电源描述:设备—资源—电源域依赖关系 设备树(DT)描述 power-domains = <&pd_gpu> (设备归属电源域) vdd-supply = <&reg_vdd_gpu> (稳压器供电) clocks = <&clk_gpu> clock-names = "core" resets = <&rst_gpu> reset-names = "rst" interrupts = <GIC_SPI 45 ...> wakeup-source; operating-points-v2 (性能/电压档位) 由内核DT解析器转换为device结构 内核电源资源框架 genpd 电源域框架 Regulator Framework Common Clock Framework Reset Controller Framework Wakeup / IRQ 子系统 Runtime PM / System PM 统一资源句柄:devm_* 获取与释放 生命周期:probe → runtime_suspend → resume → remove ACPI 描述 _PR0 / _PR3 电源资源 (Power Resource) _PS0 / _PS3 电源状态 (D0 / D3 设备状态) _DSD 设备属性 (时钟/复位等扩展) _PRW 唤醒源 (GPE / 唤醒能力) _S0W / _S3W (可唤醒的睡眠深度) _PSC / _PSE 电源状态转换 (Power State Change) 由ACPI解释器转换为device结构

方法流程:从硬件描述到驱动资源获取

无论DT还是ACPI,内核都会把描述解析为统一的struct device及其资源。驱动侧通过标准API获取资源,典型流程如下:

  1. 解析阶段:DT由of_platform_populate()展开节点,ACPI由acpi_bus_scan()枚举设备,生成platform_device或acpi_device。
  2. 绑定阶段:驱动probe时通过device_property_read_*()、devm_regulator_get()、devm_clk_get()、devm_reset_control_get()等获取资源句柄。
  3. 电源域关联:调用dev_pm_domain_attach()把设备挂到genpd域,域内设备共享上电/断电时序。
  4. 唤醒配置:通过device_set_wakeup_capable()与enable_irq_wake()把中断注册为唤醒源。
  5. 生命周期协同: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 + PowerResourcedev_pm_domain_attach()
稳压器vdd-supply = <&reg>_PR0 引用的电源资源devm_regulator_get()
时钟clocks / clock-names_DSD 或平台固件表devm_clk_get()
复位resets / reset-names_DSD 扩展devm_reset_control_get()
唤醒源wakeup-source; + interrupts_PRW / _S0Wdevice_set_wakeup_capable()
电源状态operating-points-v2_PS0/_PS3pm_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方法,并在休眠前后对比功耗与唤醒日志,形成闭环验证。