4. 设备树绑定(Binding)

设备树绑定(Device Tree Binding)是连接硬件描述与内核驱动之间的契约文档。它定义了设备树节点中哪些属性是必需的、哪些是可选的、属性值的类型与格式,以及节点与驱动之间的匹配关系。编写正确的绑定文档,是确保设备树能被内核正确解析、驱动能正确匹配硬件的基础。

核心原则: 绑定文档是设备树与内核驱动之间的“接口规范”。驱动开发者根据绑定文档解析设备树节点,设备树编写者根据绑定文档填充节点属性。两者必须严格对齐,否则设备无法正常工作。

4.1 绑定文档的标准格式

Linux 内核社区采用 YAML 格式编写设备树绑定文档,文件扩展名为 .yaml,存放于内核源码树的 Documentation/devicetree/bindings/ 目录下。YAML 格式相比传统的纯文本格式,具有结构清晰、可机器校验、易于维护等优点。

一个标准的 YAML 绑定文档包含以下顶层字段:

字段 必选/可选 说明
# SPDX-License-Identifier 必选 许可证标识,通常为 (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2 必选 YAML 版本声明
$id 必选 绑定文档的唯一 URI,格式为 http://devicetree.org/schemas/.../xxx.yaml#
$schema 必选 元模式引用,固定为 http://devicetree.org/meta-schemas/core.yaml#
title 必选 绑定的简短标题,如 "NXP PCA9685 PWM Controller"
maintainers 必选 维护者邮箱列表
description 可选 对设备功能的详细描述,可包含多行文本
properties 必选 定义节点所有属性的名称、类型、约束和描述
required 必选 列出所有必须存在的属性名称列表
additionalProperties 可选 是否允许未在 properties 中定义的额外属性,通常设为 false
examples 可选 一个或多个设备树节点示例,用于展示正确的用法

以下是一个典型的 I2C 设备绑定文档片段:

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

title: NXP PCA9685 PWM Controller

maintainers:
  - Stefan Wahren <stefan.wahren@i2se.com>

description: |
  The PCA9685 is an I2C-bus controlled 16-channel LED controller
  optimized for Red/Green/Blue/Amber (RGBA) color backlighting
  applications.

properties:
  compatible:
    const: nxp,pca9685

  reg:
    maxItems: 1

  "#pwm-cells":
    const: 2

  nxp,output-mode:
    description: |
      Output mode configuration. 0 = open-drain, 1 = totem-pole.
    $ref: /schemas/types.yaml#/definitions/uint32
    enum: [0, 1]
    default: 1

required:
  - compatible
  - reg
  - "#pwm-cells"

additionalProperties: false

examples:
  - |
    i2c {
        #address-cells = <1>;
        #size-cells = <0>;

        pca9685@40 {
            compatible = "nxp,pca9685";
            reg = <0x40>;
            #pwm-cells = <2>;
            nxp,output-mode = <1>;
        };
    };

4.2 如何编写自定义绑定

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

  1. 确定 compatible 字符串:遵循 厂商,设备型号 的命名规范,如 ti,ads1115、fsl,imx6q-uart。厂商名使用小写,设备型号使用小写字母、数字和连字符。
  2. 分析硬件寄存器与功能:梳理设备需要从设备树获取哪些配置信息,如基地址、中断号、时钟频率、GPIO 引脚、电源电压等。
  3. 定义属性集合:将硬件配置映射为设备树属性。标准属性优先使用内核预定义的类型(如 reg、interrupts、clocks),自定义属性需在绑定中明确定义类型和约束。
  4. 编写 YAML 绑定文件:按照标准格式编写,确保每个属性都有 description 和类型约束。
  5. 编写设备树节点示例:在 examples 字段中提供一个完整的、可工作的设备树节点片段。
  6. 运行绑定校验工具:使用 dt_binding_check 验证 YAML 语法和模式正确性。
最佳实践: 自定义属性命名应使用 厂商,属性名 的格式,避免与标准属性冲突。例如 ti,conversion-rate 而不是 conversion-rate。属性值类型优先使用 uint32、string、phandle 等基础类型,复杂结构使用 uint32-array 或 string-array。

4.3 绑定正确性检查

内核提供了两套独立的校验工具链,分别用于检查绑定文档本身和检查设备树实例是否符合绑定规范。

校验目标 Make 目标 工具 说明
绑定文档语法 dt_binding_check yamllint + dtschema 验证 YAML 格式、字段完整性、类型约束、引用正确性
设备树实例合规 dtbs_check dt-validate 将编译后的 .dtb 与绑定模式进行比对,报告缺失属性、类型错误、值越界等

