4. 设备树绑定规范:标准属性与自定义属性
设备树绑定规范是连接硬件描述与驱动代码的契约。内核通过绑定规范解析设备树节点中的标准属性(compatible、reg、interrupts)和自定义属性,完成设备识别、资源分配与驱动匹配。本章深入剖析这三类属性的语法规则、内核解析机制及厂商前缀管理方法。
核心观点:绑定规范的本质是“硬件描述语言”的语法定义。标准属性保证跨平台兼容性,自定义属性提供硬件特定扩展,厂商前缀避免命名冲突。
4.1 标准属性:compatible / reg / interrupts
标准属性是设备树规范中强制或推荐使用的属性,内核在启动阶段直接解析它们完成设备枚举与资源映射。
4.1.1 compatible 属性:驱动匹配的钥匙
compatible 属性是一个字符串列表,用于标识设备的具体型号和兼容性。内核通过它查找匹配的驱动。
- 格式:
"厂商前缀,设备型号",可包含多个字符串,按优先级从高到低排列。 - 匹配规则:驱动中的
of_match_table与设备树中的compatible字符串进行精确匹配。 - 最佳实践:第一个字符串必须是精确的“厂商,型号”,后续字符串可以是更通用的兼容型号。
// 设备树节点示例
uart0: serial@10000000 {
compatible = "fsl,imx6q-uart", "fsl,imx21-uart";
reg = <0x10000000 0x4000>;
interrupts = <0 58 4>;
};
// 驱动匹配表
static const struct of_device_id imx_uart_dt_ids[] = {
{ .compatible = "fsl,imx6q-uart", .data = &imx6q_data },
{ .compatible = "fsl,imx21-uart", .data = &imx21_data },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, imx_uart_dt_ids);
匹配优先级:内核遍历
of_match_table 时,按设备树中 compatible 字符串的顺序依次匹配。第一个匹配成功的驱动将被绑定。
4.1.2 reg 属性:地址与大小描述
reg 属性描述设备在总线上的地址空间,由一组“地址+大小”对组成。内核通过它完成内存映射和资源分配。
| 属性字段 | 含义 | 示例值 |
|---|---|---|
#address-cells |
地址字段占用的 32 位单元数 | 1(32位地址)或 2(64位地址) |
#size-cells |
大小字段占用的 32 位单元数 | 1(32位大小)或 0(无大小) |
reg |
地址与大小交替排列的数组 | <0x10000000 0x4000> |
// 64位地址空间示例
memory@80000000 {
device_type = "memory";
reg = <0x00000000 0x80000000 0x00000000 0x80000000>;
// 地址:0x00000000_80000000,大小:0x00000000_80000000(2GB)
};
// 多段地址空间示例
dma-controller@1e000000 {
compatible = "snps,dma-spear1340";
reg = <0x1e000000 0x1000>,
<0x1e001000 0x1000>;
// 两段连续的寄存器空间
};
4.1.3 interrupts 属性:中断描述
interrupts 属性描述设备的中断请求线,需要与 interrupt-parent 配合使用。中断描述符的格式由中断控制器定义。
- 标准格式:
<中断类型 中断号 触发方式> - 中断类型:0(SPI,共享外设中断)、1(PPI,私有外设中断)
- 触发方式:1(上升沿触发)、2(下降沿触发)、4(高电平触发)、8(低电平触发)
// GIC 中断控制器示例
gic: interrupt-controller@2c001000 {
compatible = "arm,cortex-a9-gic";
#interrupt-cells = <3>;
interrupt-controller;
reg = <0x2c001000 0x1000>,
<0x2c002000 0x1000>;
};
// 使用中断的设备节点
ethernet@18000000 {
compatible = "smsc,lan9115";
reg = <0x18000000 0x100>;
interrupts = <0 42 4>;
interrupt-parent = <&gic>;
};
常见错误:忘记设置
interrupt-parent 或 #interrupt-cells 不匹配,会导致中断无法正确路由。务必检查中断控制器的 #interrupt-cells 值。
4.2 自定义属性:硬件特定扩展
自定义属性允许 SoC 厂商描述标准属性无法覆盖的硬件特性。内核通过 of_property_read_* 系列 API 读取这些属性。
4.2.1 自定义属性定义规则
- 命名规范:使用厂商前缀作为命名空间,如
fsl,uart-has-rtscts。 - 数据类型:支持
u32、u64、string、boolean、phandle等。 - 文档要求:必须在绑定文档中明确定义,否则视为未公开接口。
// 自定义属性示例
spi@10014000 {
compatible = "fsl,imx6q-ecspi", "fsl,imx51-ecspi";
reg = <0x10014000 0x4000>;
interrupts = <0 46 4>;
fsl,spi-num-chipselects = <4>; // 自定义:片选数量
fsl,spi-32bit-mode; // 自定义:布尔属性,启用32位模式
cs-gpios = <&gpio1 0 0>,
<&gpio1 1 0>,
<&gpio1 2 0>,
<&gpio1 3 0>; // 自定义:片选GPIO列表
};
4.2.2 内核读取自定义属性 API
| API 函数 | 功能 | 返回值 |
|---|---|---|
of_property_read_u32 |
读取 32 位整数属性 | 0 成功,负值错误 |
of_property_read_string |
读取字符串属性 | 0 成功,负值错误 |
of_property_read_bool |
检查布尔属性是否存在 | true/false |
of_get_named_gpio |
读取 GPIO 属性 | GPIO 编号或负值错误 |
// 驱动中读取自定义属性示例
static int imx_ecspi_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
u32 num_cs;
bool mode_32bit;
int ret;
// 读取 u32 属性
ret = of_property_read_u32(np, "fsl,spi-num-chipselects", &num_cs);
if (ret) {
dev_err(&pdev->dev, "missing fsl,spi-num-chipselects\n");
return ret;
}
// 读取布尔属性
mode_32bit = of_property_read_bool(np, "fsl,spi-32bit-mode");
// 读取 GPIO 属性
for (int i = 0; i < num_cs; i++) {
int gpio = of_get_named_gpio(np, "cs-gpios", i);
if (gpio < 0) {
dev_err(&pdev->dev, "invalid cs-gpios at index %d\n", i);
return gpio;
}
}
return 0;
}
设计原则:自定义属性应遵循“最小必要”原则。能用标准属性表达的硬件特性,绝不使用自定义属性。自定义属性必须附带完整的绑定文档。
4.3 厂商前缀管理
厂商前缀是设备树命名空间的核心机制,确保不同厂商的同名设备不会冲突。Linux 内核维护一个官方前缀列表。
4.3.1 前缀注册与查询
所有厂商前缀必须在 Documentation/devicetree/bindings/vendor-prefixes.yaml 中注册。内核社区通过补丁审核机制管理前缀的添加。
| 前缀 | 厂商全称 | 应用场景 |
|---|---|---|
fsl |
Freescale Semiconductor / NXP | i.MX 系列 SoC |
brcm |
Broadcom Corporation | BCM 系列网络芯片 |
ti |
Texas Instruments | OMAP / AM 系列处理器 |
allwinner |
Allwinner Technology | Sunxi 系列 SoC |
rockchip |
Rockchip Electronics | RK 系列 SoC |
4.3.2 前缀使用规范
- 唯一性:每个厂商只能注册一个前缀,避免使用缩写歧义。
- 稳定性:已注册的前缀不得更改,废弃前缀需标记为 deprecated。
- 兼容性:新设备必须使用已注册的前缀,未注册前缀的补丁将被拒绝。
# vendor-prefixes.yaml 示例片段
%YAML 1.2
---
$id: http://devicetree.org/schemas/vendor-prefixes.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
maintainers:
- Devicetree Specification Mailing List <devicetree-spec@vger.kernel.org>
properties:
fsl:
description: Freescale Semiconductor Inc. (now NXP Semiconductors)
type: string
pattern: "^fsl,.*$"
brcm:
description: Broadcom Corporation
type: string
pattern: "^brcm,.*$"
合规检查:提交设备树补丁前,务必使用
scripts/checkpatch.pl 检查厂商前缀是否已注册。未注册前缀会导致补丁被自动拒绝。
4.4 绑定规范综合架构
下图展示了设备树绑定规范在驱动架构中的完整作用链路:从设备树源文件到驱动匹配、资源获取、硬件初始化。
4.5 绑定规范最佳实践
清单检查:每次编写设备树节点时,对照以下清单确保绑定规范合规:
- compatible 字符串是否包含已注册的厂商前缀?
- reg 属性的 #address-cells 和 #size-cells 是否与父节点一致?
- interrupts 属性的格式是否与中断控制器的 #interrupt-cells 匹配?
- 自定义属性是否在绑定文档中定义?
- 厂商前缀是否在 vendor-prefixes.yaml 中注册?
4.5.1 绑定文档编写规范
每个设备树绑定必须包含 YAML 格式的文档,描述所有属性的类型、约束和默认值。内核社区已全面迁移到 YAML 绑定格式。
# Documentation/devicetree/bindings/serial/fsl-imx-uart.yaml
%YAML 1.2
---
$id: http://devicetree.org/schemas/serial/fsl-imx-uart.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Freescale i.MX UART
maintainers:
- Shawn Guo <shawnguo@kernel.org>
properties:
compatible:
items:
- enum:
- fsl,imx6q-uart
- fsl,imx21-uart
- const: fsl,imx21-uart
reg:
maxItems: 1
interrupts:
maxItems: 1
fsl,uart-has-rtscts:
description: Indicates that the UART has RTS/CTS hardware flow control
type: boolean
required:
- compatible
- reg
- interrupts
additionalProperties: false
4.5.2 常见绑定错误与修复
| 错误类型 | 错误示例 | 正确做法 |
|---|---|---|
| 前缀未注册 | mycompany,uart |
使用已注册前缀,如 fsl,uart |
| compatible 顺序错误 | "fsl,imx21-uart", "fsl,imx6q-uart" |
精确型号在前,通用型号在后 |
| reg 地址单元不匹配 | 父节点 #address-cells = <2>,子节点只写一个地址 |
确保地址单元数与父节点一致 |
| 中断描述符格式错误 | interrupts = <42 4>(缺少中断类型) |
根据 #interrupt-cells 填写完整 |
总结:设备树绑定规范是驱动架构设计的基石。标准属性保证跨平台兼容性,自定义属性提供硬件特定扩展,厂商前缀确保命名空间隔离。掌握这三类属性的语法和内核解析机制,是编写高质量设备树和驱动代码的前提。