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

设备树绑定文档(Device Tree Binding Document)是内核官方定义的“硬件描述规范说明书”。它规定了某个IP或设备在设备树中应该怎么写、有哪些属性、哪些是必须的、哪些是可选的。对于驱动开发者来说,绑定文档就是设备树与驱动之间的“契约”。

核心认知: 绑定文档不是代码,但它决定了驱动能否正确解析设备树。没有绑定文档的驱动,在主线内核中几乎不可能被接受。

3.1 内核绑定文档目录结构

内核源码树中的 Documentation/devicetree/bindings/ 目录按照设备类型和厂商进行组织。其结构清晰,便于开发者快速定位。

内核绑定文档目录结构(局部) bindings/ arm/ clock/ gpio/ i2c/ arm,vic.yaml fixed-clock.yaml gpio-controller.yaml i2c-gpio.yaml vendor/ vendor/ vendor/ vendor/ ti,am335x-uart.yaml nvidia,tegra-i2c.yaml

目录结构遵循以下规则:

  • 顶层目录:按设备类型分类,如 arm/、clock/、gpio/、i2c/、spi/、net/、sound/、display/ 等。
  • 厂商子目录:在类型目录下,通常有 vendor/ 子目录,存放具体厂商的绑定文件。
  • 文件格式:新内核(5.x+)统一使用 .yaml 格式(基于 json-schema 的 YAML 绑定)。旧内核可能还有 .txt 格式的纯文本绑定。
目录路径 内容说明 典型文件
bindings/arm/ ARM 架构相关设备(CPU、GIC、定时器等) arm,gic.yaml
bindings/clock/ 时钟控制器、固定时钟、PLL 等 fixed-clock.yaml
bindings/gpio/ GPIO 控制器、GPIO 按键等 gpio-controller.yaml
bindings/i2c/ I2C 控制器及 I2C 从设备 i2c-gpio.yaml
bindings/spi/ SPI 控制器及 SPI 从设备 spi-controller.yaml
bindings/net/ 以太网 MAC/PHY、无线网卡等 microchip,enc28j60.yaml
bindings/sound/ 音频编解码器、DAI 链路等 simple-audio-card.yaml
bindings/trivial-devices.yaml 简单设备(无需独立绑定文件)的汇总 (单文件)

3.2 如何阅读绑定文档

以 Documentation/devicetree/bindings/gpio/gpio-controller.yaml 为例,展示如何解析一个标准的 YAML 绑定文件。

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

title: GPIO Controller

maintainers:
  - Linus Walleij <linus.walleij@linaro.org>

description: |
  This binding describes a generic GPIO controller.
  Each GPIO controller must have a "gpio-controller" property.

properties:
  compatible:
    description: Compatible string for the GPIO controller.
    items:
      - enum:
          - brcm,bcm2835-gpio
          - nvidia,tegra20-gpio
          - ti,omap4-gpio

  reg:
    description: Address and length of the register map.
    maxItems: 1

  gpio-controller:
    description: Boolean property indicating this node is a GPIO controller.
    type: boolean

  "#gpio-cells":
    description: Number of cells in a GPIO specifier.
    enum: [1, 2]
    default: 2

required:
  - compatible
  - reg
  - gpio-controller
  - "#gpio-cells"

additionalProperties: false

examples:
  - |
    gpio@50000000 {
        compatible = "brcm,bcm2835-gpio";
        reg = <0x50000000 0x1000>;
        gpio-controller;
        #gpio-cells = <2>;
    };

阅读绑定文档的关键步骤:

  1. 查看 title 和 description:了解该绑定描述的是什么设备。
  2. 检查 compatible 列表:确认你的设备是否在支持的兼容字符串列表中。
  3. 分析 properties 段:区分哪些是 required(必须)属性,哪些是 optional(可选)属性。
  4. 理解属性类型:boolean(布尔)、uint32(32位整数)、string(字符串)、phandle(句柄)等。
  5. 参考 examples 段:示例代码展示了完整的设备树节点写法,是最直接的参考。
