4. 设备树节点与属性:节点命名规范、compatible属性、reg属性、interrupts属性详解
设备树(Device Tree)是描述硬件拓扑结构的结构化数据,它通过节点(Node)和属性(Property)将外设的地址、中断、时钟、复位等资源信息传递给内核。驱动开发者必须准确理解节点命名规则以及 compatible、reg、interrupts 等核心属性的语义,才能正确完成外设内存映射与中断绑定。
4.1 节点命名规范
设备树中每个节点代表一个硬件设备或总线桥接器,其命名必须遵循以下规则:
- 通用格式:
node-name@unit-address - node-name:描述设备类型的通用名称,如
uart、i2c、gpio、ethernet。 - unit-address:设备在父总线上的地址,通常为寄存器基地址或片选编号,使用小写十六进制。
- 命名长度:建议不超过 31 个字符,避免使用下划线开头。
- 特殊节点:根节点
/、别名节点aliases、选择节点chosen为固定名称。
命名示例对比
| 正确命名 | 错误命名 | 说明 |
|---|---|---|
uart@ff010000 |
UART@ff010000 |
节点名应全小写 |
i2c@ff020000 |
i2c_controller@ff020000 |
避免下划线,用连字符 |
ethernet@ff030000 |
eth@ff030000 |
名称应体现设备类型 |
gpio@ff040000 |
gpio@0xff040000 |
unit-address 不带 0x 前缀 |
实战建议:在编写设备树时,优先参考内核文档
Documentation/devicetree/bindings/ 中对应设备的命名惯例,保持与主线内核一致,避免因命名不规范导致驱动匹配失败。
4.2 compatible 属性:驱动匹配的核心
compatible 属性是设备树与驱动代码之间的“握手协议”。内核通过该属性查找对应的 platform_driver,完成设备与驱动的绑定。
- 格式:字符串列表,推荐使用
"厂商,设备型号"形式。 - 匹配优先级:内核按列表顺序依次匹配,第一个匹配成功即绑定。
- 回退机制:通常包含一个精确型号和一个通用型号,例如
"ti,am3352-uart", "ns16550"。
compatible 属性示例
uart0: uart@ff010000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x00 ff010000 0x00 0x4000>;
interrupts = <0 58 4>;
};
上述代码中,内核优先匹配 "fsl,imx6q-uart",若未找到则尝试 "fsl,imx21-uart"。驱动端通过 of_match_table 声明支持的 compatible 列表。
| compatible 值 | 匹配驱动 | 说明 |
|---|---|---|
"fsl,imx6q-uart" |
imx6q-uart.c | 精确匹配 i.MX6Q 专用驱动 |
"fsl,imx21-uart" |
imx21-uart.c | 回退匹配通用 i.MX 驱动 |
"ns16550" |
8250.c | 回退匹配标准 16550 驱动 |
常见错误:compatible 字符串中厂商名和设备名之间必须使用逗号分隔,且不能包含空格。例如
"ti, am3352-uart" 是错误的,应写为 "ti,am3352-uart"。
4.3 reg 属性:外设内存映射的基石
reg 属性描述设备在系统总线上的地址空间,包括寄存器基地址和地址范围。内核通过该属性完成 IO 内存的映射,驱动才能通过 ioremap 访问硬件寄存器。
- 格式:
reg = <address1 length1 address2 length2 ...>; - 地址单元:由父节点的
#address-cells决定,通常为 1 或 2。 - 长度单元:由父节点的
#size-cells决定,通常为 1 或 2。 - 多段地址:一个设备可能包含多个地址区域,如寄存器区、FIFO 区、DMA 区。
reg 属性典型示例
soc {
#address-cells = <2>; // 地址使用 2 个 32 位单元(64 位地址)
#size-cells = <2>; // 长度使用 2 个 32 位单元(64 位长度)
uart@ff010000 {
compatible = "fsl,imx6q-uart";
reg = <0x00 0xff010000 0x00 0x4000>;
// 地址:0xff010000,长度:0x4000(16KB)
};
dma@ff020000 {
compatible = "fsl,imx6q-dma";
reg = <0x00 0xff020000 0x00 0x1000 // 控制寄存器
0x00 0xff030000 0x00 0x1000>; // 描述符区
};
};
| #address-cells | #size-cells | 地址表示范围 | 典型场景 |
|---|---|---|---|
| 1 | 1 | 32 位地址,32 位长度 | ARM 32 位 SoC |
| 2 | 2 | 64 位地址,64 位长度 | ARM64 / RISC-V 64 位 SoC |
| 1 | 0 | 32 位地址,无长度 | GPIO 引脚编号 |
实战要点:驱动中通过
platform_get_resource(pdev, IORESOURCE_MEM, 0) 获取第一个 reg 段的物理地址和长度,然后调用 devm_ioremap_resource 完成内存映射。多段地址时 index 参数依次递增。
4.4 interrupts 属性:中断绑定与触发类型
interrupts 属性描述设备的中断资源,包括中断号、中断类型和触发方式。内核通过该属性将设备的中断请求线与中断控制器关联起来。
- 格式:
interrupts = <interrupt-specifier>; - interrupt-specifier:由中断控制器的
#interrupt-cells决定,通常包含中断号、触发类型和优先级。 - 中断类型:常见值包括 IRQ_TYPE_EDGE_RISING (1)、IRQ_TYPE_EDGE_FALLING (2)、IRQ_TYPE_LEVEL_HIGH (4)、IRQ_TYPE_LEVEL_LOW (8)。
- 中断父节点:通过
interrupt-parent属性指定中断控制器,若不指定则继承父节点。
interrupts 属性示例
gic: interrupt-controller@ffa00000 {
compatible = "arm,gic-400";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x00 0xffa00000 0x00 0x10000>;
};
uart@ff010000 {
compatible = "fsl,imx6q-uart";
reg = <0x00 0xff010000 0x00 0x4000>;
interrupts = <0 58 4>;
// 0: SPI 中断类型(0=PPI, 1=SPI)
// 58: 中断号
// 4: 触发类型(高电平触发)
};
| #interrupt-cells | 中断说明符格式 | 典型中断控制器 |
|---|---|---|
| 1 | <中断号> | 简单中断控制器(如 GPIO) |
| 2 | <中断号 触发类型> | ARM GIC v2 |
| 3 | <中断类型 中断号 触发类型> | ARM GIC v3/v4 |
中断触发类型对照表
| 宏定义 | 数值 | 触发方式 | 适用场景 |
|---|---|---|---|
IRQ_TYPE_EDGE_RISING |
1 | 上升沿触发 | 按键、GPIO 边沿事件 |
IRQ_TYPE_EDGE_FALLING |
2 | 下降沿触发 | 下降沿敏感设备 |
IRQ_TYPE_LEVEL_HIGH |
4 | 高电平触发 | UART、I2C 等标准外设 |
IRQ_TYPE_LEVEL_LOW |
8 | 低电平触发 | 低电平有效设备 |
驱动获取中断资源:使用
platform_get_irq(pdev, 0) 获取第一个中断号,然后调用 devm_request_irq 注册中断处理函数。多中断时 index 参数递增。
4.5 综合实战:完整设备树节点示例
以下是一个完整的 UART 设备树节点,综合运用了本节所学的所有核心属性:
/ {
model = "Embedded Linux Board";
compatible = "vendor,board-rev1";
soc@0 {
#address-cells = <2>;
#size-cells = <2>;
compatible = "simple-bus";
ranges;
gic: interrupt-controller@ffa00000 {
compatible = "arm,gic-400";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x00 0xffa00000 0x00 0x10000>;
};
uart0: uart@ff010000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x00 0xff010000 0x00 0x4000>;
interrupts = <0 58 4>;
clocks = <&clk 20>;
clock-names = "uart";
status = "okay";
};
};
};
属性总结表
| 属性 | 作用 | 驱动获取方式 | 常见错误 |
|---|---|---|---|
compatible |
驱动匹配标识 | of_match_table |
厂商名与设备名间缺少逗号 |
reg |
物理地址与长度 | platform_get_resource |
address/size-cells 不匹配 |
interrupts |
中断号与触发类型 | platform_get_irq |
中断类型值与硬件不匹配 |
interrupt-parent |
指定中断控制器 | 自动继承 | 未指定导致中断无法路由 |
实战建议:在移植驱动时,先通过
cat /proc/device-tree/<设备路径>/compatible 确认设备树中的 compatible 值,再与驱动中的 of_match_table 对比,确保完全一致。使用 devicetree.org 的规范命名可提高代码的可移植性。