3. 设备树核心概念
设备树(Device Tree)的本质,是把“硬件长什么样、接在哪里、怎么被驱动识别”这件事,从内核代码里搬到一份可读、可改、可复用的数据结构中。上一节我们理解了 DTS/DTB 的编译与加载流程,这一节聚焦最常打交道的六个关键属性:compatible、reg、interrupts、clocks、gpios、pinctrl。它们分别回答“是什么设备”“占哪段地址”“怎么发中断”“时钟从哪来”“引脚怎么控制”“引脚复用成什么功能”。把这六个属性吃透,绝大多数外设 Bring-up 的节点编写就有了骨架。
一、六个属性的职责分工
设备树节点不是随便写属性,而是由内核的匹配与解析机制决定。驱动在注册时会声明自己支持的 compatible 列表,内核用它与节点中的 compatible 逐项比对,匹配成功才调用驱动的 probe。匹配之后,驱动通过 platform_get_resource、devm_ioremap_resource、platform_get_irq、devm_clk_get、devm_gpiod_get、pinctrl_select_state 等接口,把节点里的属性翻译成可操作的资源。因此,属性写错,往往不是“编译报错”,而是 probe 失败或功能静默异常。
二、compatible:设备与驱动的“身份证”
compatible 是设备树中最重要的属性,没有之一。它是一个字符串列表,格式通常为 "厂商前缀,型号",例如 "ti,omap4-i2c"、"fsl,imx6q-uart"。内核匹配时按列表顺序从前往后尝试,因此更具体的型号应放在前面,通用兼容项放在后面。例如:
uart1: serial@02020000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x02020000 0x4000>;
interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clks IMX6QDL_CLK_UART_IPG>,
<&clks IMX6QDL_CLK_UART_SERIAL>;
status = "okay";
};
使用场景上,compatible 决定了“谁来驱动这个设备”。如果写错厂商前缀或型号,驱动不会 probe,表现为设备节点存在但功能完全不可用。排查时可用 cat /proc/device-tree/.../compatible 或 of_node 相关调试接口确认实际值。
三、reg:地址与长度的描述
reg 描述设备占用的地址空间,格式为 <地址 长度> 对,可有多组。地址和长度的位宽由父节点的 #address-cells 和 #size-cells 决定。例如父节点 #address-cells = <1>、#size-cells = <1> 时,reg = <0x40012000 0x1000> 表示从 0x40012000 开始、长度 0x1000 的寄存器区间。对于 I2C/SPI 从设备,reg 往往表示片选号或从机地址,而不是物理内存地址,这一点极易混淆。
reg 含义的简单方法:看父节点的总线类型。挂在 simple-bus、platform 下的通常是 MMIO 地址;挂在 i2c、spi 下的通常是设备地址或片选号。四、interrupts:中断号与触发方式
interrupts 描述设备的中断连接,具体编码由中断控制器决定。以 ARM GIC 为例,常见格式为 <中断类型 中断号 触发方式>,其中 GIC_SPI 表示共享外设中断,GIC_PPI 表示私有外设中断;触发方式有 IRQ_TYPE_LEVEL_HIGH、IRQ_TYPE_EDGE_RISING 等。驱动侧通过 platform_get_irq() 获取虚拟中断号,再 request_irq() 注册处理函数。若中断一直不来,先确认触发方式是否与硬件一致,再确认 interrupt-parent 是否指向正确的中断控制器。
五、clocks、gpios、pinctrl:资源与引脚
clocks 引用时钟源,驱动用 devm_clk_get() 获取后 clk_prepare_enable() 使能。很多外设“寄存器能读但功能不动”,原因就是时钟没开。gpios 描述 GPIO 连接,格式为 <&gpio控制器 引脚号 有效电平>,常用于复位、使能、中断脚。注意 GPIO_ACTIVE_LOW 表示低电平有效,驱动调用 gpiod_set_value(desc, 1) 时会自动输出低电平,不要手动取反。pinctrl 则负责引脚复用与电气配置,通常成对出现 pinctrl-names 和 pinctrl-0,驱动在 probe 或运行时切换 default、sleep 等状态。
| 属性 | 核心含义 | 典型使用场景 | 常见错误 |
|---|---|---|---|
compatible | 设备与驱动的匹配标识 | 所有平台设备、I2C/SPI 从设备 | 厂商前缀或型号拼写错误 |
reg | 地址区间或设备地址 | MMIO 寄存器、I2C 从机地址 | 与父节点 cells 位宽不匹配 |
interrupts | 中断号与触发方式 | 按键、网卡、DMA 中断 | 触发方式与硬件相反 |
clocks | 时钟源引用 | UART、I2C、SPI 控制器 | 忘记使能时钟 |
gpios | GPIO 引脚与有效电平 | 复位脚、使能脚、中断脚 | 有效电平写反 |
pinctrl-0 | 引脚复用与电气配置 | I2C SCL/SDA、UART TX/RX | 引脚组引用错误或漏配 |
六、案例与复盘:一个 I2C 触摸屏节点
下面是一个触摸屏从设备的典型节点,六个属性几乎全部出现:
&i2c1 {
status = "okay";
clock-frequency = <400000>;
touch@38 {
compatible = "goodix,gt911";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
irq-gpios = <&gpio1 9 GPIO_ACTIVE_HIGH>;
pinctrl-names = "default";
pinctrl-0 = <>911_pins>;
status = "okay";
};
};
Bring-up 时若触摸无响应,可按以下顺序复盘:第一,确认 compatible 与驱动匹配表一致,dmesg | grep gt911 是否有 probe 日志;第二,确认 reg 地址与硬件原理图一致,I2C 地址常因 ADDR 引脚接法不同而不同;第三,确认 interrupts 的触发方式与触摸芯片 INT 引脚输出一致;第四,确认 reset-gpios 的有效电平,复位时序不对会导致芯片不启动;第五,确认 pinctrl-0 中 SCL/SDA 已复用为 I2C 功能,而不是普通 GPIO。
gpios 和 pinctrl 混为一谈:pinctrl 决定“这个引脚是什么功能”,gpios 决定“这个引脚作为 GPIO 时怎么用”。先复用,再控制,顺序不能反。总结来看,compatible 解决匹配,reg 解决寻址,interrupts 解决事件通知,clocks 解决时序基础,gpios 解决电平控制,pinctrl 解决引脚复用。六者各司其职,又通过内核的资源解析接口串联成完整的驱动 probe 链路。理解这条链路,比死记属性格式更重要。