4、设备树绑定文档:如何阅读内核Documentation/devicetree/bindings、常见外设绑定规范。
设备树绑定文档,说白了就是芯片厂商写给驱动开发者的“说明书”。
我刚入行那会儿,总觉得设备树就是一堆看不懂的属性和数字。直到有一次调一个I2C触摸屏,死活唤不醒,折腾了两天。后来一个老同事过来看了一眼,说:“你查过绑定文档吗?”我这才去翻 Documentation/devicetree/bindings 目录。结果发现,有个 wakeup-source 属性我没配。加上去,问题秒解。
从那以后,我养成了一个习惯:写驱动之前,先读绑定文档。今天我就带你系统地过一遍,怎么高效地读这些文档,以及常见外设的绑定规范长什么样。
4.1 绑定文档的目录结构
内核源码树下的 Documentation/devicetree/bindings/ 目录,是按外设类型分门别类整理的。你想想看,一个成熟的嵌入式Linux系统,可能涉及几十种外设,如果没有清晰的目录结构,找起来会非常痛苦。
我习惯先看顶层目录,快速定位目标外设的类型。下面这张表列出了最常用的几个子目录:
| 子目录 | 包含的外设类型 | 典型文件举例 |
|---|---|---|
arm/ |
ARM架构相关的SoC和板级绑定 | arm,vexpress.yaml |
clock/ |
时钟控制器、PLL、时钟源 | clk-imx.yaml |
gpio/ |
GPIO控制器 | gpio-controller.yaml |
i2c/ |
I2C控制器及I2C设备 | i2c-imx.yaml |
pinctrl/ |
引脚复用和引脚配置 | pinctrl-bindings.txt |
regulator/ |
电压调节器 | regulator.yaml |
serial/ |
UART串口控制器 | fsl-imx-uart.yaml |
spi/ |
SPI控制器及SPI设备 | spi-controller.yaml |
display/ |
显示接口、LCD控制器、HDMI | panel-simple.yaml |
sound/ |
音频编解码器、DAI链路 | simple-audio-card.yaml |
grep -r "compatible" arch/arm/boot/dts/ 找到所有用到的 compatible 字符串,然后去 bindings 目录下反向查找对应的文档。这样比漫无目的地翻目录快得多。
4.2 如何阅读一个绑定文档
现在的绑定文档,大部分已经迁移到了 YAML 格式(以 .yaml 结尾)。相比以前的纯文本格式,YAML 更结构化,也更容易被工具校验。但不管什么格式,核心内容都围绕这几个问题:
- 这个设备是什么? —— 通过
compatible属性来匹配驱动。 - 它需要哪些资源? —— 寄存器地址、中断号、时钟、DMA通道等。
- 它有哪些可选功能? —— 比如是否支持唤醒、是否使能某个特性。
- 子节点怎么配置? —— 对于I2C/SPI总线控制器,子节点代表挂载的具体设备。
我拿一个实际的 I2C 控制器绑定文档片段来举例。假设我们看 i2c-imx.yaml:
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/i2c/i2c-imx.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale i.MX I2C Controller
maintainers:
- Oleksij Rempel <o.rempel@pengutronix.de>
properties:
compatible:
oneOf:
- items:
- enum:
- fsl,imx1-i2c
- fsl,imx21-i2c
- fsl,imx6q-i2c
- items:
- const: fsl,imx6ul-i2c
- const: fsl,imx6q-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-frequency:
description: I2C bus clock frequency in Hz
default: 100000
enum: [100000, 400000]
required:
- compatible
- reg
- interrupts
- clocks
你看,这个文档清晰地告诉我们:
compatible可以写fsl,imx6ul-i2c,后面再跟一个fsl,imx6q-i2c作为 fallback。reg和interrupts是必填项,各一个。clock-frequency是可选的,默认 100KHz,可以配成 400KHz。
spi-max-frequency,结果 SPI 控制器死活不工作。后来查绑定文档才发现,那个控制器要求频率必须写在子节点的 spi-max-frequency 属性里,而不是控制器节点里。文档里写的是 maxItems: 1,但我没注意看描述。所以,一定要看属性的描述和约束条件,不要想当然。
4.3 常见外设绑定规范速览
下面我挑几个最常用的外设,把它们的绑定要点列出来。这些是我在实际项目中反复用到的。
4.3.1 GPIO 控制器
GPIO 控制器的绑定,核心是定义 gpio-controller 和 #gpio-cells。
gpio0: gpio@2090000 {
compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
reg = <0x0209c000 0x4000>;
interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
<GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
这里 #gpio-cells = <2> 表示引用这个 GPIO 时,需要两个参数:GPIO 编号和标志位(比如高电平有效还是低电平有效)。
4.3.2 I2C 设备
I2C 子节点的绑定,通常需要 compatible、reg(I2C 地址),以及一些厂商自定义的属性。
&i2c1 {
clock-frequency = <400000>;
status = "okay";
touch: touchscreen@38 {
compatible = "edt,edt-ft5406";
reg = <0x38>;
interrupt-parent = <&gpio1>;
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
wakeup-source;
};
};
注意那个 wakeup-source,就是我在开头提到的那个属性。没有它,触摸屏在系统休眠后无法唤醒。
4.3.3 引脚复用 (pinctrl)
引脚复用是设备树里最繁琐的部分之一。绑定文档通常会定义一组 pinctrl-<n> 属性,每个属性指向一个引脚配置节点。
&iomuxc {
pinctrl_uart1: uart1grp {
fsl,pins = <
MX6UL_PAD_UART1_TX_DATA__UART1_DCE_TX 0x1b0b1
MX6UL_PAD_UART1_RX_DATA__UART1_DCE_RX 0x1b0b1
>;
};
};
&uart1 {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_uart1>;
status = "okay";
};
这里的 0x1b0b1 是引脚配置的 pad 控制值,包括上下拉、驱动能力、速度等。这些值怎么算?嗯,你得去看芯片的参考手册,绑定文档里一般不会写那么细。
4.4 绑定文档的“潜规则”
除了文档里明写的,还有一些不成文的规则。我总结了几条:
- compatible 字符串的命名: 一般是
厂商,设备型号,比如fsl,imx6ul-uart。如果设备兼容某个更通用的型号,可以追加一个 fallback,比如"fsl,imx6ul-uart", "fsl,imx6q-uart"。 - reg 属性的顺序: 通常第一个是寄存器基地址和长度,第二个可能是某些特殊功能区的地址。具体顺序要看文档。
- status 属性: 默认是
"disabled",需要显式设为"okay"才能启用。我见过有人忘了设这个,查了半天。 - 中断类型: 尽量用
IRQ_TYPE_LEVEL_HIGH或IRQ_TYPE_EDGE_RISING这样的宏,不要直接写数字。可读性差,而且容易写错。
4.5 知识结构图:绑定文档阅读流程
下面这张图,是我自己总结的阅读绑定文档的流程。每次拿到一个新外设,我都会按这个步骤走一遍。
这个流程看起来简单,但每一步都有细节。比如第一步“定位文档”,如果你不知道外设的 compatible 字符串,可以先去内核源码里搜 of_match_table,找到驱动对应的 compatible,再反向查文档。
4.6 避坑指南
最后,分享几个我亲身经历的教训:
- 不要完全相信示例代码: 绑定文档里的示例,有时候是简化版,或者来自老版本内核。我遇到过示例里用了
interrupts = <0 66 4>,但实际内核已经改用GIC_SPI宏了。所以,示例只能参考,最终要以文档的properties定义为准。 - 注意 YAML 的缩进: YAML 对缩进极其敏感。我见过有人把
required列表缩进错了,导致整个绑定校验失败。如果你手写设备树,建议用dt-validate工具检查一下。 - 多看看
maintainers字段: 如果文档里写了维护者的邮箱,遇到问题可以直接发邮件问。我试过几次,大部分维护者都很乐意解答。当然,前提是你已经仔细读过文档了。
Documentation/devicetree/bindings/ 目录下,用 grep -r "pattern" *.yaml 可以快速搜索某个属性或 compatible 字符串。比如我想查所有支持 wakeup-source 的设备,直接 grep -r "wakeup-source" *.yaml 就行。
好了,关于绑定文档的阅读方法,我就讲到这里。记住,文档是死的,但硬件是活的。遇到问题,多查文档,多动手验证,慢慢你就能形成自己的“套路”。