2. 设备树基础语法:节点、属性、reg、compatible、status等核心概念

设备树(Device Tree)是一种描述硬件资源的数据结构,它使用节点(Node)和属性(Property)的层级关系,将非可枚举的硬件信息传递给操作系统内核。在Linux网络设备驱动中,MAC控制器、DMA引擎、PHY芯片、MDIO总线等硬件单元,都通过设备树节点进行描述和关联。掌握设备树的基础语法,是解析MAC节点到PHY绑定路径的前提。

核心认知:设备树不是C代码,而是一种硬件描述语言(DSL)。它不定义驱动行为,只定义“硬件在哪里、是什么、怎么访问”。驱动通过匹配compatible属性来绑定节点,通过读取reg、interrupts等属性来获取硬件资源。

2.1 节点(Node)与属性(Property)

设备树的基本单元是节点。每个节点代表一个硬件设备、总线、控制器或功能块。节点可以嵌套,形成树状结构。节点内部包含属性,属性是键值对,用于描述节点的特征。

/dts-v1/;

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

    soc {
        compatible = "simple-bus";
        #address-cells = <1>;
        #size-cells = <1>;

        eth0: ethernet@10000000 {
            compatible = "vendor,mac-controller";
            reg = <0x10000000 0x1000>;
            interrupts = <0 33 4>;
            status = "okay";
        };
    };
};
节点命名规则:节点名称格式为 node-name@unit-address。其中 node-name 是通用名称(如 ethernet、i2c、gpio),unit-address 是设备在总线上的地址(如寄存器基址、I2C从机地址)。eth0: 是标签(label),用于在其他地方引用该节点。

下表总结了节点和属性的基本构成要素:

元素 语法 说明 示例
根节点 / { }; 设备树的顶层容器,代表整个板级硬件 / { model = "..." };
子节点 node-name { }; 在父节点内部定义,可嵌套任意层级 soc { ethernet@... };
标签 label: 节点别名,用于引用或覆盖节点 eth0: ethernet@10000000
属性 key = value; 键值对,值可以是字符串、数字、数组等 status = "okay";
引用 &label 通过标签引用节点,用于追加或修改属性 &eth0 { phy-handle = &phy0; };
设备树节点层级结构 根节点 / soc (simple-bus) eth0: ethernet@10000000 mdio@10001000 gpio@10002000 phy0: ethernet-phy@0 phy1: ethernet-phy@1 设备树层级结构:根节点 → 总线节点 → 设备节点 → 子设备节点 每个节点通过 compatible 属性与驱动绑定,通过 reg 属性获取地址空间

2.2 核心属性详解

设备树规范定义了一组标准属性,用于描述硬件资源的分配和设备的标识。对于网络设备驱动,以下四个属性最为关键:compatible、reg、interrupts 和 status。

2.2.1 compatible —— 驱动匹配的“身份证”

compatible 属性是设备节点与驱动代码之间的绑定桥梁。它的值是一个字符串列表,格式通常为 "厂商,设备型号"。内核在启动时,遍历所有设备节点,将节点的 compatible 值与驱动注册的 of_match_table 进行匹配。

// 设备树节点
ethernet@10000000 {
    compatible = "vendor,mac-controller-v2", "vendor,mac-controller";
    // 内核匹配时,优先使用第一个字符串
};

// 驱动中的匹配表
static const struct of_device_id mac_dt_ids[] = {
    { .compatible = "vendor,mac-controller-v2", .data = &mac_v2_data },
    { .compatible = "vendor,mac-controller",    .data = &mac_v1_data },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, mac_dt_ids);
兼容性设计原则:当硬件版本升级但寄存器接口兼容时,应在 compatible 列表中同时包含新版本和旧版本的字符串。内核会按列表顺序匹配,优先使用最精确的版本。这保证了新内核能驱动旧硬件,旧内核也能识别新硬件(虽然可能功能受限)。

2.2.2 reg —— 地址空间描述

