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

设备树属性是连接硬件描述与内核驱动行为的桥梁。每个节点通过一组标准属性向内核传递硬件拓扑、地址映射、中断路由、时钟与 GPIO 依赖等关键信息。本章将逐一拆解最常用的六大属性,结合 DTB 加载与驱动 probe 阶段的排查要点,帮助你在实际调试中快速定位属性配置错误。

排查前置提醒: 属性值写错(如大小写、地址位数、中断类型码)是 DTB 加载失败或驱动 probe 返回 -ENODEV 的最常见原因。建议始终对照芯片参考手册与内核 binding 文档逐字段校验。

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

compatible 属性是设备节点与内核驱动之间建立绑定的核心字段。驱动通过 of_match_table 中的字符串列表与设备树节点的 compatible 值进行精确匹配。

4.1.1 格式与匹配规则

  • 格式: "厂商,型号",例如 "ti,am335x-uart"、"fsl,imx6q-uart"。
  • 匹配顺序: 内核优先匹配列表中的第一个字符串;若驱动支持多个兼容型号,则按 of_match_table 中的顺序依次尝试。
  • 通配符: 不支持下划线或通配符,必须精确字符串匹配。
// 设备树节点示例
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ti,omap3-uart";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};

// 驱动匹配表示例
static const struct of_device_id omap_uart_of_match[] = {
    { .compatible = "ti,am335x-uart", .data = &am335x_uart_data },
    { .compatible = "ti,omap3-uart",  .data = &omap3_uart_data },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, omap_uart_of_match);
常见排查点: 驱动 probe 未执行时,优先检查 compatible 字符串是否与驱动中的 of_match_table 完全一致(包括大小写、连字符、逗号后是否有空格)。使用 dtc -I dtb -O dts 反编译 DTB 确认实际值。

4.1.2 匹配流程 SVG

compatible 属性匹配流程 设备树节点 compatible = "ti,am335x-uart" 驱动 of_match_table "ti,am335x-uart" → data 匹配成功 调用 probe() 匹配失败 返回 -ENODEV 排查要点: 1. 使用 dtc 反编译确认 DTB 中的 compatible 字符串;2. 检查驱动 of_match_table 末尾是否有 sentinel 空结构;3. 确认模块已加载且未屏蔽。

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

reg 属性描述设备在系统总线上的地址范围。内核通过它完成 IO 资源注册,驱动调用 of_iomap() 或 platform_get_resource() 获取映射后的虚拟地址。

4.2.1 格式与地址单元

属性 含义 示例
#address-cells 地址字段占用的 32 位单元数 1(32位地址)、2(64位地址)
#size-cells 长度字段占用的 32 位单元数 1(32位长度)、2(64位长度)
reg 地址 + 长度对,格式为 <addr size> <0x44e09000 0x2000>
// 32位地址空间示例
soc {
    #address-cells = <1>;
    #size-cells = <1>;
    uart0: serial@44e09000 {
        reg = <0x44e09000 0x2000>;  // 起始地址 0x44e09000,长度 0x2000
    };
};

// 64位地址空间示例
pcie@40000000 {
    #address-cells = <3>;
    #size-cells = <2>;
    reg = <0x00 0x40000000 0x00 0x10000000>;  // 高32位 低32位 高32位长度 低32位长度
};
排查技巧: 使用 cat /proc/iomem 查看内核注册的 IO 资源是否与 DTB 中的 reg 一致。若驱动 probe 返回 -EBUSY,通常意味着 reg 地址范围与其他设备重叠。

4.2.2 reg 解析流程 SVG

reg 属性解析与资源注册流程 DTB reg 原始值 <0x44e09000 0x2000> of_translate_address() 解析地址与长度单元 platform_get_resource() 注册到 IORESOURCE_MEM 解析失败 地址越界 / 单元数不匹配

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

interrupts 属性描述设备的中断请求线。它依赖 interrupt-parent 指定中断控制器,并通过 #interrupt-cells 定义每个中断描述所需的单元数。

4.3.1 中断描述格式

中断控制器类型 #interrupt-cells 格式说明
GIC(通用中断控制器) 3 <中断类型 中断号 触发标志>
简单中断控制器 1 <中断号>
级联中断控制器 2 <父中断号 子中断号>
// GIC 中断示例(ARM 平台)
gic: interrupt-controller@48241000 {
    compatible = "arm,cortex-a15-gic";
    #interrupt-cells = <3>;
    interrupt-controller;
    reg = <0x48241000 0x1000>;
};

uart0: serial@44e09000 {
    compatible = "ti,am335x-uart";
    reg = <0x44e09000 0x2000>;
    interrupt-parent = <&gic>;
    interrupts = <0 72 4>;  // 类型:SPI(0) 中断号:72 触发:高电平(4)
};
常见排查点: 中断号与触发类型必须与芯片手册完全一致。使用 cat /proc/interrupts 查看中断是否注册成功;若中断未触发,检查 interrupt-parent 的 phandle 是否正确指向中断控制器节点。

4.3.2 中断路由 SVG

