4. 设备树地址与中断映射:地址编码与中断控制器规则
设备树(Device Tree)通过标准属性描述硬件地址空间与中断系统的拓扑结构。本章深入解析 #address-cells、#size-cells 的地址编码规则,以及 interrupt-controller、interrupt-parent、interrupts 的中断映射机制,为驱动开发中的资源获取提供精确的硬件描述基础。
4.1 地址编码:address-cells 与 size-cells
设备树使用 reg 属性描述设备在总线上的地址范围。#address-cells 和 #size-cells 定义地址与长度字段的 32 位字数量,形成层次化的地址空间。
| 属性 | 作用 | 取值含义 | 典型值 |
|---|---|---|---|
#address-cells |
子节点 reg 中地址字段的 32 位字个数 |
1:32 位地址;2:64 位地址 | 1(32位SoC)、2(64位SoC) |
#size-cells |
子节点 reg 中长度字段的 32 位字个数 |
0:无长度(仅地址);1:32 位长度 | 1(常见)、0(仅地址) |
reg |
设备地址与长度列表 | 格式:<addr1 len1 addr2 len2 ...> |
<0x10000000 0x1000> |
关键规则:父节点的
#address-cells 和 #size-cells 决定其直接子节点 reg 的解析方式。根节点通常定义 #address-cells = <2> 和 #size-cells = <2> 以支持 64 位地址空间。
4.1.1 地址编码示例
/dts-v1/;
/ {
#address-cells = <2>; // 64位地址
#size-cells = <2>; // 64位长度
memory@80000000 {
device_type = "memory";
reg = <0x0 0x80000000 0x0 0x80000000>;
// 地址:0x80000000,长度:0x80000000(2GB)
};
soc {
#address-cells = <1>; // 32位地址
#size-cells = <1>; // 32位长度
ranges = <0x0 0x0 0xfe000000 0x02000000>;
// 子地址0映射到父地址0xfe000000,范围0x02000000
uart@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
// 地址:0x10000000,长度:0x1000(4KB)
};
};
};
驱动开发提示:Linux 内核通过
of_address_to_resource() 将 reg 自动转换为 struct resource,驱动无需手动解析 #address-cells 和 #size-cells。但理解编码规则对调试设备树错误至关重要。
4.2 中断控制器与中断映射
设备树通过 interrupt-controller、#interrupt-cells、interrupt-parent 和 interrupts 属性描述中断系统的拓扑结构,实现从设备中断源到中断控制器的映射。
4.2.1 中断控制器属性
| 属性 | 作用 | 说明 |
|---|---|---|
interrupt-controller |
声明节点为中断控制器 | 空属性,存在即表示该节点可接收中断 |
#interrupt-cells |
定义 interrupts 中每个中断描述所需的 32 位字个数 |
ARM GIC 通常为 3(类型、编号、标志) |
interrupt-parent |
指定设备连接的中断控制器 | 值为中断控制器的 phandle |
interrupts |
描述设备的中断请求线 | 格式由 #interrupt-cells 决定 |
4.2.2 中断映射规则
- 中断控制器声明:每个中断控制器节点必须包含
interrupt-controller和#interrupt-cells。 - 中断父级指定:设备节点通过
interrupt-parent指向其连接的中断控制器。若未指定,则继承父节点的interrupt-parent。 - 中断描述:
interrupts属性包含一个或多个中断描述,每个描述由#interrupt-cells个 32 位整数组成。 - 中断级联:中断控制器可以连接上级中断控制器,形成中断树。根中断控制器通常为 GIC(Generic Interrupt Controller)。
常见错误:中断描述中的标志位(如触发类型、极性)必须与中断控制器硬件一致。ARM GIC 中,
interrupts = <0 33 4> 表示 SPI 类型、中断号 33、高电平触发。标志位错误会导致中断无法正确响应。
4.3 中断映射完整示例
/ {
#address-cells = <2>;
#size-cells = <2>;
gic: interrupt-controller@f0001000 {
compatible = "arm,gic-400";
reg = <0x0 0xf0001000 0x0 0x1000>,
<0x0 0xf0002000 0x0 0x2000>;
interrupt-controller;
#interrupt-cells = <3>; // 类型、编号、标志
};
soc {
#address-cells = <1>;
#size-cells = <1>;
ranges = <0x0 0x0 0xfe000000 0x02000000>;
interrupt-parent = <&gic>; // 默认中断父级
uart@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupts = <0 33 4>;
// 类型0(SPI)、编号33、标志4(高电平触发)
};
gpio@10001000 {
compatible = "brcm,bcm2835-gpio";
reg = <0x10001000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
interrupts = <0 42 4>;
// GPIO控制器本身连接GIC,中断号42
};
};
};
中断映射流程:设备
uart 的 interrupts 通过 interrupt-parent 指向 GIC,GIC 根据 #interrupt-cells = <3> 解析 <0 33 4> 为 SPI 中断、编号 33、高电平触发。驱动通过 platform_get_irq() 获取中断号。
4.4 地址与中断映射的 SVG 架构图
下图展示设备树中地址编码与中断映射的完整拓扑结构,包括根节点、总线、设备、中断控制器的层次关系。
4.5 中断映射规则总结
| 规则 | 描述 | 示例 |
|---|---|---|
| 中断控制器声明 | 节点必须包含 interrupt-controller 和 #interrupt-cells |
interrupt-controller; #interrupt-cells = <3>; |
| 中断父级继承 | 子节点未指定 interrupt-parent 时继承父节点 |
soc { interrupt-parent = <&gic>; }; |
| 中断描述格式 | 由 #interrupt-cells 决定,常见为 3 字段 |
interrupts = <0 33 4>; |
| 中断级联 | 中断控制器可连接上级控制器,形成树状结构 | GPIO 控制器既是中断控制器,又连接 GIC |
| 驱动获取中断 | 内核 API 自动解析设备树中断映射 | platform_get_irq(pdev, 0) |
调试技巧:使用
dtc -I dtb -O dts 反编译设备树二进制文件,检查 interrupts 和 interrupt-parent 是否正确。内核启动日志中的 irq: no irq domain found for ... 错误通常表示中断映射缺失或 #interrupt-cells 不匹配。
4.6 地址与中断映射的驱动开发流程
驱动开发中,地址与中断映射的获取遵循标准流程,设备树解析由内核框架自动完成。
核心结论:设备树的地址编码(
#address-cells/#size-cells)和中断映射(interrupt-controller/interrupts)是驱动获取硬件资源的基础。内核框架自动完成设备树到内核资源的转换,驱动开发者只需理解属性含义,即可通过标准 API 获取地址和中断资源,无需手动解析设备树二进制数据。