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

设备树中的属性(Property)是描述硬件设备特征、配置和连接关系的关键载体。每个节点通过一系列标准属性向内核驱动传递必要信息。本章将深入解析嵌入式开发中最常用的六大属性,帮助您准确理解其语义、格式及在驱动匹配与资源获取中的实际作用。

核心认知:设备树属性本质上是“键-值”对,键是字符串,值可以是字符串、整数数组、二进制数据或字符串列表。驱动通过OF(Open Firmware)API读取这些属性,完成设备初始化。

4.1 compatible 属性:驱动匹配的“身份证”

compatible 属性是设备树节点与Linux驱动之间建立关联的核心桥梁。它指定了设备所兼容的硬件型号,驱动通过该属性进行匹配探测。

4.1.1 格式与规范

属性值为字符串列表,推荐采用 "厂商,型号" 的命名规范,确保全局唯一性。列表中的第一个字符串为最精确的匹配项,后续为兼容的通用型号。

compatible = "ti,am335x-uart", "ns16550";

上述示例表示该设备是TI AM335x平台的UART控制器,同时兼容标准的ns16550 UART。内核驱动匹配时,优先查找 "ti,am335x-uart",若未找到则尝试 "ns16550"。

4.1.2 驱动匹配流程

compatible 属性驱动匹配流程 内核启动,解析DTS 遍历设备树节点 读取节点 compatible 属性 与驱动 of_match_table 比对

4.1.3 驱动端匹配表示例

static const struct of_device_id am335x_uart_of_match[] = {
    { .compatible = "ti,am335x-uart", },
    { .compatible = "ns16550", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, am335x_uart_of_match);

static struct platform_driver am335x_uart_driver = {
    .probe  = am335x_uart_probe,
    .remove = am335x_uart_remove,
    .driver = {
        .name = "am335x-uart",
        .of_match_table = am335x_uart_of_match,
    },
};
常见错误:compatible 字符串拼写错误或大小写不一致是导致驱动无法加载的首要原因。务必与数据手册或内核源码中的定义严格一致。

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

reg 属性用于描述设备在总线上的地址空间,通常包含起始地址和长度。对于内存映射设备,它定义了驱动需要操作的寄存器区域。

4.2.1 格式与组成

reg 由一组或多组 <地址 长度> 对组成,地址和长度的位数由父节点的 #address-cells 和 #size-cells 决定。

uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
    interrupts = <72>;
};

上述示例中,reg = <0x44e09000 0x2000> 表示UART0的寄存器基地址为0x44E09000,地址空间大小为0x2000字节。

4.2.2 多段地址空间

某些设备拥有多个不连续的地址区域,例如包含控制寄存器和数据缓冲区。此时 reg 可包含多组地址对:

dma: dma-controller@4a000000 {
    compatible = "ti,edma3";
    reg = <0x4a000000 0x10000>  /* 全局寄存器 */
          <0x4a100000 0x10000>; /* 通道寄存器 */
    interrupts = <12 13 14>;
};

4.2.3 地址与长度单元说明

属性 含义 典型值
#address-cells 地址字段占用的32位单元数 1(32位地址)或 2(64位地址)
#size-cells 长度字段占用的32位单元数 1(32位长度)或 0(无长度)
驱动获取示例:驱动通过 platform_get_resource(pdev, IORESOURCE_MEM, 0) 获取第一段地址空间,返回 struct resource 结构体,内含起始地址和长度。

4.3 interrupts 属性:连接中断控制器

interrupts 属性描述设备使用的中断资源,包括中断号、触发类型等信息。它必须与父节点的 interrupt-parent 或全局中断控制器配合使用。

4.3.1 基本格式

interrupts = <中断号 触发类型>;

中断号的位数由中断控制器的 #interrupt-cells 决定。以ARM GIC(通用中断控制器)为例,通常使用2个cell:

gpio0: gpio@44e07000 {
    compatible = "ti,omap4-gpio";
    reg = <0x44e07000 0x1000>;
    interrupts = <96 0x4>;  /* 中断号96,高电平触发 */
};

4.3.2 中断触发类型

标志位 触发类型 说明
0x0 IRQ_TYPE_NONE 未指定
0x1 IRQ_TYPE_EDGE_RISING 上升沿触发
0x2 IRQ_TYPE_EDGE_FALLING 下降沿触发
0x3 IRQ_TYPE_EDGE_BOTH 双边沿触发
0x4 IRQ_TYPE_LEVEL_HIGH 高电平触发
0x8 IRQ_TYPE_LEVEL_LOW 低电平触发

4.3.3 中断映射关系

设备树中断映射关系 设备节点 interrupts = <96 0x4> 中断控制器 (GIC) #interrupt-cells = <2> 中断号96由GIC解析,触发类型0x4表示高电平触发 驱动通过 platform_get_irq(pdev, 0) 获取中断号

4.4 status 属性:设备启用控制

status 属性用于控制设备节点的启用或禁用状态,是设备树中实现硬件裁剪和配置灵活性的重要手段。

4.4.1 可选值

值 含义 说明
"okay" 设备启用 驱动正常探测并初始化设备
"disabled" 设备禁用 内核跳过该节点,不创建设备
"reserved" 保留 设备被保留,驱动不操作
"fail" 设备故障 设备存在但不可用

