4. 设备树地址与中断映射:地址编码与中断控制器
设备树的核心能力之一是将硬件拓扑中的地址空间、中断连接关系以标准化的方式描述出来,使操作系统无需硬编码即可完成资源分配。本章深入解析地址编码属性 #address-cells / #size-cells 以及中断控制器与中断映射的完整机制。
核心要点:地址编码决定总线域内地址与长度的字长;中断映射通过
interrupt-parent、interrupts 和 interrupt-map 将设备中断请求路由到正确的中断控制器。
4.1 地址编码:#address-cells 与 #size-cells
在设备树中,每个总线节点通过 #address-cells 和 #size-cells 定义其子节点 reg 属性的地址与长度字段所占的 32 位字(cell)数量。
| 属性 | 含义 | 常见值 | 说明 |
|---|---|---|---|
#address-cells |
地址字段占用的 cell 数 | 1 或 2 | 32 位总线用 1,64 位总线用 2 |
#size-cells |
长度字段占用的 cell 数 | 0 或 1 | 无地址范围设备用 0,有地址范围用 1 |
// 示例:32 位地址空间,带长度
soc {
#address-cells = <1>;
#size-cells = <1>;
uart0: serial@10000000 {
reg = <0x10000000 0x1000>;
};
};
// 示例:64 位地址空间,无长度(如 GPIO 控制器)
gpio0: gpio@f0000000 {
#address-cells = <2>;
#size-cells = <0>;
reg = <0x0 0xf0000000 0x0 0x1000>;
};
编码规则:子节点的
reg 属性中,地址部分占用 #address-cells 个 cell,长度部分占用 #size-cells 个 cell。若 #size-cells = 0,则 reg 只包含地址,不包含长度。
4.2 中断控制器与中断映射属性
设备树使用以下属性描述中断系统:
- interrupt-controller:空属性,标记节点为中断控制器。
- #interrupt-cells:指定中断描述符占用的 cell 数,通常为 1、2 或 3。
- interrupt-parent:指定设备所连接的中断控制器 phandle。
- interrupts:设备的中断描述符列表,格式由父中断控制器定义。
- interrupt-map:用于复杂拓扑(如 PCIe 桥)的中断路由映射。
| 属性 | 作用对象 | 示例值 |
|---|---|---|
interrupt-controller |
中断控制器节点 | 空属性 |
#interrupt-cells |
中断控制器节点 | <2> |
interrupt-parent |
设备节点 | <&gic> |
interrupts |
设备节点 | <0 33 4> |
interrupt-map |
桥接节点 | 多行映射表 |
// GIC 中断控制器定义
gic: interrupt-controller@f0001000 {
compatible = "arm,gic-400";
interrupt-controller;
#interrupt-cells = <3>;
reg = <0xf0001000 0x1000>;
};
// 设备连接 GIC
uart1: serial@10002000 {
compatible = "ns16550";
reg = <0x10002000 0x1000>;
interrupt-parent = <&gic>;
interrupts = <0 37 4>; // SPI, 中断号 37, 高电平触发
};
常见错误:忘记设置
interrupt-parent 或 #interrupt-cells 不匹配会导致中断无法正确路由,内核报错 "no irq domain found"。
4.3 中断映射的完整流程
从设备产生中断到 CPU 响应的设备树映射路径:
- 设备节点通过
interrupt-parent指向中断控制器。 - 设备节点
interrupts属性提供中断类型、编号、触发方式。 - 中断控制器节点解析
interrupts并映射到硬件中断线。 - 内核 irq_domain 建立软件中断号与硬件中断的映射。
- 驱动通过
platform_get_irq()获取中断号并注册处理函数。
4.4 interrupt-map 与复杂拓扑
当存在中断控制器级联或 PCIe 桥等复杂拓扑时,使用 interrupt-map 和 interrupt-map-mask 实现动态路由。
// PCIe 桥中断映射示例
pcie0: pcie@f0000000 {
#address-cells = <3>;
#size-cells = <2>;
interrupt-map-mask = <0xf800 0 0 7>;
interrupt-map = <
0x0000 0 0 1 &gic 0 0 0 33 4
0x0000 0 0 2 &gic 0 0 0 34 4
0x0000 0 0 3 &gic 0 0 0 35 4
0x0000 0 0 4 &gic 0 0 0 36 4
>;
};
映射规则:
interrupt-map 每行包含:子节点地址 + 中断描述符 → 父中断控制器 phandle + 父中断描述符。interrupt-map-mask 用于地址匹配时的掩码过滤。
4.5 实践检查清单
| 检查项 | 正确做法 | 常见错误 |
|---|---|---|
| 地址编码一致性 | 父子节点 #address-cells 匹配 |
子节点 reg 字段数不匹配父节点定义 |
| 中断控制器声明 | 必须包含 interrupt-controller 和 #interrupt-cells |
缺少 #interrupt-cells 导致解析失败 |
| 中断父节点指向 | 设备节点显式指定 interrupt-parent |
依赖隐式继承导致中断域错误 |
| 中断描述符格式 | 与父节点 #interrupt-cells 一致 |
cell 数量不匹配或触发类型值错误 |
| interrupt-map 路由 | 掩码与地址对齐,phandle 有效 | 掩码错误导致中断无法路由 |
调试技巧:使用
dtc -I dtb -O dts 反编译设备树,检查 interrupts 和 interrupt-parent 是否正确;内核启动时查看 dmesg | grep irq 确认中断映射是否成功。
掌握地址编码与中断映射是设备树开发的核心技能,它为后续 overlay 动态配置中断资源打下坚实基础。