3. 设备树常用属性详解:compatible、reg、interrupts、status、clocks、resets 等

设备树(Device Tree)中的属性(Property)是描述硬件设备特征、连接关系和资源需求的“数据字典”。驱动在 probe 阶段通过解析这些属性来获取硬件地址、中断号、时钟频率等关键信息,从而实现与物理设备的正确映射。本章将逐一拆解最核心的 6 个属性,并结合实际案例说明其配置规则与常见陷阱。

学习提示: 设备树属性是驱动与硬件之间的“契约”。属性值写错或缺失,轻则驱动 probe 失败,重则导致系统崩溃。建议在阅读本章时,对照实际开发板上的 .dts 文件进行验证。

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

compatible 属性是设备树中最关键的字符串属性,用于将设备节点与对应的 Linux 驱动进行绑定。其值通常采用 "厂商,设备型号" 的格式,遵循从具体到通用的降序排列原则。

3.1.1 属性格式与匹配规则

  • 格式: compatible = "vendor,device", "generic-device";
  • 匹配优先级: 内核的 of_match_table 会按字符串顺序依次匹配,第一个匹配成功的驱动将被使用。
  • 回退机制: 当没有精确匹配时,内核会尝试匹配更通用的字符串(如 "simple-bus")。
// 示例:I2C 控制器节点
i2c@ff160000 {
    compatible = "snps,designware-i2c", "simple-bus";
    reg = <0x0 0xff160000 0x0 0x1000>;
    interrupts = <0 20 4>;
    status = "okay";
};
驱动端匹配代码(of_match_table):
static const struct of_device_id dw_i2c_of_match[] = {
    { .compatible = "snps,designware-i2c", .data = &dw_i2c_data },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, dw_i2c_of_match);

3.1.2 常见错误与避坑

错误类型 现象 解决方案
字符串拼写不一致 驱动 probe 失败,dmesg 无匹配信息 严格检查设备树与驱动中的字符串是否完全一致(包括大小写)
缺少通用回退字符串 更换内核版本后驱动失效 始终保留一个通用兼容字符串(如 "simple-bus")
逗号后缺少空格 解析器报语法错误 遵循 DTS 语法:逗号后必须加空格
compatible 属性匹配流程 设备树节点 compatible = "vendor,dev-a" "generic-dev" of_match_table "vendor,dev-a" → 驱动A "generic-dev" → 驱动B 匹配成功 调用 probe() 无匹配 → probe 失败

3.2 reg —— 硬件地址与内存映射

reg 属性定义了设备在系统总线上的地址空间,包括起始地址和长度。驱动通过 of_iomap() 或 platform_get_resource() 来获取这些信息,并建立虚拟地址映射。

3.2.1 属性结构

reg = <address1 length1 address2 length2 ...>;
  • address: 设备的基地址(物理地址)。
  • length: 地址空间的长度(字节数)。
  • 多段地址: 当设备有多个不连续的地址空间时,可以依次列出多组 <address length>。

3.2.2 地址格式与 #address-cells / #size-cells

reg 的解析依赖于父节点中的 #address-cells 和 #size-cells 属性:

属性 含义 常见值
#address-cells 地址字段占用的 32 位单元数 1(32位地址)或 2(64位地址)
#size-cells 长度字段占用的 32 位单元数 1(32位长度)或 2(64位长度)
// 64位地址示例
soc@0 {
    #address-cells = <2>;
    #size-cells = <2>;

    uart@ff100000 {
        reg = <0x0 0xff100000 0x0 0x1000>;
        // 地址:0xff100000(高32位=0,低32位=0xff100000)
        // 长度:0x1000(高32位=0,低32位=0x1000)
    };
};
常见错误: 当父节点 #address-cells = <2> 时,子节点 reg 中的地址必须写 2 个 32 位值(即使高 32 位为 0)。漏写会导致地址解析错位,驱动读取到错误的基地址。
reg 属性地址映射流程 设备树节点 reg = <0x1000 0x200> #address-cells = <1> 内核解析 of_iomap(node, 0) platform_get_resource() 物理地址映射 基地址:0x1000 长度:0x200 ⚠️ 地址解析规则 #address-cells 决定地址占几个 u32,#size-cells 决定长度占几个 u32 两者不匹配时,reg 解析将完全错位

3.3 interrupts —— 中断资源描述

interrupts 属性用于描述设备使用的中断号、触发类型和中断控制器层级关系。它是驱动实现异步事件处理的关键。

3.3.1 属性格式

interrupts = <interrupt-specifier>;
interrupt-parent = <&phandle>;
  • interrupt-specifier: 由中断控制器定义的 1~3 个 32 位值,通常包含中断号、触发类型等。
  • interrupt-parent: 指向中断控制器的 phandle,指定该设备连接到哪个中断控制器。

3.3.2 中断触发类型

