3. 设备树基础语法:节点、属性与核心属性详解

设备树(Device Tree)是嵌入式 Linux 系统中描述硬件拓扑与资源分配的标准机制。对于网络驱动开发者而言,理解设备树的节点结构、属性定义以及中断绑定规则,是编写可移植、可维护驱动的基石。本章将系统讲解设备树的核心语法,并通过实战案例展示其在网络设备中的应用。

学习目标:掌握设备树节点与属性的定义规则;理解 reg、compatible、interrupts 等核心属性的语义与用法;能够独立编写或修改网络设备节点。

3.1 节点(Node)与属性(Property)基础

设备树由一系列节点组成,每个节点代表一个硬件设备或总线。节点通过属性描述其特性,如地址、中断、时钟等。节点名称通常为 device-name@address 格式,其中 @address 部分可选,用于区分同一类型的不同实例。

/dts-v1/;

/ {
    model = "Embedded Network Board";
    compatible = "vendor,board-v1";

    soc {
        #address-cells = <1>;
        #size-cells = <1>;
        ranges;

        ethernet@10000000 {
            compatible = "vendor,eth-mac";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            phy-mode = "rgmii";
            status = "okay";
        };
    };
};
节点结构解析:
  • 根节点:/ 是设备树的根,包含整个系统的顶层属性。
  • 子节点:soc 是总线节点,ethernet@10000000 是网络设备节点。
  • 属性:compatible、reg、interrupts 等描述设备的具体信息。
设备树节点层级结构 根节点 / model = "Embedded Board" compatible = "vendor,board-v1" soc 节点 #address-cells = <1> #size-cells = <1> ethernet@10000000 网络设备节点 compatible = "vendor,eth-mac" reg = <0x10000000 0x1000> interrupts = <0 33 4>

3.2 核心属性详解

3.2.1 compatible 属性:驱动匹配的关键

compatible 属性是设备树与驱动代码之间的“握手协议”。它定义了一个或多个字符串,用于标识设备的具体型号。Linux 内核通过 of_match_table 匹配该属性,从而加载正确的驱动程序。

// 设备树中的 compatible 属性
compatible = "vendor,eth-mac", "generic,eth";

// 驱动中的匹配表
static const struct of_device_id eth_of_match[] = {
    { .compatible = "vendor,eth-mac", .data = &eth_vendor_data },
    { .compatible = "generic,eth",    .data = &eth_generic_data },
    { /* end */ }
};
MODULE_DEVICE_TABLE(of, eth_of_match);
属性 格式 说明
compatible 字符串列表 第一个字符串为最精确的型号,后续为兼容型号
reg 地址-长度对 描述设备在总线上的地址空间
interrupts 中断描述符 指定中断号、触发类型等
status 字符串 okay 启用,disabled 禁用
注意:compatible 字符串的命名规范为 厂商,设备型号,避免使用通用名称导致驱动匹配冲突。例如 ti,davinci_mdio 优于 mdio。

3.2.2 reg 属性:地址空间描述

reg 属性用于描述设备在总线上的地址范围。其格式由父节点的 #address-cells 和 #size-cells 决定。对于 32 位系统,通常 #address-cells = <1>,#size-cells = <1>。

// 32 位地址空间
soc {
    #address-cells = <1>;
    #size-cells = <1>;

    ethernet@10000000 {
        reg = <0x10000000 0x1000>;  // 起始地址 0x10000000,长度 0x1000
    };
};

// 64 位地址空间
soc {
    #address-cells = <2>;
    #size-cells = <2>;

    ethernet@3000000000 {
        reg = <0x0 0x30000000 0x0 0x1000>;  // 高32位 0x0,低32位 0x30000000
    };
};
reg 属性地址映射 系统总线地址空间 0x10000000 起始地址 长度 0x1000 0x10001000 结束地址 (起始 + 长度) reg = <0x10000000 0x1000> 表示设备占用 0x10000000 ~ 0x10000FFF 的地址空间 驱动通过 ioremap() 映射该区域以访问设备寄存器

3.2.3 interrupts 属性:中断绑定

interrupts 属性是网络驱动中最重要的属性之一。它定义了设备的中断号、中断触发类型以及中断域。中断描述符的格式由 #interrupt-cells 决定,常见格式如下:

// 中断控制器定义
gic: interrupt-controller@f0001000 {
    compatible = "arm,gic-400";
    reg = <0xf0001000 0x1000>;
    interrupt-controller;
    #interrupt-cells = <3>;  // 3 个 cell:中断类型、中断号、触发类型
};

