4、设备树绑定文档:如何阅读内核Documentation/devicetree/bindings/
设备树绑定文档,说白了就是硬件描述语言的“说明书”。
我刚入行那会儿,总觉得设备树就是一堆dts文件,改改reg属性、加个compatible就完事了。直到有一次,我调试一个I2C外设死活不工作,折腾了两天,最后发现是绑定时少了一个必需的属性。嗯,从那以后,我再也不敢跳过绑定文档了。
4.1 绑定文档长什么样?
打开内核源码,进入 Documentation/devicetree/bindings/ 目录。你会看到一堆子目录,按硬件类型分类:
arm/— ARM平台相关i2c/— I2C控制器和设备gpio/— GPIO控制器clock/— 时钟框架pinctrl/— 引脚控制regulator/— 电源管理sound/— 音频设备display/— 显示相关
每个子目录下,通常是一个或多个YAML文件(新格式)或者纯文本文件(老格式)。我个人习惯先看YAML格式的,结构更清晰。
核心要点:绑定文档定义了设备树节点的“语法规则”。你写的dts必须符合这个规则,内核才能正确解析。
4.2 YAML格式的绑定文档怎么读?
以 Documentation/devicetree/bindings/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
- const: fsl,imx1-i2c
- items:
- enum:
- fsl,imx6q-i2c
- fsl,imx7d-i2c
- const: fsl,imx21-i2c
reg:
maxItems: 1
interrupts:
maxItems: 1
clocks:
maxItems: 1
clock-names:
items:
- const: ipg
required:
- compatible
- reg
- interrupts
- clocks
additionalProperties: false
你看,结构非常清晰:
- title — 文档标题,告诉你这是啥设备
- maintainers — 维护者邮箱,有问题可以找他们
- properties — 所有允许的属性列表
- required — 必须提供的属性,少一个都不行
- additionalProperties: false — 不允许额外属性,写多了会报错
我的经验:看绑定文档时,先看 required 部分。把必须的属性配齐了,设备大概率能跑起来。optional的属性可以后面慢慢加。
4.3 老格式的纯文本绑定文档
有些老的绑定文档还是纯文本格式,比如 Documentation/devicetree/bindings/gpio/gpio-mxc.txt。这种文档结构松散一些,但核心信息一样:
Freescale i.MX GPIO controller
Required properties:
- compatible: Should be "fsl,imx35-gpio", "fsl,imx31-gpio", etc.
- reg: Address and length of the register set
- interrupts: Should contain the GPIO interrupt(s)
- gpio-controller: Marks the device node as a GPIO controller
- #gpio-cells: Should be 2
- interrupt-controller: Marks the device node as an interrupt controller
- #interrupt-cells: Should be 2
Optional properties:
- clocks: Clock specifier
- clock-names: Should be "gpio"
Example:
gpio1: gpio@73f84000 {
compatible = "fsl,imx35-gpio", "fsl,imx31-gpio";
reg = <0x73f84000 0x4000>;
interrupts = <0 43 0x04>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
};
读这种文档,我一般直接跳到 Example 部分。看一个完整的例子,比读十行文字描述都管用。
4.4 绑定文档里的“坑”
我曾经在一个项目里,把 compatible 字符串写反了顺序。文档里写的是:
compatible = "fsl,imx6q-i2c", "fsl,imx21-i2c";
我写成了:
compatible = "fsl,imx21-i2c", "fsl,imx6q-i2c";
结果内核匹配驱动时,先匹配了 imx21-i2c 的驱动,导致一些新特性没生效。嗯,顺序很重要——越具体的兼容性字符串放前面。
避坑指南:我曾经因为漏掉 #gpio-cells 属性,导致GPIO申请失败。绑定文档里写的 required 属性,一个都不能少。少了内核不会报编译错误,但运行时行为会异常。
4.5 如何快速定位绑定文档?
内核源码那么大,怎么找到某个设备的绑定文档?我一般用这几种方法:
- 按目录找 — 知道设备类型,直接进对应子目录
- grep大法 —
grep -r "compatible字符串" Documentation/devicetree/bindings/ - 看驱动源码 — 驱动文件里通常有
MODULE_DEVICE_TABLE,里面会引用绑定文档 - 用dt-schema工具 — 新内核支持
make dt_binding_check直接验证绑定文档
小技巧:我习惯在驱动目录下直接 grep -r "of_match_table",找到匹配表后,再反向搜索绑定文档。这样效率最高。
4.6 绑定文档与设备树的关系
我画了一张图,帮你理清它们之间的关系:
说白了,绑定文档就是设备树的“语法书”。你写dts的时候,得照着这本书来。内核启动时,驱动会通过 of_match_table 去匹配设备树节点,匹配的依据就是 compatible 属性。如果属性写错了,驱动根本认不出这个设备。
4.7 实战:如何验证你的设备树是否正确?
写完dts,别急着编译烧录。先用工具验证一下:
# 方法1:使用dt-validate工具
dt-validate -s Documentation/devicetree/bindings/ your.dts
# 方法2:内核自带的检查
make dt_binding_check
make dtbs_check
# 方法3:手动检查关键属性
dtc -I dts -O dtb -o temp.dtb your.dts
dtc -I dtb -O dts temp.dtb | grep -A 20 "your-device"
核心建议:每次修改设备树后,至少跑一遍 dtbs_check。它能帮你发现90%的语法错误和属性缺失问题。我曾经在一个项目中,就因为少写了一个 #clock-cells,导致整个时钟树解析失败,系统起不来。这种低级错误,工具一查就能发现。
4.8 总结一下
读绑定文档,其实就三步:
- 找到文档 — 按目录或grep
- 看required属性 — 先把必须的配齐
- 对照Example — 照猫画虎,别自己发明创造
嗯,就这么简单。但简单不代表不重要。我见过太多工程师,设备树写错了,然后花几天时间调试驱动,最后发现是设备树的问题。绑定文档就是你的“防呆设计”,多看两眼,能省下大把时间。
最后一个小建议:如果你用的是新版本内核(5.x以上),尽量看YAML格式的绑定文档。它结构清晰,而且支持自动化验证。老格式的txt文档虽然也能看,但容易遗漏信息。