4. 设备树绑定文档:内核Documentation/devicetree/bindings/目录结构、如何阅读与编写绑定文档

设备树绑定文档(Binding Documentation)是内核中描述设备树节点属性、兼容性、中断、时钟等硬件接口的规范文件。它们位于内核源码树的 Documentation/devicetree/bindings/ 目录下,是驱动开发者与设备树编写者之间的“契约”。本章将深入剖析该目录的结构、如何高效阅读绑定文档,以及如何编写符合内核社区规范的绑定文档。

4.1 绑定文档目录结构

内核的绑定文档按硬件类别和厂商组织,形成清晰的树状结构。理解这个结构能帮助你快速定位所需文档。

核心目录布局:
  • bindings/ 根目录:包含所有设备树绑定文档。
  • bindings/arm/:ARM 架构相关的 SoC 和板级绑定。
  • bindings/clock/:时钟控制器、时钟源绑定。
  • bindings/gpio/:GPIO 控制器绑定。
  • bindings/i2c/:I2C 控制器及从设备绑定。
  • bindings/interrupt-controller/:中断控制器绑定。
  • bindings/media/:多媒体设备(摄像头、显示等)绑定。
  • bindings/net/:网络设备(以太网、无线)绑定。
  • bindings/pinctrl/:引脚控制绑定。
  • bindings/regulator/:电压调节器绑定。
  • bindings/sound/:音频设备绑定。
  • bindings/spi/:SPI 控制器及从设备绑定。
  • bindings/usb/:USB 控制器绑定。
  • bindings/vendor-prefixes.yaml:厂商前缀列表(YAML 格式)。

每个子目录下通常包含 .txt 或 .yaml 文件。新内核(5.x 及以上)强烈推荐使用 .yaml 格式,因为它支持 schema 校验,能自动检查设备树节点的合法性。

绑定文档目录结构(Documentation/devicetree/bindings/) bindings/ arm/ clock/ gpio/ i2c/ interrupt-controller/ media/ net/ pinctrl/ regulator/ sound/ spi/ usb/ arm/vexpress.yaml clock/imx6.yaml gpio/gpio.txt i2c/i2c-imx.yaml 说明: • 新内核(5.x+)推荐使用 .yaml 格式,支持 schema 校验。 • 旧内核或部分子系统仍使用 .txt 格式,但社区正逐步迁移。 • 厂商前缀文件 vendor-prefixes.yaml 定义了所有合法厂商字符串。 • 每个绑定文档对应一个或多个 compatible 字符串。

4.2 如何阅读绑定文档

绑定文档是驱动开发者与设备树编写者之间的“契约”。阅读时,你需要关注以下几个核心部分:

文档部分 内容说明 阅读要点
标题与描述 设备名称、功能概述 确认文档是否匹配你的硬件
compatible 属性 兼容字符串列表 驱动匹配的关键,必须与驱动代码一致
reg 属性 寄存器地址范围 注意地址位数(32/64位)和大小
interrupts 属性 中断号、触发类型 确认中断控制器 phandle 和中断类型
clocks 属性 时钟源 phandle 和索引 确认时钟名称和索引顺序
pinctrl 属性 引脚复用配置 确认 pinctrl-0、pinctrl-names
可选属性 非必须的属性 根据硬件需求决定是否添加
示例节点 完整的设备树节点示例 直接参考示例编写自己的节点
阅读技巧:
  • 先看 compatible 字符串,确认文档是否适用于你的硬件。
  • 重点关注 required 属性(YAML 文档中标记为 required 的字段),这些是必须提供的。
  • 示例节点是最佳参考,直接复制并修改即可。
  • 注意属性值的类型:u32、u64、string、phandle、bool 等。

4.3 如何编写绑定文档

编写绑定文档时,必须遵循内核社区的规范。新文档推荐使用 YAML 格式,因为它支持自动校验。以下是编写步骤和规范:

4.3.1 YAML 绑定文档结构

一个标准的 YAML 绑定文档包含以下部分:

# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/example/example-device.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#

title: Example Device Tree Binding

maintainers:
  - Your Name <your.email@example.com>

description: |
  This binding describes the Example Device, a fictional hardware block
  used for demonstration purposes.

