电源管理节点语法:设备树电源管理节点基本语法、regulator节点、power-domain节点
好,咱们今天聊聊设备树里电源管理节点的语法。说实话,这部分内容在刚接触设备树时容易让人头大——各种属性、各种绑定,稍不留神就写错了。我刚开始做电源管理时,就因为在regulator节点里少写了一个regulator-min-microvolt,结果板子死活起不来。嗯,从那以后我就养成了仔细核对语法的习惯。
电源管理节点基本语法
设备树里的电源管理节点,说白了就是告诉内核:我这板子上有哪些电源设备、它们怎么控制、电压电流范围是多少。每个电源设备都是一个独立的节点,挂在对应的总线上。
基本结构长这样:
/ {
regulators {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <0>;
my_regulator: my-regulator {
compatible = "regulator-fixed";
regulator-name = "my-fixed-supply";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
gpio = <&gpio1 16 GPIO_ACTIVE_HIGH>;
enable-active-high;
regulator-boot-on;
};
};
};
这里有几个关键点:
- compatible:匹配驱动用的,比如
regulator-fixed对应固定电压 regulator - regulator-name:给这个电源起个名字,调试时有用
- regulator-min/max-microvolt:电压范围,单位是微伏
- gpio:控制使能的 GPIO 引脚
- enable-active-high:高电平使能,反之用
enable-active-low - regulator-boot-on:内核启动时就打开这个 regulator
regulators 的子节点里,compatible 用 simple-bus。这样结构清晰,也方便后续维护。
regulator节点详解
regulator 节点是电源管理的核心。它不只是描述一个电源,还描述了电源之间的依赖关系。你想想看,一个 SoC 可能有几十路供电,有些必须先上电,有些可以后上电,这些都得在设备树里说清楚。
来看一个更复杂的例子——可调电压 regulator:
vcc_core: vcc-core {
compatible = "regulator-gpio";
regulator-name = "vcc_core";
regulator-min-microvolt = <900000>;
regulator-max-microvolt = <1300000>;
regulator-boot-on;
regulator-always-on;
gpios = <&gpio2 5 GPIO_ACTIVE_HIGH>,
<&gpio2 6 GPIO_ACTIVE_HIGH>;
gpios-states = <1 1>;
states = <900000 0x0>,
<1100000 0x1>,
<1200000 0x2>,
<1300000 0x3>;
};
这里多了几个属性:
- gpios-states:GPIO 的初始状态
- states:电压值与 GPIO 状态的映射表。比如
<900000 0x0>表示 GPIO 输出 0b00 时输出 0.9V - regulator-always-on:永远不关闭,常用于核心供电
states 表写错了顺序,导致电压切换时瞬间过压,烧坏了好几块板子。所以,states 里的 GPIO 状态值一定要和硬件设计一一对应,千万别想当然。
regulator 节点还有一个重要的概念——消费者。设备节点通过 *-supply 属性引用 regulator:
&mmc0 {
vmmc-supply = <&vcc_sd>;
vqmmc-supply = <&vcc_sd_io>;
};
这里的 vmmc-supply 和 vqmmc-supply 就是消费者属性。内核会根据这些属性自动管理 regulator 的开关和电压。
power-domain节点
power-domain(电源域)是比 regulator 更高层次的抽象。一个 power-domain 可能包含多个 regulator、时钟、甚至其他 power-domain。它的作用是把一组相关的电源资源打包管理,实现整体开关。
我举个例子:一个 GPU 模块,它需要核心电压、内存电压、还有一组时钟。如果每个都单独控制,代码会变得很复杂。用 power-domain 就可以统一管理。
基本语法如下:
power-domains {
pd_gpu: power-domain@0 {
compatible = "ti,sci-power-domain";
#power-domain-cells = <0>;
ti,sci-dev-id = <123>;
ti,sci-rm-range-gp = <0x1>;
domain-id = <0>;
label = "gpu_pd";
};
};
设备节点引用 power-domain:
&gpu {
power-domains = <&pd_gpu>;
clocks = <&clk_gpu_core>, <&clk_gpu_mem>;
};
这里 power-domains 属性指向了 pd_gpu 节点。内核在操作 GPU 设备时,会自动先打开对应的 power-domain。
PD_TOP 包含 PD_GPU 和 PD_DSP。关闭 PD_TOP 时,所有子域都会关闭。这种层级关系在设备树里通过 power-domains 属性引用父域来实现。
来看一个嵌套的例子:
pd_top: power-domain@1 {
compatible = "generic-power-domain";
#power-domain-cells = <0>;
domain-id = <1>;
};
pd_gpu: power-domain@2 {
compatible = "generic-power-domain";
#power-domain-cells = <0>;
power-domains = <&pd_top>; /* 父域是 PD_TOP */
domain-id = <2>;
};
这里 pd_gpu 的 power-domains 属性指向了 pd_top,表示它是 PD_TOP 的子域。内核在操作时,会先确保父域已经开启。
知识体系结构图
下面这张图展示了电源管理节点的核心逻辑关系:
避坑指南与经验总结
做了这么多年电源管理,我踩过不少坑。这里分享几个最常见的:
- regulator 节点重复定义:同一个 regulator 名字在多个 dts 文件里出现,导致覆盖。我建议用
&节点名的方式引用修改,不要重新定义整个节点。 - 电压范围设置过宽:有些新手把
regulator-min-microvolt设成 0,regulator-max-microvolt设成很大值。这会导致内核调压时跑到硬件不支持的值。一定要严格按照硬件手册来。 - power-domain 循环依赖:A 域依赖 B 域,B 域又依赖 A 域。内核在初始化时会死锁。我曾经排查过一个这样的问题,花了整整两天才找到原因。
- 忘记加
#power-domain-cells:这个属性告诉内核这个 power-domain 需要几个参数。如果漏了,引用它的设备会解析失败。
regulator.disable_early_printk=1 和 debug 参数,可以看到 regulator 的注册和使能过程。如果某个 regulator 没起来,日志里会有明确的错误信息。
嗯,以上就是电源管理节点语法的核心内容。说白了,regulator 管的是具体的电压电流,power-domain 管的是模块级的电源状态。两者配合使用,才能实现精细化的电源管理。写设备树时,多对照硬件原理图,多检查属性值,能省去很多调试时间。
公众号:蓝海资料掘金营,微信deep3321