4. 设备树常用属性:compatible、reg、interrupts、status、clocks、gpios 等属性的含义与用法

设备树(Device Tree)是描述硬件拓扑结构的节点树,每个节点通过一系列属性(property)向内核传递硬件资源信息。驱动开发者必须掌握这些属性的语义与编码规则,才能正确匹配设备、获取地址、中断、时钟、GPIO 等资源。本章逐一拆解最常用的 6 类属性,并给出实际设备树片段与驱动获取方法。

学习提示:设备树属性本质上是“键-值”对,值可以是字符串、32 位整数数组、字节数组或字符串列表。内核通过 of_* 系列 API 读取这些值,驱动无需关心底层存储格式。

4.1 compatible 属性:设备与驱动的匹配钥匙

compatible 是设备树中最重要的属性,它告诉内核该设备兼容哪个具体的芯片或驱动。内核通过该属性在驱动列表中查找匹配项。

  • 格式:字符串列表,推荐使用 "厂商,型号" 命名规范。
  • 匹配顺序:内核优先匹配列表中的第一个字符串,失败后依次尝试后续字符串。
  • 驱动侧:在 of_device_id 表中声明相同的字符串。
// 设备树节点示例
uart0: serial@10000000 {
    compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
    reg = <0x10000000 0x4000>;
    interrupts = <0 58 4>;
};

