Skip to content

设备树:原理与语法

什么是设备树

设备树(Device Tree)是描述硬件拓扑的数据结构,以文本格式(DTS)编写,编译为二进制(DTB)后由 Bootloader 传递给内核。它解决了 ARM 平台"板级代码爆炸"问题——同一内核镜像可以通过不同 DTB 支持不同硬件。

DTS 源文件 (.dts / .dtsi)
    │  dtc 编译器

DTB 二进制 (.dtb)
    │  Bootloader 加载

内核解析 → 创建 platform_device / i2c_client 等

DTS 基本语法

c
/dts-v1/;

/ {                                    /* 根节点 */
    #address-cells = <1>;              /* 子节点 reg 地址占 1 个 cell */
    #size-cells = <1>;                 /* 子节点 reg 大小占 1 个 cell */

    model = "My Board v1.0";
    compatible = "myvendor,myboard";

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

        cpu@0 {
            compatible = "arm,cortex-a53";
            reg = <0>;                 /* CPU 编号 */
        };
    };

    memory@80000000 {
        device_type = "memory";
        reg = <0x80000000 0x40000000>; /* 基地址 + 大小 */
    };

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;                        /* 地址透传 */

        uart0: serial@fe201000 {
            compatible = "myvendor,my-uart";
            reg = <0xfe201000 0x1000>;
            interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
            clocks = <&ccu CLK_UART0>;
            clock-names = "uart_clk";
            pinctrl-0 = <&uart0_pins>;
            pinctrl-names = "default";
            status = "disabled";       /* 默认禁用 */
        };

        i2c0: i2c@fe804000 {
            compatible = "myvendor,my-i2c";
            reg = <0xfe804000 0x1000>;
            interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
            #address-cells = <1>;
            #size-cells = <0>;
            status = "okay";

            /* I2C 从设备 */
            eeprom@50 {
                compatible = "atmel,24c256";
                reg = <0x50>;
                pagesize = <64>;
            };
        };
    };
};

板级文件覆盖(.dts 引用 .dtsi)

c
/* SoC 级描述:my-soc.dtsi */
/ {
    soc {
        uart0: serial@fe201000 {
            compatible = "myvendor,my-uart";
            reg = <0xfe201000 0x1000>;
            status = "disabled";   /* SoC 级默认禁用 */
        };
    };
};

/* 板级描述:myboard.dts */
#include "my-soc.dtsi"

&uart0 {
    status = "okay";               /* 板级启用 */
    current-speed = <115200>;
};

常用属性速查

compatible

驱动匹配的关键属性,格式为 "vendor,device"

c
compatible = "myvendor,my-uart-v2", "myvendor,my-uart";
/* 内核从左到右尝试匹配,支持向后兼容 */

reg

设备寄存器地址和大小:

c
/* 单段 */
reg = <0xfe201000 0x1000>;

/* 多段(如 PCI BAR) */
reg = <0x0 0xfe201000 0x0 0x1000>,
      <0x0 0xfe202000 0x0 0x1000>;

interrupts

c
/* GIC SPI 中断 */
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;

/* 多个中断 */
interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>,
             <GIC_SPI 33 IRQ_TYPE_EDGE_RISING>;
interrupt-names = "rx", "tx";

驱动中按名称获取:

c
irq = platform_get_irq_byname(pdev, "rx");

clocks

c
clocks = <&ccu CLK_UART0>, <&ccu CLK_APB>;
clock-names = "uart_clk", "apb_pclk";

驱动中:

c
clk = devm_clk_get(&pdev->dev, "uart_clk");

gpio

c
reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
enable-gpios = <&gpio 11 GPIO_ACTIVE_HIGH>;

驱动中:

c
struct gpio_desc *reset = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
gpiod_set_value(reset, 1);   /* 置高(考虑极性) */

设备树 Overlay(动态加载)

c
/* my-sensor.dts overlay */
/dts-v1/;
/plugin/;

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

    sensor@48 {
        compatible = "ti,tmp102";
        reg = <0x48>;
    };
};
bash
# 编译 overlay
dtc -@ -I dts -O dtb -o my-sensor.dtbo my-sensor.dts

# 加载 overlay(Raspberry Pi 风格)
dtoverlay my-sensor

# 或通过 configfs
mkdir /sys/kernel/config/device-tree/overlays/my-sensor
cat my-sensor.dtbo > /sys/kernel/config/device-tree/overlays/my-sensor/dtbo

调试工具

bash
# 查看当前设备树
dtc -I fs /sys/firmware/devicetree/base

# 查看特定节点
cat /sys/firmware/devicetree/base/soc/serial@fe201000/compatible

# 反编译 DTB
dtc -I dtb -O dts -o out.dts /boot/dtb/myboard.dtb

# 查看内核解析结果
ls /sys/bus/platform/devices/

褚成志的笔记