4.4.2 典型应用场景

在SoC级DTSI文件中,外设通常默认设置为 status = "disabled",在板级DTS文件中根据实际硬件焊接情况选择性启用:

// am33xx.dtsi (SoC级)
uart0: serial@44e09000 {
    compatible = "ti,am335x-uart";
    reg = <0x44e09000 0x2000>;
    status = "disabled";
};

// myboard.dts (板级)
&uart0 {
    status = "okay";
    pinctrl-0 = <&uart0_pins>;
};
最佳实践:始终在板级DTS中显式设置 status = "okay",避免依赖默认值。同时检查硬件原理图,确保启用的外设引脚未被复用冲突。

4.5 clocks 属性:时钟资源绑定

clocks 属性将设备与其所需的时钟源关联起来,驱动通过该属性获取时钟句柄,实现时钟使能、频率配置等操作。

4.5.1 基本格式

clocks = <&clock_controller clock_id>;
clock-names = "name";

clocks 属性值是一个phandle加时钟索引的组合,clock-names 为每个时钟指定名称,便于驱动按名称查找。

i2c0: i2c@44e0b000 {
    compatible = "ti,omap4-i2c";
    reg = <0x44e0b000 0x1000>;
    interrupts = <70>;
    clocks = <&l4_wkup_clkctrl 0>;
    clock-names = "fck";
};

4.5.2 多时钟设备

复杂外设可能需要多个时钟源,例如AHB总线时钟和APB总线时钟:

usb: usb@4a064000 {
    compatible = "ti,am335x-usb";
    reg = <0x4a064000 0x1000>;
    interrupts = <18>;
    clocks = <&l3_clkctrl 0>, <&l4_clkctrl 0>;
    clock-names = "usb_ahb", "usb_apb";
};

4.5.3 驱动获取时钟

static int my_probe(struct platform_device *pdev)
{
    struct clk *clk;

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

    clk_prepare_enable(clk);
    // ... 设备初始化 ...
}

4.6 gpios 属性:GPIO资源描述

gpios 属性用于描述设备使用的GPIO引脚,包括GPIO控制器引用、引脚号和标志位。它是设备树中连接外设与GPIO控制器的标准方式。

4.6.1 格式与示例

gpios = <&gpio_controller pin_number flags>;
led: led@0 {
    compatible = "gpio-leds";
    gpios = <&gpio0 12 GPIO_ACTIVE_HIGH>;
    label = "user-led";
};

上述示例中,LED连接到GPIO0控制器的第12号引脚,高电平有效。

4.6.2 GPIO标志位

标志位 宏定义 说明
0 GPIO_ACTIVE_HIGH 高电平有效
1 GPIO_ACTIVE_LOW 低电平有效
2 GPIO_OPEN_DRAIN 开漏输出
4 GPIO_OPEN_SOURCE 开源输出

4.6.3 多GPIO设备

某些设备需要多个GPIO引脚,例如复位引脚和中断引脚:

wifi: wifi@1 {
    compatible = "broadcom,bcm4334";
    reg = <1>;
    gpios = <&gpio1 15 GPIO_ACTIVE_LOW>,  /* 复位引脚 */
            <&gpio1 16 GPIO_ACTIVE_HIGH>; /* 中断引脚 */
    gpio-names = "reset", "irq";
};

4.6.4 驱动获取GPIO

static int my_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    struct gpio_desc *reset_gpio;

    reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_HIGH);
    if (IS_ERR(reset_gpio))
        return PTR_ERR(reset_gpio);

    gpiod_set_value(reset_gpio, 0);  // 拉低复位
    msleep(10);
    gpiod_set_value(reset_gpio, 1);  // 释放复位
}
属性对比总结:
  • compatible:驱动匹配的核心标识,采用"厂商,型号"格式。
  • reg:描述地址空间,支持多段不连续区域。
  • interrupts:绑定中断资源,需配合中断控制器解析。
  • status:控制设备启用/禁用,实现硬件配置灵活裁剪。
  • clocks:关联时钟源,驱动通过名称获取时钟句柄。
  • gpios:描述GPIO引脚,支持命名和标志位配置。

4.7 属性综合应用示例

以下是一个完整的SPI设备节点,综合使用了本章讲解的所有属性:

spi0: spi@48030000 {
    compatible = "ti,am335x-spi", "ti,omap4-spi";
    reg = <0x48030000 0x1000>;
    interrupts = <65>;
    clocks = <&l4_per_clkctrl 0>;
    clock-names = "fck";
    status = "okay";
    #address-cells = <1>;
    #size-cells = <0>;

    adc: adc@0 {
        compatible = "ti,ads1256";
        reg = <0>;
        spi-max-frequency = <1000000>;
        interrupts-extended = <&gpio2 5 GPIO_ACTIVE_LOW>;
        clocks = <&adc_clk 0>;
        clock-names = "adc_clk";
        gpios = <&gpio2 6 GPIO_ACTIVE_HIGH>;  /* 复位引脚 */
        status = "okay";
    };
};
学习建议:在实际开发中,建议对照芯片数据手册和内核源码中的设备树绑定文档(Documentation/devicetree/bindings/)来编写属性,确保每个属性的取值和格式符合硬件规范。