2. 设备树基础
设备树(Device Tree,DT)是嵌入式 Linux 系统中用于描述硬件拓扑结构和资源分配的一种数据结构。它取代了传统 ARM 平台中充斥在 arch/arm/mach-* 目录下的大量板级代码(board file),实现了“内核镜像与硬件描述分离”的设计目标。对于 Platform 驱动开发者而言,设备树是获取硬件资源(如寄存器地址、中断号、DMA 通道)的核心来源。
2.1 设备树语法基础
设备树源文件(.dts)采用一种树形结构的文本格式,其基本语法单元包括:节点(node)、属性(property)和值(value)。一个典型的设备树片段如下:
/dts-v1/;
/ {
model = "Embedded Development Board";
compatible = "vendor,board-v1", "vendor,board";
#address-cells = <1>;
#size-cells = <1>;
soc {
compatible = "simple-bus";
#address-cells = <1>;
#size-cells = <1>;
ranges;
uart0: serial@101f1000 {
compatible = "vendor,uart";
reg = <0x101f1000 0x1000>;
interrupts = <0 5 4>;
clock-frequency = <24000000>;
status = "okay";
};
};
};
语法要点:
- 根节点:用
/表示,是整个设备树的起点。 - 节点名称:格式为
node-name@unit-address,其中@unit-address通常为设备寄存器基地址,用于区分同名节点。 - 属性:键值对形式,值可以是字符串、32 位整数数组(
< >)、字节数组([ ])、字符串列表等。 - 标签(Label):如
uart0:,用于在.dtsi文件中引用或覆盖节点。 - 包含文件:使用
#include引入.dtsi头文件,实现 SoC 级描述的复用。
.dts 文件通过 Device Tree Compiler(dtc)编译为 .dtb(Device Tree Blob)二进制文件。Bootloader(如 U-Boot)将 .dtb 加载到内存,并将地址传递给内核。
2.2 节点与属性
设备树中的每个硬件设备或总线都对应一个节点。节点可以嵌套,形成层次结构。属性则描述了节点的具体特征。
| 概念 | 说明 | 示例 |
|---|---|---|
| 节点(Node) | 代表一个硬件设备、总线或控制器 | spi@10010000 { ... }; |
| 属性(Property) | 描述节点的特性或资源 | reg = <0x10010000 0x1000>; |
| 子节点 | 挂载在父节点下的设备 | spi@10010000 { flash@0 { ... }; }; |
| 状态属性 | 控制设备是否启用 | status = "disabled"; 或 "okay" |
常见的标准属性包括:
compatible:驱动匹配的关键字。reg:描述设备地址空间。interrupts:描述中断资源。clocks:关联时钟控制器。dmas:关联 DMA 控制器(后续章节重点)。status:设备状态。
@unit-address 仅用于区分同名节点,内核并不直接解析该地址来获取寄存器基址。寄存器基址必须通过 reg 属性明确指定。
2.3 reg 属性:地址资源描述
reg 属性是 Platform 驱动获取寄存器物理地址和内存范围的核心依据。其格式由父节点的 #address-cells 和 #size-cells 决定。
#address-cells:表示地址字段占用的 32 位整数个数(通常为 1 或 2)。#size-cells:表示长度字段占用的 32 位整数个数(通常为 1 或 2)。
每个 reg 条目由 (address, length) 对组成。例如:
#address-cells = <1>;
#size-cells = <1>;
dma@101e3000 {
reg = <0x101e3000 0x1000>;
/* 基址: 0x101e3000, 范围: 0x1000 (4KB) */
};
#address-cells = <2>;
#size-cells = <2>;
pcie@40000000 {
reg = <0x0 0x40000000 0x0 0x10000000>;
/* 基址: 0x40000000 (高32位0, 低32位0x40000000), 范围: 0x10000000 (256MB) */
};
驱动侧通过 platform_get_resource(pdev, IORESOURCE_MEM, index) 获取 reg 属性中第 index 个条目的物理地址和长度。
reg 资源后,通常需要调用 devm_ioremap_resource() 将物理地址映射到内核虚拟地址空间,才能对寄存器进行读写操作。
2.4 interrupts 属性:中断资源描述
interrupts 属性描述了设备的中断请求线。其格式依赖于中断控制器(Interrupt Controller)的 #interrupt-cells 属性。
以常见的 GIC(Generic Interrupt Controller)为例,#interrupt-cells = <3>,三个字段的含义为:
| 字段 | 含义 | 常见值 |
|---|---|---|
| 字段 1 | 中断类型 | 0 = SPI (共享外设中断), 1 = PPI (私有外设中断) |
| 字段 2 | 中断号 | SPI 中断号范围: 32 ~ 1019 |
| 字段 3 | 触发类型与极性 | 4 = 高电平触发, 8 = 低电平触发, 2 = 上升沿触发, 1 = 下降沿触发 |
设备节点示例:
interrupt-parent = <&gic>; /* 指定中断控制器 */
interrupts = <0 45 4>; /* SPI 中断, 中断号 45, 高电平触发 */
驱动侧通过 platform_get_resource(pdev, IORESOURCE_IRQ, index) 或 platform_get_irq(pdev, index) 获取中断号,然后调用 devm_request_irq() 注册中断处理函数。
interrupts 属性可以包含多个条目,驱动通过 index 参数区分。
2.5 compatible 属性:驱动匹配的核心
compatible 属性是设备树与驱动代码之间的“桥梁”。它包含一个或多个字符串,用于标识设备的具体型号和兼容性。
- 格式:
"厂商,设备型号",例如"vendor,uart"。 - 匹配优先级:内核在注册 Platform 设备时,会按照
compatible属性中的字符串顺序,依次与驱动中of_device_id表进行匹配。 - 兼容性列表:可以包含多个字符串,表示该设备兼容多种驱动。例如
compatible = "vendor,uart-v2", "vendor,uart-v1",表示优先匹配 v2 驱动,若不存在则回退到 v1 驱动。
驱动侧匹配代码示例:
static const struct of_device_id my_uart_of_match[] = {
{ .compatible = "vendor,uart-v2", .data = &uart_v2_data },
{ .compatible = "vendor,uart-v1", .data = &uart_v1_data },
{ /* end of list */ }
};
MODULE_DEVICE_TABLE(of, my_uart_of_match);
static struct platform_driver my_uart_driver = {
.probe = my_uart_probe,
.remove = my_uart_remove,
.driver = {
.name = "my_uart",
.of_match_table = my_uart_of_match,
},
};
compatible 字符串必须与驱动中 of_device_id 表完全一致(包括大小写和标点符号)。一个常见的错误是设备树中写 "vendor,uart",而驱动中写 "vendor,uart" 但多了一个空格,导致匹配失败。
2.6 设备树资源获取流程总览
下面的 SVG 图展示了从设备树节点到 Platform 驱动获取资源的完整流程:
2.7 关键属性对比总结
| 属性 | 用途 | 驱动获取 API | 典型值示例 |
|---|---|---|---|
compatible |
驱动匹配 | of_match_device() |
"vendor,uart" |
reg |
寄存器/内存地址 | platform_get_resource(pdev, IORESOURCE_MEM, 0) |
<0x101f1000 0x1000> |
interrupts |
中断号与触发方式 | platform_get_irq(pdev, 0) |
<0 45 4> |
status |
设备启用/禁用 | of_device_is_available() |
"okay" / "disabled" |
/sys/firmware/devicetree/base 目录下的设备树结构,加深对节点和属性的理解。