3、设备树规范:标准属性(compatible、reg、interrupts、status等)

设备树里那些标准属性,说白了就是内核和硬件之间的「通用接口协议」。你想想看,如果每个驱动都自己发明一套属性命名,那内核得乱成什么样?所以规范定了这么几个核心属性,大家统一遵守。我最早啃设备树的时候,就是被这几个属性绕晕过,后来发现其实就那么回事。

3.1 compatible —— 硬件身份证

这个属性最重要。它告诉内核:「我是谁,我能兼容谁」。格式很简单:"厂商,型号"。比如:

compatible = "ti,am335x-uart", "ns16550";

内核匹配驱动时,会从左往右试。第一个最精确,后面的作为备选。我在项目中遇到过一个问题:某款芯片的UART模块,厂商在设备树里只写了 "custom-uart",结果内核没有对应驱动,系统直接不认这个串口。后来加上 "ns16550" 作为兼容项,立马就好了。

我的习惯:写 compatible 时,永远把最精确的型号放第一位,后面跟一个通用的标准型号。这样既保证精确匹配,又留了后路。

3.2 reg —— 硬件地址空间

reg 描述的是设备在总线上的地址范围。格式是 reg = <地址 长度>。看个例子:

uart0: serial@44e09000 {
    compatible = "ti,am335x-uart", "ns16550";
    reg = <0x44e09000 0x2000>;
};

这里地址是 0x44e09000,长度 0x2000(8KB)。内核拿到这个信息后,就知道该把哪段物理地址映射到虚拟地址空间。

嗯,这里要注意:reg 里的地址是总线地址,不是 CPU 看到的物理地址。如果设备挂在 I2C 或 SPI 总线上,reg 表示的是从设备地址。比如:

eeprom@50 {
    compatible = "atmel,24c02";
    reg = <0x50>;
};

这里的 0x50 就是 I2C 从设备地址,没有长度字段。

我曾经踩过的坑:写 reg 时长度给错了。某次我把一个 4KB 的寄存器空间写成了 0x100(256字节),结果驱动访问到末尾地址时直接触发异常。调试了一下午才发现是长度不对。

3.3 interrupts —— 中断连接线

interrupts 属性描述设备的中断信息。它依赖 interrupt-parent 指定中断控制器。格式通常是:

interrupt-parent = &intc;
interrupts = <0 56 4>;

这三个数字的含义:

  • 第一个:中断类型(0=SPI,1=PPI)
  • 第二个:中断号(硬件中断号)
  • 第三个:触发方式(1=上升沿,2=下降沿,4=高电平,8=低电平)

你想想看,如果设备有多个中断怎么办?那就写多组:

interrupts = <0 56 4>, <0 57 4>;

内核会按顺序解析,驱动里通过 platform_get_irq() 依次获取。

3.4 status —— 设备开关

status 属性控制设备是否启用。取值就几个:

含义
"okay" 设备启用,正常工作
"disabled" 设备禁用,内核不初始化
"reserved" 保留,由固件使用
"fail" 设备检测失败

我习惯在开发板上把所有外设都设为 "okay",量产时再根据实际硬件裁剪。有一次客户说某个功能不工作,我远程一看,设备树里 status 写成了 "disable"(少了个 d),内核直接忽略了该设备。

核心要点:status 是设备树里最容易被忽略的属性。调试时如果设备不工作,先检查 status 是不是 "okay"。

3.5 其他常用标准属性

除了上面四个,还有几个我经常用的:

  • device_type:早期设备树用的多,现在基本被 compatible 取代。但有些总线(如 PCI)还在用。
  • model:描述板子型号,比如 model = "TI AM335x BeagleBone Black"
  • clock-frequency:直接指定时钟频率,常用于简单设备。
  • gpios:描述 GPIO 引脚,格式是 <控制器 引脚号 标志>

3.6 知识体系结构图

下面这张图把标准属性的关系理清楚了:

设备树标准属性 compatible 硬件身份证 "厂商,型号" reg 地址空间 <地址 长度> interrupts 中断配置 类型 号 触发 status 设备开关 okay/disabled 其他标准属性 device_type model clock-frequency gpios 内核通过标准属性解析硬件信息 驱动匹配 → 地址映射 → 中断注册 → 设备启用

3.7 实战中的组合使用

实际项目中,这些属性很少单独出现。我给你看一个完整的例子:

uart2: serial@48022000 {
    compatible = "ti,omap4-uart", "ns16550";
    reg = <0x48022000 0x400>;
    interrupts = <0 73 4>;
    status = "okay";
    clock-frequency = <48000000>;
};

这个节点描述了 OMAP4 上的 UART2:

  • 兼容 TI 自家驱动和标准 16550 驱动
  • 寄存器基址 0x48022000,大小 1KB
  • 使用 SPI 中断 73,高电平触发
  • 设备启用,时钟 48MHz

内核解析时,先看 status 决定是否处理,然后用 compatible 找驱动,reg 做 ioremap,interrupts 注册中断处理函数。一气呵成。

我的建议:写设备树时,先把这几个标准属性写全。哪怕有些属性暂时用不到,也留个注释。这样后续维护的人一看就明白硬件设计意图。

标准属性就是设备树的「语法基础」。搞懂了它们,你就能读懂大部分设备树文件了。剩下的就是各种 binding 文档里定义的厂商专用属性,那些都是基于这些标准属性扩展出来的。