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 如何编写自定义绑定
当需要为新的硬件设备编写驱动时,必须同时编写对应的设备树绑定文档。以下是编写自定义绑定的标准流程:
- 确定 compatible 字符串:遵循
厂商,设备型号的命名规范,如ti,ads1115、fsl,imx6q-uart。厂商名使用小写,设备型号使用小写字母、数字和连字符。 - 分析硬件寄存器与功能:梳理设备需要从设备树获取哪些配置信息,如基地址、中断号、时钟频率、GPIO 引脚、电源电压等。
- 定义属性集合:将硬件配置映射为设备树属性。标准属性优先使用内核预定义的类型(如
reg、interrupts、clocks),自定义属性需在绑定中明确定义类型和约束。 - 编写 YAML 绑定文件:按照标准格式编写,确保每个属性都有
description和类型约束。 - 编写设备树节点示例:在
examples字段中提供一个完整的、可工作的设备树节点片段。 - 运行绑定校验工具:使用
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 核心自动解析
// ... 初始化硬件 ...
}
下图展示了绑定文档、设备树、驱动三者之间的协作关系:
4.5 绑定文档的维护与演进
设备树绑定文档是内核 ABI 的一部分,一旦合入主线,必须保持向后兼容。修改绑定文档时需遵循以下规则:
- 禁止删除已有属性:即使某个属性在新版本中不再使用,也应在绑定中标记为
deprecated: true,而不是直接移除。 - 新增属性必须可选:新增属性应设置合理的默认值,确保旧设备树文件在不修改的情况下仍能正常工作。
- compatible 字符串不可修改:如果硬件版本有差异,应新增 compatible 字符串,而不是修改已有的字符串。
- 提交信息必须包含绑定变更:任何驱动代码的修改,如果涉及设备树接口,必须同时更新对应的绑定文档。
调试建议: 当设备无法被驱动识别时,首先检查
/sys/firmware/devicetree/base/ 下的设备树节点内容,确认 compatible 属性是否与驱动匹配。然后使用 dtbs_check 验证设备树文件是否合规。最后检查内核日志中是否有 OF: ... 相关的错误信息,这些信息通常能直接指出绑定不匹配的具体原因。