中断路由与属性绑定关系 uart0 设备节点 interrupt-parent = <&gic> interrupts = <0 72 4> GIC 中断控制器 #interrupt-cells = <3> 中断类型: SPI(0) CPU 中断处理 IRQ 72 注册成功 排查命令: cat /proc/interrupts | grep 72 — 查看中断触发计数 cat /proc/device-tree/uart0/interrupts — 查看 DTB 中的原始中断值

4.4 status —— 设备启用控制

status 属性控制设备节点是否被内核枚举。它通常用于在 board-level DTS 中覆盖 SoC 级 DTSI 中的设备状态。

4.4.1 可选值及含义

值 含义 内核行为
"okay" 设备启用 正常创建 platform_device,调用 probe
"disabled" 设备禁用 跳过该节点,不创建设备
"reserved" 保留状态 通常用于特殊固件场景
"fail" 设备故障 内核标记为不可用
// SoC 级 DTSI 中默认禁用
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart";
    reg = <0x44e09000 0x2000>;
    status = "disabled";
};

// Board 级 DTS 中启用
&uart0 {
    status = "okay";
};
排查技巧: 若驱动 probe 未执行,首先检查 status 是否为 "okay"。使用 cat /proc/device-tree/<path>/status 查看当前状态值。注意字符串必须小写,"OK" 或 "Ok" 均无效。

4.5 clocks —— 时钟依赖与频率配置

clocks 属性描述设备依赖的时钟源。它通过 phandle 引用时钟控制器节点,驱动使用 clk_get() 或 devm_clk_get() 获取时钟句柄。

4.5.1 时钟属性格式

// 时钟控制器定义
clocks {
    uart0_clk: clock@44e00000 {
        compatible = "ti,am335x-clock";
        #clock-cells = <0>;
        clock-frequency = <48000000>;
    };
};

// 设备引用时钟
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart";
    reg = <0x44e09000 0x2000>;
    clocks = <&uart0_clk>;
    clock-names = "fck";
};

4.5.2 时钟属性对比表

属性 作用 驱动获取方式
clocks 时钟 phandle 列表 of_clk_get()
clock-names 时钟名称列表,与 clocks 一一对应 devm_clk_get(dev, name)
clock-frequency 固定频率(用于无时钟控制器场景) of_property_read_u32()
常见排查点: 时钟未使能是驱动挂死或数据传输异常的主要原因。使用 cat /sys/kernel/debug/clk/clk_summary 查看时钟是否已开启;检查 clock-names 是否与驱动中的字符串匹配。

4.6 gpios —— GPIO 引用与配置

gpios 属性(或 cs-gpios、reset-gpios 等命名后缀)描述设备使用的 GPIO 引脚。驱动通过 gpiod_get() 或 of_get_named_gpio() 获取 GPIO 描述符。

4.6.1 GPIO 属性格式

// GPIO 控制器定义
gpio0: gpio@44e07000 {
    compatible = "ti,am335x-gpio";
    #gpio-cells = <2>;
    gpio-controller;
    reg = <0x44e07000 0x1000>;
};

// 设备引用 GPIO
led0 {
    compatible = "gpio-leds";
    gpios = <&gpio0 15 GPIO_ACTIVE_HIGH>;  // GPIO0_15, 高电平有效
};

// 带名称的 GPIO 引用
spi0: spi@48030000 {
    compatible = "ti,am335x-spi";
    cs-gpios = <&gpio0 10 GPIO_ACTIVE_LOW>,
               <&gpio0 11 GPIO_ACTIVE_LOW>;
};

4.6.2 GPIO 属性排查表

排查项 检查方法 典型错误
GPIO 号越界 对比芯片手册 GPIO 范围 引用了不存在的 GPIO bank
GPIO 复用冲突 检查 pinctrl 配置 同一引脚被两个设备使用
有效电平错误 确认 GPIO_ACTIVE_HIGH/LOW LED 常亮或常灭
phandle 错误 检查 gpio-controller 节点路径 编译报错或 probe 返回 -EINVAL

4.6.3 GPIO 引用关系 SVG

GPIO 属性引用关系 led0 设备节点 gpios = <&gpio0 15 1> gpio0 控制器 #gpio-cells = <2> 物理引脚 GPIO0_15 排查命令: cat /sys/kernel/debug/gpio — 查看 GPIO 申请状态;cat /proc/device-tree/led0/gpios — 查看原始 GPIO 值

4.7 属性综合排查对照表

属性 驱动获取 API 常见错误 排查命令
compatible of_match_table 字符串不匹配、大小写错误 dtc -I dtb -O dts
reg platform_get_resource() 地址越界、#address-cells 不匹配 cat /proc/iomem
interrupts platform_get_irq() 中断号错误、触发类型不匹配 cat /proc/interrupts
status 内核自动判断 拼写错误、未设置为 "okay" cat /proc/device-tree/.../status
clocks devm_clk_get() 时钟未使能、clock-names 不匹配 cat /sys/kernel/debug/clk/clk_summary
gpios gpiod_get() GPIO 号越界、复用冲突 cat /sys/kernel/debug/gpio
核心原则: 设备树属性的本质是“硬件拓扑的静态描述”。驱动通过标准 API 解析这些属性,任何解析失败都会导致 probe 提前返回。排查时遵循“从 DTB 原始值 → 内核解析结果 → 驱动使用方式”的三层验证路径,可以快速定位 90% 的属性相关问题。