设备树语法入门:节点、属性、标签、别名、包含文件

各位同学,咱们今天聊聊设备树的基础语法。说实话,我刚接触设备树那会儿,看着满屏的.dts文件也是一头雾水。但后来发现,这东西说白了就是个描述硬件信息的树形结构。你把它想象成一张硬件清单,CPU、内存、外设都列在上面,驱动通过它来找到对应的硬件。

节点:设备树的基本单元

节点是设备树的核心。每个节点代表一个硬件设备或总线。我习惯把节点比作文件夹,根节点/就是根目录,下面挂着一堆子节点。

/ {
    compatible = "acme,coyote";
    #address-cells = <1>;
    #size-cells = <1>;
    
    cpu@0 {
        compatible = "arm,cortex-a9";
        reg = <0x0>;
    };
    
    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>;
    };
};

注意看,节点名字后面可以跟@地址。这个地址不是必须的,但如果有多个同类型设备,加上地址能区分它们。我在项目中遇到过,两个I2C控制器都叫i2c,结果编译报错,加上@0x1000@0x2000就解决了。

属性:描述节点的关键信息

属性就是节点的特征。常见的属性有:

  • compatible:驱动匹配的关键字,驱动通过它找到对应的设备
  • reg:寄存器地址和大小
  • interrupts:中断号
  • status:设备状态,okaydisabled

重点:compatible属性的格式通常是"厂商,型号"。比如"ti,omap3-i2c"。驱动会优先匹配更具体的字符串,再匹配通用的。这个顺序很重要,别搞反了。

属性值可以是:

  • 字符串:"string"
  • 32位整数:<0x12345678>
  • 64位整数:<0x00000000 0x12345678>
  • 字节数组:[0x12 0x34 0x56]
  • 字符串列表:"str1", "str2"

标签:给节点起个名字

标签就是节点的别名。用标签名:节点名的格式定义。我个人习惯给所有重要节点都加上标签,方便其他地方引用。

uart0: serial@44e09000 {
    compatible = "ti,omap3-uart";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};

有了标签,其他地方就能用&uart0来引用这个节点。嗯,这里要注意,标签在整个设备树里必须是唯一的,不能重复。

别名:另一种引用方式

别名和标签有点像,但用途不同。别名通常放在根节点的aliases子节点里,用于给设备分配固定的编号。

/ {
    aliases {
        serial0 = &uart0;
        serial1 = &uart1;
        i2c0 = &i2c0;
    };
};

为什么需要别名?你想想看,如果板子上有多个串口,驱动怎么知道哪个是ttyS0?别名就是干这个的。我曾经调试一块板子,串口编号总对不上,最后发现是别名写错了。

包含文件:复用与模块化

设备树支持#include预处理指令。这跟C语言的#include一样,可以把公共部分抽出来。

#include "soc.dtsi"
#include "board-common.dtsi"

/ {
    model = "My Custom Board";
    compatible = "my,board";
    
    &uart0 {
        status = "okay";
    };
};

.dtsi文件是设备树包含文件,通常放SoC的公共定义。我建议把SoC相关的节点都放在.dtsi里,板级差异放在.dts里。这样换板子时只需要改.dts文件。

小技巧:&节点标签的方式可以在包含文件里修改已有节点的属性。比如在.dts里把某个外设的statusdisabled改成okay

避坑指南

我曾经踩过的坑:

  • 节点名不要用大写字母,设备树编译器会报错
  • reg属性的地址和大小必须跟#address-cells#size-cells匹配
  • 标签不能以数字开头,否则编译通不过
  • 包含文件路径用双引号,系统头文件用尖括号

说实话,设备树语法本身不难,难的是理解硬件和驱动的对应关系。我刚开始写设备树时,经常忘记加status = "okay",结果驱动死活不工作。后来养成了习惯,写完设备树先检查所有外设的status

好了,这一节的内容就这些。下一节咱们聊聊如何编写一个完整的设备树文件,从零开始搭建一个板子的设备树。到时候我会拿一个实际项目来演示,你们会看到设备树是怎么一步步从空白变成几百行代码的。