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

设备树(Device Tree)中的属性(property)是描述硬件配置与连接关系的关键载体。每个节点通过一组标准属性向内核传递设备类型、地址映射、中断路由、时钟与复位依赖等信息。本章将逐一拆解最常用的六大属性,并结合实际案例说明其解析规则与常见陷阱。

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

compatible 属性是设备树中最核心的字符串列表属性,用于将设备节点与内核驱动进行绑定。内核在启动时遍历设备树,通过 of_match_table 逐一比对节点的 compatible 值,找到匹配的驱动后执行 probe。

字段 说明 示例
厂商前缀 标识设备制造商,通常与 vendor-prefix 绑定 ti, fsl, samsung,
设备型号 具体芯片或 IP 核名称 am335x-uart imx6q-ecspi
兼容列表 从最具体到最通用,内核按顺序匹配 "ti,am335x-uart", "ns16550"
// 典型 compatible 写法
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};
匹配优先级:内核优先匹配列表中的第一个字符串。若第一个驱动未注册,则尝试第二个。建议将最精确的型号放在最前,通用型号放在其后,以兼顾精确匹配与向后兼容。
compatible 属性匹配流程 设备树节点 compatible = "A","B" 内核匹配引擎 of_match_table 匹配成功 调用 probe() 匹配失败 跳过该节点 匹配顺序:先精确匹配 "A",失败后尝试通用匹配 "B"

3.2 reg —— 地址空间与内存映射

reg 属性描述设备在系统总线上的地址范围,由一组或多组 (address, length) 对组成。其解析依赖于父节点的 #address-cells 和 #size-cells 属性。

// 32 位地址空间示例
sram@80000000 {
    compatible = "mmio-sram";
    reg = <0x80000000 0x10000>;  // 起始地址 0x80000000,长度 64KB
};

// 64 位地址空间示例
pcie@4000000000 {
    compatible = "pci-host-ecam-generic";
    reg = <0x00000040 0x00000000 0x0 0x10000000>;  // 高32位 低32位 长度高32位 长度低32位
    #address-cells = <3>;
    #size-cells = <2>;
};
父节点属性 含义 典型值
#address-cells 每个地址占用的 32 位单元数 1(32位地址)或 2(64位地址)
#size-cells 每个长度占用的 32 位单元数 1(32位长度)或 2(64位长度)
常见错误:当父节点使用 #address-cells = <2> 时,子节点 reg 中的地址必须写满两个 32 位单元,否则内核解析会错位,导致 ioremap 映射到错误的物理地址。

3.3 interrupts —— 中断路由与触发类型

interrupts 属性描述设备的中断请求线,包含中断号、触发类型等信息。其解析依赖于父节点或中断控制器的 #interrupt-cells 属性。

// 简单中断控制器(GIC)
gic: interrupt-controller@f0001000 {
    compatible = "arm,cortex-a9-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0xf0001000 0x1000>;
};

// 设备节点使用中断
uart@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <0 72 4>;  // 中断类型 0(SPI) 中断号72 触发类型4(高电平)
};
中断类型(GIC 三单元) 值 说明
中断类型 0 = SPI, 1 = PPI SPI 共享外设中断,PPI 私有外设中断
中断号 32~1019(SPI) 实际硬件中断线编号
触发类型 1 = 上升沿, 4 = 高电平 决定中断检测方式
中断路由拓扑 UART 外设 interrupts = <0 72 4> IRQ 72 GIC 中断控制器 #interrupt-cells = <3> FIQ/IRQ ARM CPU 中断处理 中断属性解析流程 1. 内核读取 interrupts 属性中的原始数据 2. 根据父节点 #interrupt-cells 解析中断号与触发类型 3. 调用 irq_of_parse_and_map() 映射为 Linux IRQ 号

3.4 status —— 设备启用状态控制

status 属性用于控制设备节点的启用或禁用状态,是板级适配中最常用的开关属性之一。内核在解析设备树时,会检查该属性决定是否创建设备并调用驱动。

