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 校验,能自动检查设备树节点的合法性。
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;
}
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: ...相关消息,定位匹配失败的原因。