// 驱动侧匹配表
static const struct of_device_id imx_uart_dt_ids[] = {
    { .compatible = "fsl,imx6q-uart", },
    { .compatible = "fsl,imx21-uart", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
最佳实践:第一个字符串使用最精确的 SoC 型号,后续字符串使用兼容的旧型号。这样新硬件可以复用旧驱动,同时保留精确匹配能力。

4.2 reg 属性:描述地址空间与寄存器范围

reg 属性描述设备在总线上的地址区域,通常包含起始地址和长度。内核通过 platform_get_resource() 或 of_iomap() 获取这些信息。

参数 含义 示例值
address-cells 父节点中地址字段占用的 32 位单元数 1(32 位地址)或 2(64 位地址)
size-cells 父节点中长度字段占用的 32 位单元数 1(32 位长度)
reg 地址 + 长度交替排列的整数数组 <0x10000000 0x4000>
// 64 位地址空间示例
memory@80000000 {
    device_type = "memory";
    reg = <0x00000000 0x80000000 0x00000000 0x20000000>;
    // 地址: 0x00000000_80000000, 长度: 0x00000000_20000000 (512MB)
};

// 驱动获取 reg 资源
struct resource *res;
void __iomem *base;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
常见错误:忘记在父节点中正确设置 #address-cells 和 #size-cells,导致内核解析 reg 时错位。例如 64 位地址下若父节点设置 #address-cells = <1>,则高 32 位地址会被截断。

4.3 interrupts 属性:中断资源描述

interrupts 属性描述设备使用的中断号、触发类型等信息。它依赖父节点 interrupt-controller 和 #interrupt-cells 的定义。

  • interrupt-cells:定义每个中断描述需要几个 32 位整数。
  • 常见格式:
    • ARM GIC:<中断类型 中断号 触发类型>(3 cells)
    • 简单中断控制器:<中断号 触发类型>(2 cells)
  • 触发类型:0 表示上升沿,1 表示下降沿,4 表示高电平,8 表示低电平。
// 设备树节点
gpio-keys {
    compatible = "gpio-keys";
    #address-cells = <1>;
    #size-cells = <0>;
    interrupt-parent = <&intc>;

    button@0 {
        label = "Power";
        linux,code = <116>;
        interrupts = <0 30 4>;  // GIC SPI 30, 高电平触发
    };
};

// 驱动获取中断号
int irq = platform_get_irq(pdev, 0);
if (irq < 0) {
    dev_err(&pdev->dev, "failed to get IRQ\n");
    return irq;
}
调试技巧:使用 cat /proc/interrupts 查看系统中所有中断的分配情况,确认设备树中描述的中断号是否与硬件一致。

4.4 status 属性:设备启用状态控制

status 属性控制设备是否被内核初始化。它允许在 .dtsi 文件中定义设备,在具体板级 .dts 文件中按需启用或禁用。

取值 含义 内核行为
"okay" 设备启用 正常探测并绑定驱动
"disabled" 设备禁用 跳过该设备,不创建 platform device
"reserved" 保留状态 通常由固件使用,内核不处理
"fail" 设备故障 内核检测到硬件错误时设置
// 在 SoC .dtsi 中默认禁用
&uart1 {
    status = "disabled";
};

// 在板级 .dts 中启用
&uart1 {
    status = "okay";
    pinctrl-0 = <&pinctrl_uart1>;
    pinctrl-names = "default";
};
设计模式:SoC 级 .dtsi 文件将所有外设默认设为 disabled,板级 .dts 只启用实际使用的外设。这样可以避免未连接的外设浪费资源,也便于排查硬件冲突。

4.5 clocks 属性:时钟资源绑定

clocks 属性将设备连接到 SoC 的时钟树中,驱动通过时钟框架获取并控制设备的工作频率。

  • 格式:phandle + 时钟索引(由时钟控制器 #clock-cells 决定)。
  • clock-names:为每个时钟指定名称,驱动可通过名称查找,提高可读性。
  • 驱动 API:clk_get()、clk_prepare_enable()、clk_set_rate()。
// 设备树节点
i2c@2000000 {
    compatible = "snps,designware-i2c";
    reg = <0x02000000 0x1000>;
    interrupts = <0 35 4>;
    clocks = <&clkc 22>, <&clkc 23>;
    clock-names = "pclk", "sclk";
};

// 驱动获取时钟
struct clk *pclk, *sclk;

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

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

clk_prepare_enable(pclk);
clk_prepare_enable(sclk);

4.6 gpios 属性:GPIO 资源描述

gpios 属性(或 cs-gpios、reset-gpios 等命名后缀)描述设备使用的 GPIO 引脚。它依赖 GPIO 控制器节点的 #gpio-cells 定义。

属性名 典型用途 示例
gpios 通用 GPIO 引用 <&gpio1 15 GPIO_ACTIVE_LOW>
enable-gpios 使能引脚 <&gpio2 5 0>
reset-gpios 复位引脚 <&gpio3 10 GPIO_ACTIVE_HIGH>
cs-gpios 片选引脚(SPI) <&gpio4 12 0>
// 设备树节点
leds {
    compatible = "gpio-leds";

    led-red {
        label = "red";
        gpios = <&gpio1 0 GPIO_ACTIVE_HIGH>;
        linux,default-trigger = "heartbeat";
    };
};

// 驱动获取 GPIO
struct gpio_desc *red_led;

red_led = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
if (IS_ERR(red_led))
    return PTR_ERR(red_led);

gpiod_set_value(red_led, 1);  // 点亮 LED

4.7 属性综合关系图

下图展示了一个典型外设节点中,各属性如何协同描述硬件资源,以及驱动如何通过内核 API 获取这些资源:

设备树属性与驱动资源获取关系图 设备树属性 → 驱动资源获取流程 设备树节点 uart@10000000 compatible = "fsl,imx6q-uart" reg = <0x10000000 0x4000> interrupts = <0 58 4> clocks = <&clkc 22> status = "okay" 内核解析 内核解析层 of_match_node() of_address_to_resource() of_irq_parse_one() of_clk_get() of_get_named_gpio() of_device_is_available() 封装 API 驱动获取层 platform_get_irq() platform_get_resource() devm_clk_get() devm_gpiod_get() devm_ioremap() 说明: 设备树属性经过内核解析层转换为标准资源结构,驱动通过封装 API 获取,无需直接操作设备树二进制数据。 匹配类 • compatible • status 地址类 • reg • #address-cells • #size-cells 资源类 • interrupts • clocks • gpios 其他常用属性 • pinctrl-0 / pinctrl-names • dmas / dma-names • power-domains

4.8 属性使用对照表

下表汇总了 6 种核心属性的用途、驱动获取 API 和常见错误,方便快速查阅:

属性 用途 驱动获取 API 常见错误
compatible 设备与驱动匹配 of_match_node() 字符串拼写错误,大小写不匹配
reg 地址空间描述 platform_get_resource() address-cells/size-cells 设置错误
interrupts 中断资源绑定 platform_get_irq() interrupt-cells 与控制器不匹配
status 设备启用控制 of_device_is_available() 忘记在板级 dts 中改为 okay
clocks 时钟资源绑定 devm_clk_get() clock-names 与驱动不匹配
gpios GPIO 引脚绑定 devm_gpiod_get() GPIO_ACTIVE_LOW/HIGH 标志错误
实战要点:编写设备树时,先确认 SoC 参考手册中每个外设的地址范围、中断号、时钟 ID 和 GPIO 引脚号。使用 dtc -I dtb -O dts 反编译验证,再用 ls /sys/firmware/devicetree/base/ 检查内核解析结果。