// 网络设备中断绑定
ethernet@10000000 {
    compatible = "vendor,eth-mac";
    reg = <0x10000000 0x1000>;
    interrupts = <0 33 4>;  // GIC_SPI 33, IRQ_TYPE_LEVEL_HIGH
    interrupt-parent = <&gic>;
};
中断类型 值 说明
GIC_SPI 0 共享外设中断,可路由到任意 CPU
GIC_PPI 1 私有外设中断,绑定到特定 CPU
IRQ_TYPE_EDGE_RISING 1 上升沿触发
IRQ_TYPE_EDGE_FALLING 2 下降沿触发
IRQ_TYPE_LEVEL_HIGH 4 高电平触发
IRQ_TYPE_LEVEL_LOW 8 低电平触发
中断绑定实战要点:
  • interrupt-parent:显式指定中断控制器,若不指定则继承父节点。
  • 中断号偏移:GIC 中断号通常从 32 开始(SPI 0 对应硬件中断号 32)。
  • 触发类型:网络设备常用 IRQ_TYPE_LEVEL_HIGH(4)或 IRQ_TYPE_EDGE_RISING(1)。
中断绑定流程 网络设备 ethernet@10000000 interrupts = <0 33 4> GIC 中断控制器 interrupt-controller #interrupt-cells = <3> CPU 核 中断处理 中断绑定数据流 1. 网络设备产生中断信号 (IRQ 33) 2. GIC 根据中断类型 (SPI) 和触发类型 (Level High) 路由中断 3. CPU 响应中断,调用注册的中断处理函数 4. 驱动在 probe 中通过 platform_get_irq() 获取中断号并注册 handler 5. 中断处理完成后,驱动需清除中断状态位并重新使能中断

3.3 网络设备节点实战案例

以下是一个完整的千兆以太网控制器设备树节点示例,包含 MDIO 总线、PHY 设备以及中断绑定:

// 千兆以太网控制器节点
gmac: ethernet@10000000 {
    compatible = "snps,dwmac-4.20", "snps,dwmac";
    reg = <0x10000000 0x2000>;
    interrupts = <0 33 4>, <0 34 4>, <0 35 4>;
    interrupt-names = "macirq", "eth_wake_irq", "eth_lpi";
    interrupt-parent = <&gic>;
    phy-mode = "rgmii";
    rx-fifo-depth = <4096>;
    tx-fifo-depth = <4096>;
    status = "okay";

    mdio0: mdio {
        #address-cells = <1>;
        #size-cells = <0>;
        compatible = "snps,dwmac-mdio";

        phy0: ethernet-phy@0 {
            reg = <0>;
            compatible = "ethernet-phy-ieee802.3-c22";
            interrupts = <0 36 4>;
            interrupt-parent = <&gic>;
        };
    };
};
实战提示:
  • interrupt-names:为每个中断指定名称,驱动可通过 platform_get_irq_byname() 获取。
  • MDIO 子节点:用于管理 PHY 设备,PHY 的 reg 属性对应其 MDIO 地址。
  • phy-mode:定义 MAC 与 PHY 之间的接口模式,如 rgmii、rmii、sgmii。

3.4 常见错误与调试方法

错误类型 现象 解决方案
compatible 不匹配 驱动 probe 失败,dmesg 显示无匹配驱动 检查设备树与驱动中的 compatible 字符串是否一致
reg 地址错误 ioremap 返回 NULL,访问寄存器导致崩溃 核对 #address-cells 和 #size-cells,确保地址范围正确
中断号冲突 中断无法触发或触发异常 使用 cat /proc/interrupts 查看中断分配情况
中断类型错误 中断丢失或频繁触发 确认硬件实际触发类型与设备树中 interrupts 的 cell 一致
调试建议:使用 dtc -I dtb -O dts 反编译设备树二进制文件,验证节点属性是否正确。内核启动时添加 clk_ignore_unused 参数可避免时钟未使能导致的中断问题。
设备树调试流程 步骤 1 编写 .dts 文件 步骤 2 dtc 编译为 .dtb 步骤 3 内核加载 .dtb 步骤 4 驱动 probe 调试检查点 dmesg | grep -i "of" 查看设备树解析日志 cat /proc/device-tree/ 遍历设备树节点 失败时返回步骤 1 修改 .dts

3.5 本章小结

设备树是嵌入式 Linux 网络驱动开发的核心技能。通过本章学习,你应掌握:

  • 节点与属性:设备树的基本结构,节点命名规则,属性值的格式。
  • compatible:驱动匹配的关键,必须与驱动中的 of_match_table 一致。
  • reg:地址空间描述,注意 #address-cells 和 #size-cells 的配置。
  • interrupts:中断绑定,理解中断类型、中断号与触发类型的组合。
  • 实战案例:完整的网络设备节点包含 MAC、MDIO、PHY 以及中断配置。