阅读技巧: 绑定文档中的 required 列表是“最低要求”。如果你的设备树节点缺少任何一个 required 属性,内核在解析时会报错,驱动 probe 会失败。

3.3 编写自定义绑定

当你需要为新的硬件设备编写驱动时,必须同时编写对应的绑定文档。以下是编写自定义绑定的标准流程和规范。

3.3.1 绑定文件模板

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

title: My Custom Device

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

description: |
  This binding describes a custom device that does XYZ.
  It uses MMIO registers and generates interrupts.

properties:
  compatible:
    description: Compatible string for the device.
    items:
      - const: vendor,my-custom-device

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

  interrupts:
    description: Interrupt number for the device.
    maxItems: 1

  clock-names:
    description: Names of the clocks.
    items:
      - const: bus
      - const: core

  clocks:
    description: Phandles to the clocks.
    minItems: 2
    maxItems: 2

  vendor-specific-prop:
    description: A vendor-specific property (example).
    $ref: /schemas/types.yaml#/definitions/uint32
    default: 0x100

required:
  - compatible
  - reg
  - interrupts

additionalProperties: false

examples:
  - |
    my-device@40000000 {
        compatible = "vendor,my-custom-device";
        reg = <0x40000000 0x1000>;
        interrupts = <0 42 4>;
        clocks = <&clkc 15>, <&clkc 16>;
        clock-names = "bus", "core";
        vendor-specific-prop = <0x200>;
    };

3.3.2 编写步骤

步骤 操作 说明
1 确定 compatible 字符串 格式为 vendor,device,如 ti,am335x-uart。vendor 必须是内核认可的厂商前缀。
2 列出所有属性 包括 reg、interrupts、clocks、resets、DMA 等标准属性,以及厂商自定义属性。
3 标记 required 和 optional 驱动 probe 必须依赖的属性放在 required 中;可选功能放在 optional 中。
4 编写 examples 提供一个完整的、可编译通过的设备树节点示例。
5 使用 dt_binding_check 验证 运行 make dt_binding_check DT_SCHEMA_FILES=my-custom-device.yaml 检查语法。

3.3.3 验证绑定文件

内核提供了专门的工具来验证绑定文件的正确性:

# 验证单个绑定文件
make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/misc/my-custom-device.yaml

# 验证所有绑定文件(耗时较长)
make dt_binding_check

# 验证设备树源文件是否匹配绑定
make dtbs_check DT_SCHEMA_FILES=my-custom-device.yaml
常见错误:
  • compatible 字符串未在绑定文档中列出,导致 dtbs_check 报错。
  • required 属性遗漏,驱动 probe 时返回 -ENODEV。
  • 属性类型错误(如把 uint32 写成了 string),导致解析失败。
  • YAML 缩进错误(必须使用空格,不能使用 Tab)。

3.3.4 绑定文档与驱动的对应关系

绑定文档 ↔ 驱动 ↔ 设备树 对应关系 绑定文档 .yaml 文件 定义属性规则 驱动代码 .c 文件 解析设备树节点 设备树源文件 .dts/.dtsi 文件 描述硬件实例 工作流程 1. 绑定文档定义 compatible = "vendor,device" 以及 reg、interrupts 等属性规则。 2. 驱动通过 of_match_table 匹配 compatible 字符串,调用 probe 函数。 3. 驱动使用 of_iomap、of_get_property 等 API 读取设备树中的属性值。

3.3.5 绑定文档编写规范总结

  • 命名规范:文件名使用 vendor,device.yaml 格式,如 ti,am335x-uart.yaml。
  • 兼容性:如果设备有多个版本,使用 enum 列出所有兼容字符串。
  • 属性复用:尽量复用内核已有的属性定义(如 reg、interrupts),避免重复发明轮子。
  • 文档完整性:每个属性都要有 description,说明其作用和取值范围。
  • 示例有效性:examples 中的代码必须能通过 dtbs_check 验证。
实战建议: 在编写自定义绑定之前,先在内核源码中搜索类似设备的绑定文件作为参考。例如,编写 SPI 设备绑定时,参考 bindings/spi/ 目录下的现有文件。复制一份并修改,比从零开始更高效且不易出错。