4. 设备树绑定文档:如何阅读和编写设备树绑定文档,理解 compatible 字符串的匹配规则
设备树绑定文档(Device Tree Binding Documentation)是连接硬件描述与内核驱动之间的“契约”。它定义了设备树节点中必须包含哪些属性、属性值的合法范围,以及最重要的——compatible 字符串的匹配规则。在嵌入式 Linux 开发中,无论是移植新硬件还是编写设备驱动,正确理解绑定文档都是避免“设备无法识别”或“驱动加载失败”的关键前提。
Documentation/devicetree/bindings/ 目录下的 YAML 或文本文件。它告诉驱动开发者“设备树节点应该长什么样”,同时告诉设备树编写者“驱动期望看到哪些属性”。
4.1 绑定文档的结构与阅读方法
一个标准的设备树绑定文档通常包含以下五个部分。以 I2C 控制器绑定为例,其结构如下表所示:
| 章节 | 内容说明 | 示例(I2C 控制器) |
|---|---|---|
| 标题与描述 | 声明该绑定适用的硬件模块名称和功能概述 | NXP i.MX I2C Controller |
| compatible 字符串 | 列出所有合法的 compatible 值,通常按“厂商,型号”格式 | "fsl,imx6q-i2c", "fsl,imx21-i2c" |
| 属性列表 | 定义 required(必须)和 optional(可选)属性 | reg, interrupts, clocks |
| 子节点描述 | 说明子节点(如 I2C 从设备)的绑定规则 | 每个从设备必须有 reg 和 compatible |
| 示例 | 提供一个完整的设备树节点片段 | 展示 i2c@021a0000 节点的完整写法 |
4.2 compatible 字符串的匹配规则
compatible 字符串是设备树节点与驱动之间最核心的“握手协议”。内核通过 of_match_table 进行匹配,其规则遵循以下优先级:
- 精确匹配: 驱动中的 compatible 字符串与设备树节点中的第一个值完全一致时,立即匹配成功。
- 向后兼容匹配: 设备树节点可以列出多个 compatible 值(从最具体到最通用),内核会依次尝试匹配驱动表中的所有条目。
- 通配符不支持: 设备树绑定不识别
*或?等通配符,所有匹配都是字符串的精确比较。
下面这张 SVG 图展示了 compatible 字符串从设备树节点到驱动匹配的完整流程:
4.3 编写绑定文档的规范与步骤
当你需要为新的硬件模块编写绑定文档时(例如一个自定义的 FPGA 外设),请遵循以下步骤:
- 步骤 1:确定 compatible 字符串格式 —— 使用
"厂商名,设备型号"格式,例如"acme,my-fpga-ctrl"。厂商名必须是全小写,设备型号使用短横线连接。 - 步骤 2:列出 required 属性 —— 至少包含
compatible和reg(如果设备有 MMIO 地址空间)。 - 步骤 3:定义 optional 属性 —— 如
interrupts、clocks、dmas等,并说明每个属性的类型和约束。 - 步骤 4:描述子节点规则(如有) —— 例如总线控制器下的子节点必须包含
reg和compatible。 - 步骤 5:提供完整示例 —— 示例节点必须包含所有 required 属性,并注释说明每个字段的含义。
"my-device")。缺少厂商前缀会导致与其他驱动冲突。内核社区要求所有新的 compatible 字符串必须经过 Documentation/devicetree/bindings/vendor-prefixes.yaml 中的厂商前缀注册。
4.4 驱动中的匹配实现
在 Linux 驱动代码中,compatible 字符串的匹配通过 of_device_id 结构体数组实现。下面是一个典型的驱动匹配表:
static const struct of_device_id my_driver_of_match[] = {
{ .compatible = "acme,my-fpga-ctrl-v2", .data = &v2_config },
{ .compatible = "acme,my-fpga-ctrl-v1", .data = &v1_config },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_driver_of_match);
当设备树节点包含 compatible = "acme,my-fpga-ctrl-v2", "acme,my-fpga-ctrl-v1" 时,内核会先尝试匹配 v2,匹配成功后直接返回,不会继续检查 v1。这种机制允许新版本的驱动优先处理新硬件,同时保持对旧硬件的向后兼容。
4.5 绑定文档的 YAML 格式(现代标准)
从 Linux 内核 5.x 开始,新的绑定文档推荐使用 YAML 格式(基于 json-schema)。相比传统的纯文本格式,YAML 格式具有自动校验能力。下面是一个简化的 YAML 绑定示例:
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: ACME FPGA Controller
maintainers:
- Jane Doe <jane.doe@acme.com>
properties:
compatible:
items:
- enum:
- acme,my-fpga-ctrl-v2
- acme,my-fpga-ctrl-v1
reg:
maxItems: 1
interrupts:
maxItems: 1
required:
- compatible
- reg
additionalProperties: false
examples:
- |
fpga@40000000 {
compatible = "acme,my-fpga-ctrl-v2", "acme,my-fpga-ctrl-v1";
reg = <0x40000000 0x1000>;
interrupts = <0 42 4>;
};
dt-validate 工具自动检查绑定文档的合规性。如果节点缺少 required 属性或 compatible 值不在枚举列表中,编译会直接报错。这大大减少了运行时“设备不工作”的调试时间。
4.6 覆盖场景下的绑定注意事项
在使用设备树覆盖(Device Tree Overlay)时,绑定文档的规则同样适用,但需要额外关注以下几点:
| 场景 | 绑定要求 | 常见问题 |
|---|---|---|
| 覆盖新增节点 | 必须包含完整的 required 属性,compatible 必须与目标驱动匹配 | 遗漏 reg 属性导致驱动 probe 失败 |
| 覆盖修改节点 | 只能修改 optional 属性,不能修改 compatible 字符串 | 修改 compatible 后驱动无法重新匹配 |
| 覆盖删除节点 | 不需要绑定文档,但需确保删除操作不会破坏父节点约束 | 删除子节点后父节点仍引用已删除的资源 |
| 覆盖引用 phandle | 引用的节点必须已在基树中定义,且 compatible 匹配 | phandle 指向不存在的节点导致覆盖加载失败 |
下面这张 SVG 图展示了覆盖场景下绑定文档与设备树节点、驱动之间的三角关系:
4.7 实战:排查 compatible 不匹配问题
当设备无法被驱动识别时,可以按照以下步骤快速定位:
- 检查设备树节点: 使用
dtc -I dtb -O dts反编译 DTB,确认节点中的 compatible 字符串是否与预期一致。 - 检查驱动匹配表: 在内核源码中搜索
of_match_table,确认驱动声明的 compatible 值是否与设备树节点中的第一个值完全一致。 - 检查绑定文档: 确认绑定文档中列出的 compatible 值是否包含你使用的字符串。如果使用 YAML 绑定,运行
dt-validate检查节点合法性。 - 检查内核日志: 使用
dmesg | grep -i "of_match\|compatible\|probe"查看内核是否打印了匹配失败的信息。
cat /proc/device-tree/<设备路径>/compatible 可以直接读取设备树节点中的 compatible 值,与驱动源码中的字符串进行逐字符对比(注意不可见字符和空格)。