reg 属性描述设备在总线上的地址区域。它由一组地址+长度对组成,每个对的解释依赖于父节点的 #address-cells 和 #size-cells 属性。

父节点属性 含义 reg 示例 解释
#address-cells = <1>
#size-cells = <1>
32位地址,32位长度 reg = <0x10000000 0x1000>; 基址 0x10000000,长度 0x1000(4KB)
#address-cells = <2>
#size-cells = <2>
64位地址,64位长度 reg = <0x0 0x10000000 0x0 0x1000>; 基址 0x10000000(高32位0),长度 0x1000
#address-cells = <1>
#size-cells = <0>
32位地址,无长度(I2C/SPI从设备) reg = <0x1c>; I2C从机地址 0x1c
// 典型MAC节点reg定义
eth0: ethernet@10000000 {
    compatible = "vendor,mac";
    reg = <0x10000000 0x1000>;          // MAC控制寄存器
    reg-names = "mac_regs";              // 可选:给reg区域命名
    interrupts = <0 33 4>;              // 中断描述
    interrupt-names = "mac_irq";         // 可选:给中断命名
};

2.2.3 interrupts —— 中断资源描述

interrupts 属性描述设备使用的中断线。它的格式依赖于父节点(通常是中断控制器)的 #interrupt-cells 属性。对于ARM GIC(通用中断控制器),通常使用3个cell:

  • 第一个cell:中断类型(0=SPI共享外设中断,1=PPI私有外设中断)
  • 第二个cell:中断号(SPI从32开始编号,PPI从16开始编号)
  • 第三个cell:中断触发方式(1=上升沿触发,2=下降沿触发,4=高电平触发,8=低电平触发)
// 中断控制器定义
gic: interrupt-controller@10480000 {
    compatible = "arm,cortex-a9-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x10480000 0x1000>,
          <0x10490000 0x1000>;
};

// 使用中断的MAC节点
eth0: ethernet@10000000 {
    compatible = "vendor,mac";
    reg = <0x10000000 0x1000>;
    interrupts = <0 33 4>;   // SPI中断号33,高电平触发
    interrupt-parent = <&gic>; // 指定中断控制器
};

2.2.4 status —— 设备状态控制

status 属性用于控制设备节点的启用或禁用状态。它允许在不修改硬件描述的情况下,通过设备树覆盖来启用或禁用某个外设。

status 值 含义 使用场景
"okay" 设备已启用,驱动应正常探测 默认启用的外设
"disabled" 设备已禁用,驱动不会探测 硬件存在但当前未使用,或引脚被复用
"reserved" 设备保留,操作系统不应使用 由固件或安全世界使用的设备
"fail" 设备检测失败,不可用 硬件自检失败时由bootloader设置
// 在板级dts中覆盖禁用某个MAC
&eth1 {
    status = "disabled";  // 第二个以太网口硬件存在但未使用
};

// 在另一款变体板中重新启用
&eth1 {
    status = "okay";
    phy-handle = <&phy1>;
};
设备树属性与驱动匹配流程 设备树节点 compatible = "vendor,mac"; reg = <0x1000 0x1000>; interrupts = <0 33 4>; status = "okay"; phy-handle = <&phy0>; 内核OF匹配层 of_match_table 匹配 compatible 字符串比对 MAC驱动 probe() 回调 资源获取 驱动 probe() 中获取资源 of_iomap(np, 0) → 获取 reg 地址映射 irq_of_parse_and_map(np, 0) → 获取中断号 of_parse_phandle(np, "phy-handle", 0) → 获取PHY节点 属性驱动流程:设备树节点 → 内核OF匹配 → 驱动probe → 资源解析 compatible 决定驱动绑定,reg 决定地址映射,interrupts 决定中断号,status 决定设备是否启用 phy-handle 等自定义属性用于建立设备间的引用关系(MAC→PHY)

2.3 特殊属性与常用节点类型

