1. 设备树基础回顾

设备树(Device Tree)是一种描述硬件资源的数据结构,它把原本固化在 Linux 内核源码中的硬件信息抽取为独立文本,使同一份内核镜像能适配不同硬件平台。在嵌入式 Linux 开发中,设备树是驱动工程师必须掌握的基础设施。

1.1 设备树文件体系

文件类型 扩展名 说明
设备树源文件 .dts 人类可读的硬件描述文本,由开发者编写
设备树头文件 .dtsi 可被 .dts 通过 #include 包含,用于 SoC 公共定义
设备树二进制 .dtb 由 DTC 编译器将 .dts 编译生成的二进制 blob
设备树 Overlay 源文件 .dtso 描述增量修改的源文件,编译为 .dtbo
设备树 Overlay 二进制 .dtbo 运行时动态加载的 overlay 片段
工程提示: 在 Linux 5.x 之后,Overlay 源文件推荐使用 .dtso 后缀,与普通 .dts 区分。DTC 编译器通过 -@ 选项生成带符号表的 .dtbo,这是动态加载的前提。

1.2 DTS 语法基础

设备树采用树形结构,根节点是 /,每个节点代表一个硬件设备或总线,节点内包含若干属性描述设备特征。

/dts-v1/;

/ {
    compatible = "acme,coyote-rev1";
    #address-cells = <1>;
    #size-cells = <1>;

    cpus {
        #address-cells = <1>;
        #size-cells = <0>;

        cpu@0 {
            compatible = "arm,cortex-a9";
            reg = <0x0>;
            device_type = "cpu";
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x20000000>;
    };

    serial@101f1000 {
        compatible = "ns16550";
        reg = <0x101f1000 0x1000>;
        interrupts = <0x0 0x1c 0x4>;
        clock-frequency = <3686400>;
    };
};

1.3 节点命名与路径

每个节点由 节点名 和可选的 单元地址 组成,格式为 node-name@unit-address。单元地址通常与 reg 属性的首地址一致,用于区分同一层级下的多个同类型设备。

  • 节点名:描述设备类型,如 serial、i2c、gpio。
  • 单元地址:可选,当存在多个相同节点名时用于区分,如 cpu@0、cpu@1。
  • 路径:从根节点到目标节点的全路径,如 /soc/i2c@7e804000/eeprom@50。
  • 标签(Label):为节点起别名,方便引用,如 uart0: serial@101f1000。
关键规则: 同一父节点下,子节点的 node-name@unit-address 必须唯一。如果省略单元地址,则节点名本身必须唯一。

1.4 核心属性详解

1.4.1 compatible

设备驱动匹配的核心属性,格式为 "厂商,型号"。内核通过该属性查找对应的 platform_driver。推荐使用多个值,从最具体到最通用。

compatible = "ti,am335x-uart", "ns16550";

1.4.2 reg 与地址编码

reg 描述设备在总线上的地址空间,由 #address-cells 和 #size-cells 决定每个地址和长度的字长(32 位或 64 位)。

属性 含义 示例值
#address-cells 地址字段占用的 32 位单元数 <1> 表示 32 位地址
#size-cells 长度字段占用的 32 位单元数 <1> 表示 32 位长度
reg 地址 + 长度对,可包含多组 <0x101f1000 0x1000>
// 64 位地址示例
soc {
    #address-cells = <2>;
    #size-cells = <2>;

    dma@0x100000000 {
        reg = <0x1 0x00000000 0x0 0x100000>;
        // 地址: 0x1_00000000, 长度: 0x100000
    };
};

1.4.3 interrupts 与中断映射

interrupts 属性描述设备的中断号、触发类型等信息,其解释依赖于父节点的 #interrupt-cells。常见的中断控制器类型:

中断控制器 #interrupt-cells 中断描述格式
GIC(通用中断控制器) 3 <中断类型 中断号 触发类型>
简单中断控制器 1 <中断号>
TI 特定中断控制器 2 <中断号 触发类型>
// GIC 中断示例:SPI 中断 28,上升沿触发
interrupts = <0x0 0x1c 0x4>;
// 字段说明:
//   0x0  – 中断类型:0=SPI(共享外设中断),1=PPI(私有外设中断)
//   0x1c – 中断号(28)
//   0x4  – 触发类型:1=上升沿,2=下降沿,4=高电平,8=低电平
常见错误: 中断号在设备树中通常从 0 开始编号,而芯片手册可能从 1 开始。务必核对 GIC 规范或 SoC 参考手册,否则中断无法正确路由。

1.5 设备树核心结构图

设备树核心结构图 / (根节点) compatible, #address-cells #size-cells cpus memory@80000000 serial@101f1000 cpu@0 cpu@1 compatible reg = <0x0> device_type compatible reg = <0x1> device_type device_type = "memory" reg = <0x80000000 0x20000000> compatible = "ns16550" reg = <0x101f1000 0x1000> interrupts = <0 28 4> 设备树关键设计要点 • 根节点 / 包含 compatible 和地址编码属性 • 子节点通过 node-name@unit-address 唯一标识 • reg 属性由父节点的 #address-cells / #size-cells 决定格式 • interrupts 属性依赖中断控制器的 #interrupt-cells 定义

1.6 特殊属性与节点

属性/节点 作用 典型用法
status 控制设备启用状态 "okay" 启用,"disabled" 禁用
phandle 为节点分配唯一数字句柄 由 DTC 自动生成,用于跨节点引用
aliases 为节点路径定义短别名 serial0 = &uart0;
chosen 传递运行时参数(如启动参数) bootargs = "console=ttyS0,115200";
gpios 描述 GPIO 引脚绑定 <&gpio1 15 0>

1.7 设备树 Overlay 的入口:& 引用语法

Overlay 的核心机制是通过 &label 引用基树中的节点,然后添加或覆盖属性。这种语法使得 Overlay 文件只描述增量修改,无需重复整个树结构。

// 基树中定义 uart0 节点
uart0: serial@101f1000 {
    compatible = "ns16550";
    reg = <0x101f1000 0x1000>;
    interrupts = <0x0 0x1c 0x4>;
    status = "disabled";
};

// Overlay 文件:启用 uart0 并添加 pinmux
/dts-v1/;
/plugin/;

&uart0 {
    status = "okay";
    pinctrl-names = "default";
    pinctrl-0 = <&uart0_pins>;
};
Overlay 工程要点: 基树编译时必须使用 -@ 选项生成符号表(__symbols__ 节点),Overlay 才能通过标签名找到目标节点。动态加载时,内核的 configfs 接口会校验符号解析是否成功。

1.8 本章小结

设备树是嵌入式 Linux 硬件描述的事实标准,掌握 DTS 语法、节点层次、reg 地址编码和 interrupts 中断映射是进行 Overlay 开发的前提。下一阶段我们将深入 Overlay 的编译、加载与运行时调试技术。