4. 设备树基础

设备树(Device Tree, DT)是描述硬件拓扑与资源配置的树形数据结构,是 Linux 内核实现跨架构驱动移植的核心抽象层。本章从 DT 语法出发,通过 ARM 与 RISC-V 平台实例,建立设备树绑定文档的编写规范,为后续驱动移植打下基础。

核心认知:设备树将硬件描述从内核源码中剥离,使得同一份内核镜像可以支持不同硬件配置。驱动通过设备树节点获取寄存器地址、中断号、时钟频率等资源,无需硬编码。

4.1 设备树语法基础

设备树源文件(.dts)采用树形层级结构,每个节点代表一个硬件设备或总线。基本语法元素包括:

  • 节点(Node): 以 node-name@unit-address 格式定义,unit-address 通常为寄存器基址。
  • 属性(Property): 键值对,用于描述设备特性,如 reg、interrupts、compatible。
  • 标签(Label): 为节点提供别名,便于引用,如 uart0: serial@10000000。
  • 包含文件: 使用 #include 引入 .dtsi 头文件,实现 SoC 级描述复用。
语法速查表: 常用属性及其含义
属性 格式 说明
compatible "vendor,device" 驱动匹配字符串,格式为"厂商,设备型号"
reg <address length> 寄存器地址与长度,支持多段
interrupts <irq_type> 中断号与触发类型
clocks <phandle> 时钟句柄,引用时钟控制器节点
status "okay" / "disabled" 设备启用状态

以下是一个典型的 UART 设备节点示例:

uart0: serial@10000000 {
    compatible = "vendor,uart-ip";
    reg = <0x10000000 0x1000>;
    interrupts = <33 4>;
    clocks = <&clk_uart 0>;
    status = "okay";
};
常见错误: 忘记在 reg 属性中指定地址长度,或 compatible 字符串未遵循"厂商,设备"格式,会导致驱动无法匹配。

4.2 设备树结构可视化

下图展示了一个简化的设备树层级结构,包含根节点、CPU、内存、中断控制器和两个外设节点:

设备树层级结构示例 / (根节点) cpus memory@80000000 interrupt-controller serial@10000000 gpio@20000000 cpu@0 gpio-bank@0 说明: 根节点包含 CPU、内存、中断控制器等顶层节点;外设节点挂在对应总线或 SoC 节点下。 每个节点通过 compatible 属性与驱动绑定,reg 属性描述硬件资源地址。

4.3 ARM 平台设备树实例

以 ARM Cortex-A 系列 SoC 为例,设备树通常包含 GIC(通用中断控制器)、PL011 UART、GPIO 等标准 IP 的描述。以下是一个简化的 ARM 平台设备树片段:

/dts-v1/;

#include "soc-arm.dtsi"

/ {
    model = "ARM Development Board";
    compatible = "arm,vexpress";

    chosen {
        bootargs = "console=ttyAMA0,115200";
    };

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

    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        uart0: serial@1c090000 {
            compatible = "arm,pl011", "arm,primecell";
            reg = <0x0 0x1c090000 0x0 0x1000>;
            interrupts = <0 37 4>;
            clocks = <&clk_uart0 &clk_uart0>;
            clock-names = "uartclk", "apb_pclk";
        };

        gpio0: gpio@1c000000 {
            compatible = "arm,pl061", "arm,primecell";
            reg = <0x0 0x1c000000 0x0 0x1000>;
            interrupts = <0 40 4>;
            gpio-controller;
            #gpio-cells = <2>;
        };
    };
};
ARM 平台关键点:
  • 使用 arm,pl011 和 arm,primecell 双 compatible 字符串,提供兼容性。
  • 中断描述采用 <中断域 中断号 触发类型> 格式,GIC 域通常为 0(SPI)。
  • 时钟通过 clocks 属性引用,clock-names 区分不同时钟角色。

4.4 RISC-V 平台设备树实例

RISC-V 平台设备树与 ARM 在结构上高度相似,但中断控制器使用 PLIC(平台级中断控制器)或 CLINT(核心本地中断控制器)。以下是一个 RISC-V 64 位 SoC 的设备树示例:

/dts-v1/;

