设备树节点精讲:compatible / reg / 中断属性配置
2026/7/21 23:36:11 网站建设 项目流程

设备树节点精讲:compatible / reg / 中断属性配置

作者:黒漂技术佬 | 系列:Linux内核配置与移植


引言

上一篇把设备树的来龙去脉讲清楚了,这一篇我们深入到节点内部,来看看设备树到底是怎么"描述"一个硬件的。设备树看起来像 JSON,但它的语义远比 JSON 更严格——每一个属性名都有固定的含义,写错一个字,驱动就找不到设备。本文把最核心的三个属性掰开揉碎来讲:compatible(谁是谁)、reg(在哪)、interrupts(怎么通知)。


一、设备树节点基本结构

一个完整的设备树节点长这样:

node-name@unit-address { property-name = <value>; compatible = "vendor,device"; reg = <address size>; status = "okay"; sub-node { /* 子节点 */ }; };

节点命名规则

  • node-name:节点类型名称(如uarti2cgpio
  • @unit-address:可选的地址后缀,值需与reg属性中的首地址一致
  • &label:引用其他节点的标签

属性值的四种基本格式

格式示例说明
整数数组< >reg = <0x1000 0x100>;32位大端整数序列
字符串" "compatible = "arm,pl011";普通字符串
字节数组[ ]mac-address = [00 11 22 33 44 55];原始十六进制字节
组合引用<& >clocks = <&clkc 24>;引用其他节点 phandle

二、compatible 属性——驱动的"身份证"

2.1 定义与格式

compatible是设备树中最重要的属性,没有之一。它用一个字符串列表告诉内核"这个设备是什么",驱动通过它来匹配设备。

uart0: serial@101f1000 { compatible = "arm,pl011", "arm,primecell"; reg = <0x101f1000 0x1000>; };

格式规范"manufacturer,model"

  • 第一部分是厂商名(arm、ti、nxp、st 等)
  • 第二部分是设备型号(pl011、omap-uart、stm32-usart 等)
  • 可以用多个字符串,从左到右匹配,越具体的放前面
  • 如果第一个匹配不到,内核会尝试下一个

2.2 驱动侧如何匹配

在驱动代码里,compatible对应这样的匹配表:

// drivers/tty/serial/amba-pl011.cstaticconststructof_device_idsbsa_uart_of_match[]={{.compatible="arm,sbsa-uart"},{.compatible="arm,pl011"},{/* sentinel */},};MODULE_DEVICE_TABLE(of,sbsa_uart_of_match);staticstructplatform_driverpl011_driver={.driver={.name="uart-pl011",.of_match_table=sbsa_uart_of_match,// ← 在这里匹配 DTS 中的 compatible},};

匹配流程

DTS: compatible = "ti,am3352-uart", "ns16550a"; ↓ 内核: of_match_table 逐个对比 "ti,am3352-uart" 匹配? → YES → 绑定该驱动 "ns16550a" 匹配? → (不再尝试)

2.3 常见 compatible 示例

设备类型compatible 字符串示例
ARM GIC 中断控制器"arm,gic-400"
PL011 串口"arm,pl011"
STM32 USART"st,stm32-usart"
I2C 控制器"nxp,lpc1788-i2c"
GPIO 控制器"ti,omap4-gpio"
以太网卡"smsc,lan9115"
温度传感器"nxp,lm75a"
SD卡控制器"rockchip,rk3288-dw-mshc"

三、reg 属性——寄存器的"门牌号"

3.1 基本格式

reg描述了设备的寄存器或内存地址范围:

reg = <起始地址1 大小1 起始地址2 大小2 ...>;

格式为成对的(address, length)。具体的含义由父节点的#address-cells#size-cells决定。

3.2 address-cells 和 size-cells

这两个属性定义在父节点上,告诉解析器reg中一个地址或长度占几个 32 位字:

soc { #address-cells = <1>; /* 地址用1个32位数表示 */ #size-cells = <1>; /* 大小用1个32位数表示 */ uart0: serial@40002000 { compatible = "arm,pl011"; reg = <0x40002000 0x1000>; /* ↑ 地址 ↑ 大小 (4KB) */ }; };

各种组合示例

#address-cells#size-cellsreg 示例适用场景
11<0x4000 0x100>32位处理器外设(最常见)
10<0x123>只有地址,没有大小(如 I2C 设备)
21<0x0 0x4000 0x100>64位地址空间
22<0x0 0x4000 0x0 0x100>64位地址 + 64位大小

3.3 不同类型总线上的 reg

内存映射外设(MMIO)

&amba { #address-cells = <1>; #size-cells = <1>; uart@101f1000 { reg = <0x101f1000 0x1000>; /* 基地址 + 寄存器范围 */ }; };

I2C 从设备

&i2c0 { #address-cells = <1>; #size-cells = <0>; /* I2C 设备没有地址空间概念 */ eeprom@50 { /* @50 表示 I2C 从设备地址 0x50 */ compatible = "atmel,24c02"; reg = <0x50>; /* 这里的 reg 是 I2C 地址,不是 MMIO */ }; };

SPI 从设备

&spi0 { #address-cells = <1>; #size-cells = <0>; flash@0 { /* @0 表示 SPI 片选 CS0 */ compatible = "spi-nand"; reg = <0>; /* 这里的 reg 是片选编号 */ }; };

四、interrupts 属性——硬件如何"喊你"

4.1 基本格式

interrupts描述了设备使用的中断信息:

uart@40002000 { compatible = "arm,pl011"; reg = <0x40002000 0x1000>; interrupts = <0 12 4>; };

这里<0 12 4>的含义取决于中断控制器的绑定规范。对于 ARM GIC 中断控制器:

字段含义值 0 12 4 的解释
第1个值中断类型0 = SPI(共享外设中断)
第2个值中断号12 = 第12号中断
第3个值触发方式4 = 高电平触发

GIC 中断触发方式编码

IRQ_TYPE_NONE = 0 // 未定义 IRQ_TYPE_EDGE_RISING = 1 // 上升沿触发 IRQ_TYPE_EDGE_FALLING = 2 // 下降沿触发 IRQ_TYPE_EDGE_BOTH = 3 // 双边沿触发 IRQ_TYPE_LEVEL_HIGH = 4 // 高电平触发 IRQ_TYPE_LEVEL_LOW = 8 // 低电平触发

4.2 interrupt-parent 和 interrupts-extended

当设备的中断控制器不是它的直接父节点时,需要显式指定:

/* 方式一:单独指定 interrupt-parent */ i2c@40005000 { compatible = "vendor,i2c"; reg = <0x40005000 0x1000>; interrupt-parent = <&intc>; /* 指定中断控制器 */ interrupts = <0 21 4>; /* SPI 21, 高电平 */ }; /* 方式二:interrupts-extended 一步到位 */ i2c@40005000 { compatible = "vendor,i2c"; reg = <0x40005000 0x1000>; interrupts-extended = <&intc 0 21 4>; /* 一行搞定 */ };

4.3 中断控制器的固定定义(interrupt-controller)

中断控制器自身在设备树中也有特定的声明:

intc: interrupt-controller@1e001000 { compatible = "arm,cortex-a9-gic"; reg = <0x1e001000 0x1000>, <0x1e000100 0x100>; #interrupt-cells = <3>; /* interrupts 属性每个单元有3个数 */ interrupt-controller; /* 宣告:我是中断控制器 */ };
  • interrupt-controller:声明这是一个中断控制器(空属性,有就表示是)
  • #interrupt-cells:声明引用该控制器时,interrupts属性中每个中断用几个整数来描述(GIC 是 3)

五、其他常用属性速查

属性作用示例
reg-names给 reg 中的地址区域起名字reg-names = "base", "alias";
clocks指定设备使用的时钟源clocks = <&clkc 15>;
clock-names给时钟起名字clock-names = "apb_pclk";
dmas指定 DMA 通道dmas = <&dmac 0 2>;
pinctrl-0引脚复用配置pinctrl-0 = <&uart0_pins>;
gpiosGPIO 引脚引用gpios = <&gpio0 5 0>;
status设备状态"okay"/"disabled"/"reserved"
phandle节点唯一标识(通常自动生成)DTC 自动为有 label 的节点生成

status 的取值含义

含义
"okay"设备可用,正常初始化
"disabled"设备不可用(硬件存在但软件不用)
"reserved"设备预留(驱动不配置,但不要更改寄存器)
"fail"设备探测失败

六、实操:写一个完整的设备节点

综合以上知识,下面是 STM32 板卡上一个温度传感器的完整设备树节点:

&i2c1 { #address-cells = <1>; #size-cells = <0>; status = "okay"; lm75: temperature-sensor@48 { compatible = "nxp,lm75a"; reg = <0x48>; /* I2C 从设备地址 */ status = "okay"; interrupts-extended = <&gpiof 3 IRQ_TYPE_EDGE_FALLING>; /* ↑PF3引脚 下降沿触发 */ #thermal-sensor-cells = <0>; pinctrl-0 = <&lm75_int_pin>; /* 引脚复用 */ }; };

逐行解读

  1. &i2c1—— 引用 SoC 级别定义好的 I2C1 控制器
  2. #address-cells = <1>—— I2C 设备地址用一个整数
  3. #size-cells = <0>—— I2C 没有地址空间
  4. @48—— 设备 I2C 地址是 0x48(传感器硬连接的地址脚决定)
  5. compatible = "nxp,lm75a"—— 匹配内核的 lm75 驱动
  6. reg = <0x48>—— 确认 I2C 地址(要和 @ 后面的值一致)
  7. interrupts-extended—— 温度超限时通过 PF3 引脚触发中断
  8. pinctrl-0—— 指定 PF3 的引脚复用功能

总结

设备树的三个核心属性记住这个类比:compatible 是身份证(告诉内核"我是谁"),reg 是门牌号(告诉内核"我在哪"),interrupts 是电话号码(告诉内核"怎么找我")。掌握了这三个,再加上#address-cells#size-cells这对规则说明书,你就能读写 80% 的设备树节点了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询