properties:
  compatible:
    enum:
      - vendor,example-device-v1
      - vendor,example-device-v2

  reg:
    maxItems: 1
    description: Register base address and size.

  interrupts:
    maxItems: 1
    description: Interrupt number and type.

  clocks:
    maxItems: 2
    description: |
      Clock phandles: first is bus clock, second is peripheral clock.

  clock-names:
    items:
      - const: bus
      - const: periph

  vendor-specific-prop:
    $ref: /schemas/types.yaml#/definitions/uint32
    description: Vendor-specific configuration value.
    default: 0

required:
  - compatible
  - reg
  - interrupts

additionalProperties: false

examples:
  - |
    example-device@f0000000 {
        compatible = "vendor,example-device-v1";
        reg = <0x0 0xf0000000 0x0 0x1000>;
        interrupts = <0 42 4>;
        clocks = <&clkc 15>, <&clkc 16>;
        clock-names = "bus", "periph";
        vendor-specific-prop = <0x12345678>;
    };

4.3.2 编写规范与检查清单

检查项 要求 常见错误
SPDX 许可证 文件头部必须包含 遗漏或使用错误许可证
compatible 字符串 格式:vendor,device 缺少厂商前缀或使用下划线
属性类型 必须使用 schema 定义的类型 使用错误类型(如 string 代替 uint32)
required 列表 列出所有必须属性 遗漏关键属性
示例节点 必须包含至少一个完整示例 示例不完整或语法错误
additionalProperties 通常设为 false 遗漏导致未知属性被忽略
重要提醒:
  • 编写完成后,使用 make dt_binding_check 命令校验 YAML 文档的语法和 schema 正确性。
  • 使用 make dtbs_check 校验设备树源文件是否匹配绑定文档。
  • 提交前,确保文档通过 scripts/checkpatch.pl 的检查。
  • 厂商前缀必须在 vendor-prefixes.yaml 中注册,否则内核不会接受。

4.4 绑定文档与驱动的关联

绑定文档最终会与驱动代码中的 of_match_table 关联。驱动通过 compatible 字符串匹配设备树节点。以下是一个典型的驱动匹配示例:

// 驱动中的 of_match_table
static const struct of_device_id example_of_match[] = {
    { .compatible = "vendor,example-device-v1", .data = &v1_data },
    { .compatible = "vendor,example-device-v2", .data = &v2_data },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, example_of_match);

// 驱动 probe 函数
static int example_probe(struct platform_device *pdev)
{
    const struct of_device_id *match;
    struct device_node *np = pdev->dev.of_node;

    match = of_match_device(example_of_match, &pdev->dev);
    if (!match)
        return -ENODEV;

    // 读取设备树属性
    struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    int irq = platform_get_irq(pdev, 0);

    // 根据 match->data 选择不同版本的硬件处理逻辑
    const struct example_hw_data *hw_data = match->data;

    return 0;
}
绑定文档与驱动匹配流程 设备树节点 compatible = "vendor,example-device-v1" 绑定文档 定义属性规范与约束 驱动 of_match_table .compatible = "vendor,example-device-v1" 校验 匹配 内核匹配:of_match_node() 遍历 of_match_table probe() 读取属性,初始化硬件 绑定文档是设备树节点与驱动之间的“契约”:设备树节点必须遵循绑定文档定义的属性规范, 驱动通过 compatible 字符串匹配节点,并在 probe 中读取绑定文档定义的属性。

4.5 常见问题与避坑指南

编写绑定文档时的常见错误:
  • compatible 字符串格式错误:必须使用小写字母、数字和连字符,格式为 vendor,device。不要使用下划线或大写字母。
  • 遗漏 required 属性:驱动依赖的属性必须标记为 required,否则设备树可能缺少关键信息。
  • 示例节点语法错误:示例节点必须使用正确的设备树语法,包括地址、中断、时钟等。
  • 未注册厂商前缀:在 vendor-prefixes.yaml 中添加厂商前缀,否则内核不会接受。
  • 属性类型不匹配:使用 schema 定义的类型,如 uint32、string、phandle 等。
调试技巧:
  • 使用 dt-validate 工具单独校验 YAML 文档。
  • 使用 dtc -I dtb -O dts 反编译设备树,检查实际节点属性。
  • 在驱动 probe 中添加 dev_info() 打印读取到的属性值,验证是否与绑定文档一致。
  • 查看内核日志中的 OF: ... 相关消息,定位匹配失败的原因。