执行校验的命令示例:

# 校验所有绑定文档(在内核源码根目录执行)
make dt_binding_check

# 校验特定绑定文档
make dt_binding_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/pwm/pca9685.yaml

# 校验所有设备树实例
make dtbs_check

# 校验特定设备树文件
make dtbs_check DT_SCHEMA_FILES=Documentation/devicetree/bindings/pwm/pca9685.yaml

常见的校验错误类型及解决方法:

  • YAML 语法错误:缩进不一致、冒号后缺少空格、列表项未对齐。使用 yamllint 可快速定位。
  • 缺少 required 属性:绑定中 required 列表列出的属性在设备树节点中不存在。检查设备树文件,补充缺失属性。
  • 属性类型不匹配:例如绑定中定义为 uint32,但设备树中写成了字符串。修正属性值格式。
  • 枚举值越界:属性值不在 enum 定义的范围内。检查硬件手册,使用正确的枚举值。
  • compatible 字符串不匹配:设备树中的 compatible 与绑定中定义的 const 或 enum 不一致。确认驱动支持的 compatible 列表。
常见陷阱: 绑定文档中的 additionalProperties: false 会拒绝所有未在 properties 中显式定义的属性。如果设备树节点包含标准属性(如 status、pinctrl-0、pinctrl-names),必须在绑定中显式声明这些属性,否则校验会报错。建议在绑定开头使用 allOf: 引用公共模式来包含这些标准属性。

4.4 绑定与驱动的匹配机制

设备树绑定文档最终通过 compatible 字符串与内核驱动关联。驱动中的 of_match_table 定义了驱动支持的 compatible 列表,设备树节点中的 compatible 属性必须与其中之一匹配,驱动才能被绑定到设备。

// 内核驱动中的匹配表示例
static const struct of_device_id pca9685_of_match[] = {
    { .compatible = "nxp,pca9685", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, pca9685_of_match);

static struct i2c_driver pca9685_driver = {
    .driver = {
        .name = "pca9685",
        .of_match_table = pca9685_of_match,
    },
    .probe = pca9685_probe,
    .id_table = pca9685_id,
};

当内核解析设备树时,会遍历所有节点,对每个节点查找 compatible 匹配的驱动。匹配成功后,调用驱动的 probe 函数,并将设备树节点指针传递给驱动。驱动通过 of_property_read_* 系列 API 读取绑定中定义的属性值。

// 驱动 probe 函数中读取设备树属性
static int pca9685_probe(struct i2c_client *client)
{
    struct device_node *np = client->dev.of_node;
    u32 output_mode;

    // 读取自定义属性,如果不存在则使用默认值 1
    of_property_read_u32(np, "nxp,output-mode", &output_mode);

    // 读取标准属性
    // reg 由 i2c 核心自动解析到 client->addr
    // interrupts 由 irq 核心自动解析

    // ... 初始化硬件 ...
}

下图展示了绑定文档、设备树、驱动三者之间的协作关系:

设备树绑定、设备树与驱动的协作关系 绑定文档 pca9685.yaml 定义属性、类型、约束 设备树节点 pca9685@40 填充属性值 内核驱动 pca9685.c of_match_table + probe 约束 匹配 dt_binding_check 校验绑定文档格式 dtbs_check 校验设备树合规性 验证 验证 关键流程 编写绑定文档 → dt_binding_check 验证 → 编写设备树节点 → dtbs_check 验证 → 驱动匹配 probe

4.5 绑定文档的维护与演进

设备树绑定文档是内核 ABI 的一部分,一旦合入主线,必须保持向后兼容。修改绑定文档时需遵循以下规则:

  • 禁止删除已有属性:即使某个属性在新版本中不再使用,也应在绑定中标记为 deprecated: true,而不是直接移除。
  • 新增属性必须可选:新增属性应设置合理的默认值,确保旧设备树文件在不修改的情况下仍能正常工作。
  • compatible 字符串不可修改:如果硬件版本有差异,应新增 compatible 字符串,而不是修改已有的字符串。
  • 提交信息必须包含绑定变更:任何驱动代码的修改,如果涉及设备树接口,必须同时更新对应的绑定文档。
调试建议: 当设备无法被驱动识别时,首先检查 /sys/firmware/devicetree/base/ 下的设备树节点内容,确认 compatible 属性是否与驱动匹配。然后使用 dtbs_check 验证设备树文件是否合规。最后检查内核日志中是否有 OF: ... 相关的错误信息,这些信息通常能直接指出绑定不匹配的具体原因。