/ {
    #address-cells = <2>;
    #size-cells = <2>;
    compatible = "riscv-virtio";

    chosen {
        bootargs = "console=ttyS0,115200";
    };

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

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

        cpu@0 {
            device_type = "cpu";
            reg = <0>;
            compatible = "riscv";
            riscv,isa = "rv64imafdc";
            mmu-type = "riscv,sv48";
        };
    };

    soc {
        #address-cells = <2>;
        #size-cells = <2>;

        uart0: serial@10000000 {
            compatible = "ns16550a";
            reg = <0x0 0x10000000 0x0 0x100>;
            interrupts = <10>;
            interrupt-parent = <&plic>;
            clock-frequency = <3686400>;
        };

        plic: interrupt-controller@c000000 {
            compatible = "riscv,plic0";
            reg = <0x0 0xc000000 0x0 0x4000000>;
            interrupts-extended = <&cpu0 11 &cpu0 9>;
            interrupt-controller;
            #address-cells = <0>;
            #interrupt-cells = <1>;
        };
    };
};
RISC-V 与 ARM 差异:
  • RISC-V 使用 riscv,isa 属性描述指令集扩展,ARM 使用 compatible 描述 CPU 型号。
  • PLIC 中断控制器使用 interrupts-extended 属性连接 CPU 核,而非 ARM 的 interrupt-parent 链。
  • RISC-V 设备树通常包含 timebase-frequency 属性,用于定时器校准。

4.5 设备树绑定文档

设备树绑定文档(DT Binding)是描述设备节点属性格式的规范文件,通常以 YAML 格式存放在内核源码 Documentation/devicetree/bindings/ 目录下。绑定文档是驱动开发者与设备树编写者之间的契约。

一个标准的绑定文档包含以下部分:

章节 内容 示例
title 设备名称与描述 Vendor UART controller
maintainers 维护者列表 John Doe <john@example.com>
properties 必选与可选属性定义 compatible, reg, interrupts
required 必选属性列表 compatible, reg
examples 完整设备树节点示例 包含地址、中断、时钟的节点

以下是一个简化的 YAML 格式绑定文档示例:

# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/serial/vendor-uart.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Vendor UART Controller

maintainers:
  - John Doe <john@example.com>

properties:
  compatible:
    items:
      - enum:
          - vendor,uart-v1
          - vendor,uart-v2

  reg:
    maxItems: 1

  interrupts:
    maxItems: 1

  clocks:
    maxItems: 2

required:
  - compatible
  - reg
  - interrupts

additionalProperties: false

examples:
  - |
    uart@10000000 {
        compatible = "vendor,uart-v1";
        reg = <0x10000000 0x1000>;
        interrupts = <33 4>;
        clocks = <&clk_uart 0>;
    };
...
绑定文档检查: 提交设备树补丁前,务必运行 make dt_binding_check 验证 YAML 格式,运行 make dtbs_check 验证设备树实例。未通过检查的绑定文档会被内核社区拒绝。

4.6 设备树编译与反编译

设备树编译器(dtc)将 .dts 源文件编译为 .dtb 二进制文件,也可将 .dtb 反编译为 .dts。常用命令如下:

操作 命令 说明
编译 dtc -I dts -O dtb -o board.dtb board.dts 将 .dts 编译为 .dtb
反编译 dtc -I dtb -O dts -o board.dts board.dtb 将 .dtb 反编译为 .dts
检查 dtc -I dts -O dtb -o /dev/null board.dts 仅检查语法,不输出文件
生成符号 dtc -@ -I dts -O dtb -o board.dtb board.dts 生成符号节点,支持叠加层
调试技巧: 在 Linux 启动后,可通过 /sys/firmware/devicetree/base 查看当前使用的设备树内容,或使用 dtc -I fs -O dts /sys/firmware/devicetree/base 导出为 .dts 格式。

4.7 设备树移植要点

从 ARM 向 RISC-V 移植驱动时,设备树需要关注以下差异:

  • 中断模型: ARM 使用 GIC(通用中断控制器),RISC-V 使用 PLIC/CLINT。中断号范围和触发类型定义不同。
  • 地址空间: RISC-V 64 位通常使用 #address-cells = <2>,ARM 32 位使用 <1>。
  • 时钟描述: ARM 平台常使用 fixed-clock 或 clk-provider,RISC-V 平台可能直接使用 clock-frequency 属性。
  • CPU 描述: RISC-V 使用 riscv,isa 描述指令集,ARM 使用 compatible 描述 CPU 型号。

下图总结了 ARM 与 RISC-V 设备树的关键差异:

ARM vs RISC-V 设备树关键差异 ARM 平台 GIC 中断控制器 #address-cells = <1> (32位) compatible = "arm,cortex-axx" 时钟通过 clk-provider 描述 RISC-V 平台 PLIC / CLINT 中断控制器 #address-cells = <2> (64位) riscv,isa = "rv64imafdc" clock-frequency 直接指定 移植时需重点修改中断控制器类型、地址宽度、CPU 描述和时钟描述方式。

4.8 本章小结