设备树基础:DTS语法、节点与属性

设备树(Device Tree)是嵌入式Linux系统中用于描述硬件拓扑结构的数据结构,它替代了传统板级文件(board-*.c)中硬编码的硬件信息。设备树源文件(.dts)通过编译器生成设备树二进制文件(.dtb),由Bootloader传递给内核解析。理解设备树语法是编写Platform驱动的前提。

核心概念:设备树将硬件描述与内核驱动分离,驱动通过compatible属性匹配设备节点,probe函数在匹配成功后自动执行。

2.1 设备树语法基础

设备树源文件采用树形结构,以节点(node)和属性(property)描述硬件。每个节点代表一个设备或总线,属性描述设备的特性。

设备树语法结构示例 / { ... } — 根节点 soc { ... } — 总线节点 chosen { ... } — 系统节点 uart@1f000 { ... } i2c@1c000 { ... } gpio@1d000 { ... } 节点属性示例 compatible = "vendor,device"; reg = <0x1f000 0x1000>; interrupts = <0 31 4>; status = "okay"; clock-frequency = <24000000>;

2.2 节点与属性详解

设备树节点由节点名和单元地址组成,格式为 node-name@unit-address。属性是键值对,用于描述设备的硬件特征。

语法元素 格式 说明 示例
节点 node-name@unit-address 节点名标识设备类型,单元地址通常为寄存器基址 uart@1f000
标签 label: node-name 为节点创建引用别名,便于其他节点引用 uart0: serial@1f000
属性 property-name = value; 描述设备特性,值可为字符串、数字、数组等 status = "okay";
包含文件 #include "file.dtsi" 引入公共设备树头文件,复用硬件定义 #include "imx6ull.dtsi"
实战提示:节点名应尽量使用标准名称(如 uart、i2c、spi),便于内核驱动匹配。单元地址必须与 reg 属性的第一个地址一致。

2.3 compatible属性——驱动匹配的关键

compatible 属性是设备树与驱动之间建立联系的桥梁。驱动通过 of_match_table 中定义的 compatible 字符串与设备树节点匹配。

// 设备树节点定义
i2c@1c000 {
    compatible = "vendor,i2c-controller", "simple-bus";
    reg = <0x01c000 0x1000>;
    interrupts = <0 32 4>;
    status = "okay";
};

// 驱动匹配表
static const struct of_device_id my_i2c_dt_ids[] = {
    { .compatible = "vendor,i2c-controller", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_i2c_dt_ids);

compatible 属性支持多个字符串,内核按优先级依次匹配。第一个字符串通常为精确型号,后续为兼容型号或总线类型。

匹配优先级 compatible字符串 匹配场景
1(最高) "vendor,i2c-controller" 精确匹配特定厂商的I2C控制器
2 "simple-bus" 回退匹配,适用于简单总线设备
3 "vendor,generic-i2c" 通用兼容匹配,覆盖同系列设备
常见错误:compatible 字符串必须与驱动中 of_match_table 完全一致(包括大小写和标点)。例如 "vendor,i2c" 与 "vendor,i2c-controller" 不匹配。

2.4 reg属性——地址与内存映射

reg 属性描述设备的寄存器地址空间,由一组或多组 (address, length) 对组成。内核通过 platform_get_resource() 函数获取这些资源。

reg属性地址映射关系 设备节点 reg = <addr len> 物理地址空间 0x01c000 ~ 0x01cfff ioremap() 虚拟地址(驱动使用)
// 设备树定义:多组reg地址
dma-controller@1e000 {
    compatible = "vendor,dma-controller";
    reg = <0x01e000 0x1000>,  // 控制寄存器
          <0x01f000 0x0200>;  // 描述符表
    interrupts = <0 33 4>;
};

// 驱动中获取reg资源
static int my_dma_probe(struct platform_device *pdev)
{
    struct resource *res;
    void __iomem *ctrl_base, *desc_base;
    
    // 获取第一个reg区域(索引0)
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    ctrl_base = devm_ioremap_resource(&pdev->dev, res);
    
    // 获取第二个reg区域(索引1)
    res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
    desc_base = devm_ioremap_resource(&pdev->dev, res);
    
    return 0;
}

2.5 interrupts属性——中断资源描述

interrupts 属性描述设备的中断请求信息,包括中断控制器、中断号、触发类型等。内核通过 platform_get_irq() 函数获取中断号。

中断描述格式 字段含义 示例
<中断号 触发类型> 简单中断控制器,直接指定中断号和触发方式 interrupts = <31 4>;
<中断控制器 中断号 触发类型> 多中断控制器,指定父控制器、中断号和触发方式 interrupts = <0 31 4>;
<中断号 触发类型 优先级> 扩展格式,增加优先级字段 interrupts = <31 4 0>;

触发类型宏定义:

宏名称 值 触发方式
IRQF_TRIGGER_NONE 0x00000000 无触发(默认)
IRQF_TRIGGER_RISING 0x00000001 上升沿触发
IRQF_TRIGGER_FALLING 0x00000002 下降沿触发
IRQF_TRIGGER_HIGH 0x00000004 高电平触发
IRQF_TRIGGER_LOW 0x00000008 低电平触发
// 设备树中断定义
ethernet@1c000 {
    compatible = "vendor,eth-controller";
    reg = <0x01c000 0x2000>;
    interrupts = <0 35 4>;  // 中断控制器0,中断号35,高电平触发
    interrupt-parent = <&gic>;  // 指定中断父控制器
};

// 驱动中获取中断号
static int my_eth_probe(struct platform_device *pdev)
{
    int irq_num;
    
    irq_num = platform_get_irq(pdev, 0);
    if (irq_num < 0) {
        dev_err(&pdev->dev, "failed to get IRQ\n");
        return irq_num;
    }
    
    // 注册中断处理函数
    ret = devm_request_irq(&pdev->dev, irq_num, my_eth_isr,
                           IRQF_TRIGGER_HIGH, "my_eth", dev);
    return 0;
}
关键总结:设备树通过 compatible 属性实现驱动与设备的匹配,reg 属性提供地址资源,interrupts 属性提供中断资源。驱动在 probe 函数中通过 platform_get_resource() 和 platform_get_irq() 获取这些资源,完成硬件初始化。

2.6 完整设备树节点示例

以下是一个综合示例,展示如何在一个设备节点中组合使用上述属性:

// 完整设备树节点
spi@1d000 {
    compatible = "vendor,spi-controller", "vendor,spi-v2";
    reg = <0x01d000 0x1000>;
    interrupts = <0 36 4>;
    interrupt-parent = <&gic>;
    status = "okay";
    clock-frequency = <50000000>;
    #address-cells = <1>;
    #size-cells = <0>;
    
    // 子设备(SPI从设备)
    spi-device@0 {
        compatible = "vendor,temperature-sensor";
        reg = <0>;  // SPI片选号
        spi-max-frequency = <10000000>;
    };
};

该节点展示了设备树的多层嵌套结构:SPI控制器节点包含子节点(SPI从设备),每个节点都有自己的 compatible 和 reg 属性。驱动通过解析这些属性,能够完整获取设备的硬件拓扑信息。