宏定义 值 触发方式
IRQ_TYPE_NONE 0 未指定
IRQ_TYPE_EDGE_RISING 1 上升沿触发
IRQ_TYPE_EDGE_FALLING 2 下降沿触发
IRQ_TYPE_LEVEL_HIGH 4 高电平触发
IRQ_TYPE_LEVEL_LOW 8 低电平触发
// 实际配置示例
gpio-keys {
    compatible = "gpio-keys";
    interrupt-parent = &gpio1;
    interrupts = <5 IRQ_TYPE_EDGE_FALLING>,
                 <6 IRQ_TYPE_EDGE_RISING>;
};
驱动端获取中断号:
int irq = platform_get_irq(pdev, 0);  // 获取第一个中断
if (irq < 0) {
    dev_err(&pdev->dev, "failed to get IRQ\n");
    return irq;
}
ret = devm_request_irq(&pdev->dev, irq, handler,
                       IRQF_TRIGGER_FALLING, "mydev", data);
中断控制器层级与设备连接 GIC (中断控制器) GPIO 控制器 (中断父节点) 设备A (interrupts = <5 4>) 设备B (interrupts = <6 1>)

3.4 status —— 设备状态控制

status 属性用于控制设备节点的启用或禁用状态,是设备树中实现“硬件裁剪”和“调试开关”的常用手段。

3.4.1 可选值

值 含义 驱动行为
"okay" 设备启用 正常 probe,注册设备
"disabled" 设备禁用 跳过 probe,不注册设备
"reserved" 保留状态 通常由固件使用,内核不处理
"fail" 设备故障 标记为不可用,驱动不加载

3.4.2 覆盖机制

在 .dtsi 文件中通常将设备默认设为 disabled,然后在具体的 .dts 板级文件中通过 &node 引用将其改为 okay:

// soc.dtsi(默认禁用)
uart1: serial@ff100000 {
    compatible = "snps,dw-apb-uart";
    reg = <0xff100000 0x1000>;
    status = "disabled";
};

// myboard.dts(板级启用)
&uart1 {
    status = "okay";
    clocks = <&clk_uart1>;
};
调试建议: 当驱动 probe 失败时,首先检查 status 是否为 "okay"。很多移植问题源于 .dtsi 中默认 disabled 而板级文件忘记覆盖。

3.5 clocks —— 时钟资源绑定

clocks 属性用于指定设备使用的时钟源,驱动通过时钟框架(CCF)来获取、使能或调整时钟频率。

3.5.1 属性格式

clocks = <&phandle1>, <&phandle2>;
clock-names = "bus", "core";
  • clocks: 指向时钟提供者节点的 phandle 列表。
  • clock-names: 为每个时钟指定名称,驱动通过名称来获取对应的时钟句柄。

3.5.2 驱动端使用

struct clk *bus_clk, *core_clk;

bus_clk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(bus_clk))
    return PTR_ERR(bus_clk);

core_clk = devm_clk_get(&pdev->dev, "core");
if (IS_ERR(core_clk))
    return PTR_ERR(core_clk);

clk_prepare_enable(bus_clk);
clk_prepare_enable(core_clk);

3.6 resets —— 复位信号控制

resets 属性用于描述设备的复位信号,驱动可以通过复位框架在初始化或错误恢复时对设备进行硬件复位。

3.6.1 属性格式

resets = <&phandle1 reset-specifier>;
reset-names = "ctrl";
  • resets: 指向复位控制器节点的 phandle,以及复位标识符。
  • reset-names: 为每个复位信号命名,便于驱动引用。

3.6.2 驱动端使用

struct reset_control *rst;

rst = devm_reset_control_get(&pdev->dev, "ctrl");
if (IS_ERR(rst))
    return PTR_ERR(rst);

// 执行硬件复位
reset_control_assert(rst);
udelay(10);
reset_control_deassert(rst);

3.7 属性组合实战案例

以下是一个完整的 SPI 控制器设备树节点,展示了多个属性的协同使用:

spi@ff110000 {
    compatible = "snps,dw-apb-ssi";
    reg = <0x0 0xff110000 0x0 0x1000>;
    interrupts = <0 36 4>;
    clocks = <&clk_spi_mst>;
    clock-names = "mst";
    resets = <&rstgen RST_SPI0>;
    reset-names = "ctrl";
    status = "okay";

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

    spi-flash@0 {
        compatible = "jedec,spi-nor";
        reg = <0>;
        spi-max-frequency = <50000000>;
    };
};
驱动 probe 流程总结:
  1. 通过 compatible 匹配驱动
  2. 通过 reg 获取地址并 ioremap
  3. 通过 interrupts 注册中断处理函数
  4. 通过 clocks 使能时钟
  5. 通过 resets 释放复位
  6. 检查 status 确认设备可用
设备树属性与驱动资源映射关系 设备树节点 compatible reg interrupts clocks / resets status of_match_table platform_get_resource platform_get_irq clk_get / reset_control of_device_is_available

3.8 本章小结

属性 核心作用 驱动获取方式 常见错误
compatible 驱动匹配 of_match_table 字符串拼写不一致
reg 地址映射 of_iomap() address/size-cells 不匹配
interrupts 中断绑定 platform_get_irq() 中断号与控制器不匹配
status 设备开关 of_device_is_available() 忘记覆盖 disabled
clocks 时钟使能 devm_clk_get() 时钟名称与驱动不匹配
resets 复位控制 devm_reset_control_get() 复位顺序错误导致死锁