4、复位控制器节点:reset-cells属性、复位提供者与消费者、复位信号索引、复位控制器的设备树表示
好,咱们接着聊复位控制器。说实话,时钟和复位在嵌入式系统里就像一对双胞胎,谁也离不开谁。时钟给系统提供心跳,复位则保证系统每次都能从干净的状态启动。我在调试一块新板子时,就遇到过因为复位信号没配好,导致外设死活不干活的情况。折腾了两天才发现,原来是复位控制器的设备树描述出了问题。
今天这一节,咱们就专门把复位控制器的设备树表示讲透。你想想看,一个SoC里那么多模块,每个模块都需要复位信号。谁提供复位?谁使用复位?信号怎么索引?这些都得在设备树里说清楚。
4.1 复位提供者与消费者模型
跟时钟一样,复位也分提供者和消费者。提供者就是能产生复位信号的硬件模块,比如系统级的复位控制器。消费者就是需要被复位的那些外设IP。
我个人习惯把复位控制器想象成一个「复位信号分发中心」。它内部有多个复位输出,每个输出对应一个索引号。外设想要复位自己,就通过设备树告诉内核:「我要用复位控制器输出的第几路信号」。
举个例子,一个典型的复位控制器可能管理着USB、Ethernet、I2C等模块的复位。每个模块对应一个复位输出索引。设备树里就这么描述:
// 复位提供者:复位控制器
rstc: reset-controller@1000e000 {
compatible = "vendor,syscon-reset";
reg = <0x1000e000 0x1000>;
#reset-cells = <1>; // 一个cell表示复位索引
};
// 复位消费者:USB控制器
usb: usb@12000000 {
compatible = "vendor,usb-controller";
reg = <0x12000000 0x10000>;
interrupts = <0 42 4>;
resets = <&rstc 3>; // 使用复位控制器的第3路复位
};
这里的关键就是 resets 属性。它告诉内核:这个USB外设的复位信号,来自 rstc 这个复位控制器的第3路输出。
核心要点:
#reset-cells:复位提供者声明,表示需要几个cell来描述一个复位信号resets:复位消费者使用,引用复位控制器并指定索引- 提供者与消费者通过 phandle + 索引的方式建立连接
4.2 reset-cells 属性的含义
#reset-cells 这个属性,说白了就是告诉别人:「你要用我的复位信号,得给我几个参数?」
最常见的值是1,表示只需要一个复位索引号。但有些复杂的复位控制器,可能还需要额外的参数,比如复位类型(硬复位还是软复位)、复位持续时间等。这时候 #reset-cells 就可能等于2或3。
我见过一个比较特殊的案例,某个FPGA厂商的复位控制器用了 #reset-cells = <2>。第一个cell是复位索引,第二个cell是复位极性(高有效还是低有效)。当时我接手这个项目时,看到这个配置还愣了一下,后来查了手册才明白。
来看一个 #reset-cells = <2> 的例子:
rstc: reset-controller@20000000 {
compatible = "vendor,complex-reset";
reg = <0x20000000 0x1000>;
#reset-cells = <2>; // 需要两个参数
};
// 消费者使用
uart: serial@21000000 {
compatible = "vendor,uart";
reg = <0x21000000 0x1000>;
resets = <&rstc 5 0>; // 索引5,低有效复位
};
我的建议: 除非硬件手册明确要求,否则尽量用 #reset-cells = <1>。参数越少,设备树越简洁,出错的概率也越低。我在项目中一般只用一个索引,其他复位参数通过驱动内部的配置寄存器来处理。
4.3 复位信号索引的设计
复位信号索引怎么分配?这其实是个设计问题。我见过两种做法:
- 按模块功能分配:比如0-7给高速外设(USB、PCIe),8-15给低速外设(I2C、SPI),16-23给内部模块(DMA、Timer)。
- 按硬件寄存器位分配:直接对应复位控制寄存器中的bit位置。比如bit0控制模块A,bit1控制模块B,索引就直接用bit号。
我个人更倾向于第二种。为什么?因为硬件设计就是这么来的。复位控制器的寄存器,每个bit控制一个模块的复位。索引号直接对应bit位置,驱动实现起来也简单,读寄存器、写寄存器,一目了然。
来看一个实际的设备树片段:
// 假设复位控制寄存器布局:
// bit0: CPU core reset
// bit1: GPU reset
// bit2: DSP reset
// bit3: USB reset
// bit4: Ethernet reset
rstc: reset-controller@30000000 {
compatible = "vendor,bit-reset";
reg = <0x30000000 0x1000>;
#reset-cells = <1>;
};
gpu: gpu@40000000 {
compatible = "vendor,gpu";
reg = <0x40000000 0x10000>;
resets = <&rstc 1>; // bit1,对应GPU复位
};
usb: usb@50000000 {
compatible = "vendor,usb";
reg = <0x50000000 0x10000>;
resets = <&rstc 3>; // bit3,对应USB复位
};
注意: 索引号一定要和硬件手册保持一致。我曾经遇到过一个问题:硬件工程师改了复位控制器的寄存器映射,但没更新设备树。结果内核复位了错误的模块,系统直接崩溃。所以每次硬件改版,设备树一定要同步更新。
4.4 复位控制器的完整设备树表示
好了,咱们把前面讲的内容串起来,看一个完整的复位控制器设备树节点应该怎么写。
一个标准的复位控制器节点,通常包含以下属性:
| 属性 | 说明 | 是否必需 |
|---|---|---|
| compatible | 兼容字符串,用于匹配驱动 | 是 |
| reg | 寄存器地址和大小 | 是 |
| #reset-cells | 复位描述需要的cell数量 | 是 |
| reset-controller | 空属性,标记这是一个复位控制器 | 推荐 |
来看一个实际项目中用过的例子:
// 系统复位控制器
sys_rst: reset-controller@a0000000 {
compatible = "rockchip,rk3399-cru-reset", "syscon-reset";
reg = <0xa0000000 0x1000>;
#reset-cells = <1>;
reset-controller; // 明确声明这是复位控制器
};
// 使用复位的外设
i2c0: i2c@ff3c0000 {
compatible = "rockchip,rk3399-i2c";
reg = <0xff3c0000 0x1000>;
interrupts = <0 57 4>;
clocks = <&cru SCLK_I2C0>;
resets = <&sys_rst 12>; // I2C0的复位索引是12
status = "disabled";
};
这里有个细节:reset-controller 这个空属性。它其实是个标记,告诉内核「我是复位提供者」。虽然有些驱动不检查这个属性,但我建议加上。为什么?因为设备树是给人看的,加上这个标记,阅读代码的人一眼就能认出这是个复位控制器节点。
避坑指南: 我曾经在一个项目里,把 #reset-cells 写成了 #clock-cells。结果内核在解析设备树时,把复位信号当成了时钟信号去处理,导致外设初始化时序完全错乱。这种低级错误,排查起来特别费时间。所以写设备树时,一定要仔细核对属性名。
4.5 多复位控制器的场景
一个复杂的SoC里,可能不止一个复位控制器。比如:
- 系统级复位控制器:管理所有主要模块的复位
- 电源域复位控制器:管理某个电源域内的模块复位
- 外设专用复位控制器:某些高速外设自带复位逻辑
这种情况下,每个复位控制器都有自己的 #reset-cells 和 reset-controller 声明。消费者根据自己的需求,选择合适的复位控制器。
// 系统复位
sys_rst: reset-controller@a0000000 {
#reset-cells = <1>;
reset-controller;
};
// 电源域复位
pd_rst: reset-controller@b0000000 {
#reset-cells = <1>;
reset-controller;
};
// 消费者可以引用不同的复位控制器
usb: usb@c0000000 {
resets = <&sys_rst 3>; // 使用系统复位
};
gpu: gpu@d0000000 {
resets = <&pd_rst 0>; // 使用电源域复位
};
这种设计的好处是灵活。不同的复位域可以独立管理,互不影响。我在做多核处理器项目时,就用了三个复位控制器:一个给主核域,一个给从核域,一个给外设域。这样任何一个域复位,都不会影响其他域的正常工作。
嗯,复位控制器的设备树表示,核心就是这些。记住三个关键点:#reset-cells 声明参数个数,resets 引用复位信号,索引号对应硬件设计。把这三点搞清楚了,复位相关的设备树配置就不会出大问题。