4. 设备树绑定文档

设备树绑定文档,说白了就是硬件描述与驱动代码之间的“契约”。

我刚开始接触设备树那会儿,总觉得这玩意儿就是个配置文件,随便写写就行。直到有一次,我写错了一个 interrupts 属性,结果中断一直触发不了,查了整整两天。嗯,从那以后,我再也不敢小看绑定文档了。

4.1 绑定文档是什么?为什么要读?

绑定文档(Binding Document)通常存放在内核源码的 Documentation/devicetree/bindings/ 目录下。每个硬件外设,比如 I2C 控制器、GPIO 控制器、时钟发生器,都有对应的 .yaml 或 .txt 文件。

它的作用很简单:告诉你这个设备在设备树里该怎么写。

  • 必须写哪些属性(required properties)
  • 可选写哪些属性(optional properties)
  • 每个属性的格式和含义
  • 子节点怎么组织

我个人习惯是:拿到一个新板子,先翻一遍绑定文档。这比看驱动代码快多了。

核心观点:绑定文档是硬件工程师和驱动工程师之间的“翻译手册”。读懂了它,你就能准确地把硬件手册里的寄存器地址、中断号、时钟频率,翻译成设备树里的属性值。

4.2 标准属性详解——你一定会用到的几个

设备树里有很多标准属性,几乎每个节点都会用到。下面这几个,是我在实际项目中打交道最多的。

4.2.1 compatible —— 驱动匹配的“身份证”

compatible 属性是设备树节点与驱动代码匹配的关键。它的格式是 "厂商,型号"。

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

这里有两个字符串。内核匹配驱动时,会按顺序尝试。第一个最精确,第二个是通用兼容。

我在项目中遇到过一个问题:某款芯片的 UART 控制器,厂商写的是 "ti,am335x-uart",但驱动里只注册了 "ns16550"。结果设备树匹配不上,串口死活不工作。后来加上通用兼容字符串就解决了。

小技巧:写 compatible 时,一定要把最精确的型号写在最前面。这样即使未来内核更新,也能优先匹配到专有驱动。

4.2.2 reg —— 地址和大小,硬件的“门牌号”

reg 属性描述设备的地址空间。它由一组 (地址, 长度) 对组成。

reg = <0x48020000 0x1000>;

这表示设备从 0x48020000 开始,占用 0x1000 字节的地址空间。

如果设备有多个地址段,可以写多组:

reg = <0x48020000 0x1000>, <0x48021000 0x100>;

为什么会这样?有些硬件有控制寄存器和数据缓冲区,它们物理地址不连续,但逻辑上属于同一个设备。

我曾经调试过一个网卡驱动,发现 DMA 传输总是失败。查了半天,原来是 reg 属性只写了控制寄存器地址,漏掉了 DMA 描述符的地址段。加上之后,一切正常。

注意:reg 属性的地址和长度,必须与硬件手册里的地址范围严格一致。多一个字节或少一个字节,都可能导致驱动访问到错误的硬件。

4.2.3 interrupts —— 中断的“接线图”

interrupts 属性描述设备使用的中断。它的格式取决于中断控制器。

对于 ARM GIC(通用中断控制器),常见格式是:

interrupts = <0 42 4>;

三个数字的含义:

  • 第一个:中断类型。0 表示 SPI(共享外设中断),1 表示 PPI(私有外设中断)。
  • 第二个:中断号。SPI 中断号从 32 开始算,所以 42 对应硬件中断号 74。
  • 第三个:触发方式。1 表示上升沿触发,4 表示高电平触发。

嗯,这里要注意:不同厂商的中断控制器,格式可能不一样。比如有些老式芯片用 <中断号 触发方式> 两段式。

我建议你写 interrupts 之前,先看看对应中断控制器的绑定文档。否则中断号写错了,驱动根本收不到中断。

4.2.4 其他常用标准属性

属性名 作用 示例
clocks 指定设备使用的时钟源 clocks = <&clk_uart 0>;
status 设备状态,okay 表示启用,disabled 表示禁用 status = "okay";
pinctrl-0 引脚复用配置 pinctrl-0 = <&uart0_pins>;
dmas DMA 通道配置 dmas = <&edma 0 2>;

4.3 如何快速阅读绑定文档?

绑定文档通常很长,但你不必从头读到尾。我总结了一个“三步法”:

  1. 先看 required properties —— 这些是必须写的,漏一个驱动就加载不了。
  2. 再看 example 部分 —— 文档末尾通常有一个完整的设备树节点示例,直接复制修改。
  3. 最后看 optional properties —— 按需添加,比如你要用 DMA 就查 dmas 属性。

举个例子,假设你要添加一个 I2C 设备。打开 i2c-controller.yaml,先看 required 里有没有 compatible、reg、interrupts。然后看 example 里怎么写地址和时钟频率。最后,如果你需要支持 400kHz 高速模式,就去 optional 里找 clock-frequency 属性。

我的习惯:把常用外设的绑定文档打印出来,贴在工位上。写设备树时扫一眼,比翻网页快多了。

4.4 避坑指南——我踩过的几个坑

这些年读绑定文档,我踩过不少坑。分享几个典型的:

  • 中断号算错:ARM GIC 的中断号从 32 开始,但有些文档直接写硬件中断号。我曾经把 SPI 中断号 74 写成了 74,结果应该是 42(74-32)。
  • reg 地址对齐:某些硬件要求 reg 的起始地址必须 4KB 对齐。我遇到过写 0x48020010,结果驱动访问时崩溃。
  • compatible 大小写:内核匹配是区分大小写的。我见过有人写 "Ti,am335x-uart",驱动里是 "ti,am335x-uart",匹配不上。

嗯,这些坑其实都可以避免。只要你养成一个习惯:写设备树之前,先打开绑定文档,逐条核对。

4.5 知识体系总览

下面这张图,帮你理清绑定文档的核心逻辑:

设备树绑定文档 文档结构 required 属性 optional 属性 标准属性 compatible reg interrupts 阅读方法 三步法 示例参考 常见避坑 中断号算错 reg 地址对齐 compatible 大小写

这张图把绑定文档的核心内容串起来了。你从中心出发,沿着箭头走一遍,就能掌握它的全貌。

好了,关于绑定文档的阅读方法和标准属性,就聊到这儿。记住:设备树是硬件与驱动的桥梁,而绑定文档就是这座桥梁的设计图纸。读懂了它,你写设备树时心里就有底了。