除了上述核心属性外,设备树还定义了一些特殊属性和节点类型,在网络驱动中频繁出现:

属性/节点 语法 网络驱动中的用途
#address-cells #address-cells = <1>; 定义子节点 reg 属性中地址字段的32位字数量
#size-cells #size-cells = <1>; 定义子节点 reg 属性中长度字段的32位字数量
reg-names reg-names = "mac", "dma"; 为多个 reg 区域命名,驱动通过名称获取对应地址
interrupt-names interrupt-names = "rx", "tx"; 为多个中断命名,驱动通过名称获取对应中断号
clocks clocks = <&clk MAC_CLK>; 指定设备使用的时钟源,MAC驱动需要时钟使能
resets resets = <&rst MAC_RST>; 指定设备复位控制器,用于硬件复位
phy-handle phy-handle = <&phy0>; MAC节点引用PHY节点,建立MAC-PHY绑定关系
phy-mode phy-mode = "rgmii"; 指定MAC与PHY之间的接口模式(MII/RMII/RGMII等)
fixed-link fixed-link { speed = <1000>; full-duplex; }; 当PHY不存在或为固定速率时,模拟PHY链接状态
// 完整的MAC节点示例(包含常用属性)
ð0 {
    status = "okay";
    compatible = "vendor,mac-v3", "vendor,mac";
    reg = <0x10000000 0x1000>,
          <0x10001000 0x100>;          // MAC寄存器 + DMA控制寄存器
    reg-names = "mac_regs", "dma_regs";
    interrupts = <0 33 4>, <0 34 4>;  // MAC中断 + DMA中断
    interrupt-names = "mac_irq", "dma_irq";
    clocks = <&clk MAC_CLK>, <&clk MAC_AXI_CLK>;
    clock-names = "mac_clk", "axi_clk";
    resets = <&rst MAC_RST>;
    reset-names = "mac_rst";
    phy-handle = <&phy0>;
    phy-mode = "rgmii-id";
    mac-address = [00 11 22 33 44 55];  // 可选:预设MAC地址
};

2.4 设备树包含与覆盖机制

在实际项目中,设备树文件通常分为多个层级:.dtsi(包含文件,定义SoC级硬件)和.dts(板级文件,定义具体板级硬件)。板级文件通过 #include 包含SoC的dtsi文件,然后通过标签引用覆盖或追加属性。

// soc.dtsi —— SoC级定义
soc {
    #address-cells = <1>;
    #size-cells = <1>;
    compatible = "simple-bus";

    eth0: ethernet@10000000 {
        compatible = "vendor,mac";
        reg = <0x10000000 0x1000>;
        interrupts = <0 33 4>;
        status = "disabled";  // SoC级默认禁用
    };
};

// board.dts —— 板级文件
#include "soc.dtsi"

&eth0 {
    status = "okay";          // 板级启用
    phy-handle = <&phy0>;
    phy-mode = "rgmii";
    pinctrl-0 = <&eth0_pins>;
    pinctrl-names = "default";
};
最佳实践:SoC级dtsi文件将所有外设的status设为"disabled",板级dts文件只启用实际使用的外设。这种设计避免了外设之间的资源冲突(如引脚复用),也使得同一个SoC可以轻松适配多种板级设计。

2.5 本章小结

设备树基础语法是理解网络设备驱动的基石。通过本章的学习,你应该掌握:

  • 节点与属性的层级关系:根节点→总线节点→设备节点→子设备节点
  • compatible 属性:驱动匹配的核心,格式为"厂商,设备型号",支持兼容性列表
  • reg 属性:描述地址空间,受父节点 #address-cells/#size-cells 控制
  • interrupts 属性:描述中断资源,格式由中断控制器定义
  • status 属性:控制设备启用/禁用,支持板级覆盖
  • 网络专用属性:phy-handle、phy-mode、fixed-link 等用于MAC-PHY绑定
  • 包含与覆盖机制:.dtsi 定义SoC级硬件,.dts 定义板级配置