3. 设备树常用属性详解:compatible、reg、interrupts、status、clocks、gpios 等标准属性
设备树(Device Tree)中的属性(Property)是描述硬件配置与连接关系的关键载体。每个节点通过一组标准属性向内核驱动程序传递硬件资源信息。本章将深入解析最常用的六个标准属性,包括其语法规则、内核解析机制及典型应用场景。
3.1 属性概览与分类
设备树属性按功能可分为三大类:设备标识类、资源描述类和状态控制类。下表列出了本章重点讲解的属性及其所属类别:
| 属性名 | 类别 | 功能简述 | 必需性 |
|---|---|---|---|
compatible |
设备标识 | 指定设备兼容的驱动名称列表 | 必需 |
reg |
资源描述 | 定义设备地址空间与寄存器范围 | 视总线类型而定 |
interrupts |
资源描述 | 描述设备使用的中断号与触发类型 | 中断设备必需 |
status |
状态控制 | 控制设备节点的启用或禁用 | 可选 |
clocks |
资源描述 | 关联设备使用的时钟源与时钟索引 | 有时钟依赖时必需 |
gpios |
资源描述 | 声明设备连接的 GPIO 引脚及用途 | 使用 GPIO 时必需 |
compatible 是唯一一个所有节点都必须包含的属性。缺少该属性将导致内核无法为设备匹配对应的驱动程序。
3.2 compatible 属性:驱动匹配的核心
compatible 属性是一个字符串列表,用于将设备节点与内核驱动程序绑定。其格式为 "厂商,设备型号",内核通过设备树匹配表(OF Match Table)进行查找。
// 设备树节点示例
uart0: serial@10000000 {
compatible = "fsl,imx6q-uart", "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 31 4>;
};
内核驱动中的匹配表定义:
static const struct of_device_id imx_uart_dt_ids[] = {
{ .compatible = "fsl,imx6q-uart", .data = &imx6q_uart_data },
{ .compatible = "ns16550", .data = &ns16550_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
匹配优先级规则:
- 内核按列表顺序依次尝试匹配,第一个匹配成功的驱动被使用。
- 通常将最具体的兼容字符串放在最前面,通用兼容字符串放在后面。
- 驱动可以通过
of_match_device()获取匹配到的of_device_id中的私有数据。
"fsl, imx6q-uart" 是错误的写法,会导致匹配失败。
3.3 reg 属性:地址空间描述
reg 属性用于描述设备在总线上的地址范围,由一组或多组 (地址,长度) 对组成。其格式由父节点的 #address-cells 和 #size-cells 决定。
| 父节点属性 | 含义 | 常见值 |
|---|---|---|
#address-cells |
地址字段占用的 32 位单元数 | 1(32位地址)或 2(64位地址) |
#size-cells |
长度字段占用的 32 位单元数 | 1(32位长度)或 0(无长度) |
// 32位地址空间示例
soc {
#address-cells = <1>;
#size-cells = <1>;
ethernet@20000000 {
reg = <0x20000000 0x10000>; // 起始地址0x20000000,长度0x10000
};
};
// 64位地址空间示例
pcie@40000000 {
#address-cells = <3>;
#size-cells = <2>;
reg = <0x00 0x40000000 0x00 0x10000000>; // 高32位地址,低32位地址,高32位长度,低32位长度
};
内核通过 of_iomap() 或 platform_get_resource() 获取 reg 属性中的地址信息:
struct resource *res;
void __iomem *base;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
3.4 interrupts 属性:中断资源绑定
interrupts 属性描述设备使用的中断资源,其格式依赖于 中断控制器 的 #interrupt-cells 属性。常见的中断类型包括 SPI(共享外设中断)和 PPI(私有外设中断)。
// GIC(通用中断控制器)中断描述示例
gic: interrupt-controller@00a00000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
};
device@30000000 {
interrupts = <0 45 4>; // 0=SPI, 45=中断号, 4=高电平触发
};
中断触发类型编码:
| 编码值 | 触发类型 | 说明 |
|---|---|---|
| 0 | IRQ_TYPE_NONE | 未指定 |
| 1 | IRQ_TYPE_EDGE_RISING | 上升沿触发 |
| 2 | IRQ_TYPE_EDGE_FALLING | 下降沿触发 |
| 3 | IRQ_TYPE_EDGE_BOTH | 双边沿触发 |
| 4 | IRQ_TYPE_LEVEL_HIGH | 高电平触发 |
| 8 | IRQ_TYPE_LEVEL_LOW | 低电平触发 |
cat /proc/interrupts 可以查看系统中所有中断的分配情况。如果设备树中中断号配置错误,驱动将无法注册中断处理函数。
3.5 status 属性:设备状态控制
status 属性用于控制设备节点的启用或禁用状态,在板级设备树中经常用于覆盖 SoC 级设备树的默认配置。
| 状态值 | 含义 | 使用场景 |
|---|---|---|
"okay" |
设备启用 | 明确启用某个外设 |
"disabled" |
设备禁用 | 默认关闭,板级文件按需开启 |
"reserved" |
保留状态 | 由固件或安全世界使用 |
"fail" |
设备故障 | 检测到硬件错误时标记 |
// SoC 级设备树:默认禁用
i2c1: i2c@02180000 {
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
reg = <0x02180000 0x4000>;
status = "disabled";
};
// 板级设备树:启用并配置
&i2c1 {
status = "okay";
clock-frequency = <100000>;
pinctrl-0 = <&pinctrl_i2c1>;
};
内核在遍历设备树时,会自动跳过 status 为 "disabled" 的节点,不会为其创建 platform 设备。
3.6 clocks 属性:时钟依赖管理
clocks 属性通过 phandle + 时钟索引 的方式,将设备与其所需的时钟源关联起来。配合 clock-names 属性,驱动可以通过名称而非索引来获取时钟。
// 时钟控制器定义
clks: clock-controller@020c4000 {
compatible = "fsl,imx6q-ccm";
#clock-cells = <1>;
};
// 使用时钟的设备节点
uart1: serial@02020000 {
compatible = "fsl,imx6q-uart";
reg = <0x02020000 0x4000>;
interrupts = <0 26 4>;
clocks = <&clks IMX6Q_CLK_UART_IPG>,
<&clks IMX6Q_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
};
驱动中获取时钟的典型代码:
struct clk *ipg_clk, *per_clk;
ipg_clk = devm_clk_get(&pdev->dev, "ipg");
per_clk = devm_clk_get(&pdev->dev, "per");
clk_prepare_enable(ipg_clk);
clk_prepare_enable(per_clk);
clock-names 属性为每个时钟指定有意义的名称,这可以避免因时钟索引顺序变化导致的驱动兼容性问题。
3.7 gpios 属性:GPIO 引脚声明
gpios 属性用于声明设备使用的 GPIO 引脚,格式为 phandle + GPIO 编号 + 标志位。与时钟类似,gpio-names 属性可以为每个 GPIO 提供语义化名称。
// GPIO 控制器定义
gpio1: gpio@0209c000 {
compatible = "fsl,imx6q-gpio";
gpio-controller;
#gpio-cells = <2>;
};
// 使用 GPIO 的设备节点
leds {
compatible = "gpio-leds";
led-red {
label = "red";
gpios = <&gpio1 15 GPIO_ACTIVE_LOW>;
linux,default-trigger = "heartbeat";
};
};
GPIO 标志位说明:
| 标志位宏 | 值 | 含义 |
|---|---|---|
GPIO_ACTIVE_HIGH |
0 | 高电平有效 |
GPIO_ACTIVE_LOW |
1 | 低电平有效 |
GPIO_OPEN_DRAIN |
2 | 开漏输出 |
GPIO_OPEN_SOURCE |
4 | 开源输出 |
驱动中获取 GPIO 的接口:
int gpio_pin;
enum of_gpio_flags flags;
gpio_pin = of_get_named_gpio_flags(np, "gpios", 0, &flags);
if (gpio_pin < 0) {
dev_err(dev, "Failed to get GPIO\n");
return gpio_pin;
}
gpio_direction_output(gpio_pin, !(flags & OF_GPIO_ACTIVE_LOW));
3.8 属性关联关系与数据流
下图展示了设备树属性与内核驱动之间的数据流动关系:
3.9 综合调试案例:UART 设备树属性检查
以下是一个完整的 UART 设备树节点及其对应的驱动调试检查清单:
// 完整 UART 设备树节点
uart2: serial@021e8000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x021e8000 0x4000>;
interrupts = <0 27 4>;
clocks = <&clks IMX6Q_CLK_UART_IPG>,
<&clks IMX6Q_CLK_UART_SERIAL>;
clock-names = "ipg", "per";
dmas = <&sdma 25 4 0>, <&sdma 26 4 0>;
dma-names = "rx", "tx";
status = "okay";
};
调试检查清单:
- compatible 匹配检查:使用
cat /sys/devices/platform/serial@021e8000/of_node/compatible确认字符串与驱动匹配表一致。 - 地址范围检查:通过
cat /proc/iomem | grep 021e8000确认寄存器地址已正确映射。 - 中断分配检查:使用
cat /proc/interrupts | grep uart查看中断号是否与设备树一致。 - 时钟状态检查:通过
clk_summary调试文件(需开启CONFIG_DEBUG_FS)查看 UART 时钟是否已启用。 - 状态确认:检查
/sys/devices/platform/下是否存在对应的设备目录,若不存在则可能是status为"disabled"。
dt-validate 工具对 DTS 文件进行语法校验,再通过 fdtdump 查看编译后的 DTB 内容,确保属性值正确无误。