status 值 含义 内核行为
"okay" 设备启用 正常创建设备并匹配驱动
"disabled" 设备禁用 跳过该节点,不创建设备
"fail" 设备故障 标记为失败,不 probe
缺失 默认启用 视为 "okay"(部分架构有差异)
// SoC 级 dtsi 中默认禁用
uart1: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
    status = "disabled";
};

// 板级 dts 中启用
&uart1 {
    status = "okay";
};
最佳实践:SoC 级 .dtsi 文件应将所有外设默认设为 disabled,板级 .dts 文件按需启用。这样可以避免未使用的设备占用资源,同时减少板级适配时的冲突。

3.5 clocks —— 时钟源与时钟门控

clocks 属性描述设备所需的时钟源,通常与 clock-names 配合使用。内核通过时钟框架(CCF)获取时钟句柄,用于使能、设置频率或门控操作。

// 时钟控制器定义
clocks {
    uart_clk: clock@0 {
        compatible = "fixed-clock";
        #clock-cells = <0>;
        clock-frequency = <48000000>;
    };
};

// 设备节点引用时钟
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
    clocks = <&uart_clk>;
    clock-names = "uart_clk";
};
属性 类型 说明
clocks phandle 列表 指向时钟控制器的句柄,可包含多个时钟
clock-names 字符串列表 为每个时钟指定名称,便于驱动通过名称查找
assigned-clocks phandle 列表 指定需要初始化的时钟,配合 assigned-clock-rates 使用
时钟依赖关系 固定时钟源 48MHz 时钟控制器 CCF 框架 UART 设备 clocks = <&uart_clk> 驱动通过 clk_get(dev, "uart_clk") 获取时钟句柄 clk_prepare_enable() 使能时钟后设备才能正常工作

3.6 resets —— 复位信号控制

resets 属性描述设备所需的复位信号,通常与 reset-names 配合使用。内核通过复位框架(reset controller framework)管理设备的复位与释放时序。

// 复位控制器定义
rstc: reset-controller@44e00000 {
    compatible = "ti,am335x-reset";
    #reset-cells = <1>;
    reg = <0x44e00000 0x1000>;
};

// 设备节点引用复位
mmc0: mmc@48060000 {
    compatible = "ti,am335x-mmc";
    reg = <0x48060000 0x1000>;
    interrupts = <64>;
    clocks = <&mmc_clk>;
    clock-names = "mmc_clk";
    resets = <&rstc 5>;       // 复位控制器句柄 + 复位 ID
    reset-names = "mmc_rst";
};
属性 类型 说明
resets phandle + 参数列表 指向复位控制器的句柄,参数由 #reset-cells 决定
reset-names 字符串列表 为每个复位信号命名,便于驱动查找
复位时序要点:驱动在 probe 阶段应调用 devm_reset_control_get() 获取复位句柄,并在设备初始化前调用 reset_control_deassert() 释放复位,确保设备处于正常工作状态。对于共享复位信号,需注意多个设备之间的复位依赖关系。

3.7 属性组合实战案例

以下是一个完整的 I2C 控制器设备树节点,展示了上述属性的组合使用方式:

i2c0: i2c@44e0b000 {
    compatible = "ti,am335x-i2c", "ti,omap4-i2c";
    reg = <0x44e0b000 0x1000>;
    interrupts = <70>;
    clocks = <&i2c_clk>;
    clock-names = "i2c_clk";
    resets = <&rstc 3>;
    reset-names = "i2c_rst";
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;

    eeprom@50 {
        compatible = "atmel,24c02";
        reg = <0x50>;
        status = "okay";
    };
};
调试建议:当设备无法正常 probe 时,按以下顺序检查设备树属性:
1. compatible 是否与驱动匹配表一致
2. status 是否为 "okay"
3. clocks 对应的时钟是否已使能
4. resets 对应的复位是否已释放
5. interrupts 中断号是否与其他设备冲突
设备树属性依赖关系总览 设备节点 核心属性集合 compatible reg interrupts clocks resets 每个属性对应内核中的一个子系统:驱动匹配、内存映射、中断管理、时钟框架、复位框架