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)
- ARM GIC:
- 触发类型: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 获取这些资源:
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/ 检查内核解析结果。