4、设备树地址与中断映射:地址编码、中断控制器与中断映射规则
设备树(Device Tree)是描述硬件拓扑与资源分配的核心数据结构。地址编码与中断映射是驱动移植中最容易出错的环节之一。本章系统梳理 #address-cells、#size-cells、interrupt-controller 以及中断映射规则,帮助工程师快速定位与修复相关适配问题。
4.1 地址编码:#address-cells 与 #size-cells
设备树中,每个总线节点通过 #address-cells 和 #size-cells 属性定义其子节点地址域的编码宽度。这两个属性共同决定了 reg 属性中地址与长度的解析方式。
| 属性 | 含义 | 常见值 | 说明 |
|---|---|---|---|
#address-cells |
子节点地址字段占用的 32 位单元数 | 1、2 | 1 表示 32 位地址,2 表示 64 位地址 |
#size-cells |
子节点长度字段占用的 32 位单元数 | 0、1 | 0 表示无长度(仅地址),1 表示 32 位长度 |
关键规则: 子节点
reg 属性中,地址部分占用 #address-cells 个 32 位单元,长度部分占用 #size-cells 个 32 位单元。解析顺序为:地址(高到低)→ 长度(高到低)。
示例:32 位地址空间
soc {
#address-cells = <1>;
#size-cells = <1>;
uart@10000000 {
reg = <0x10000000 0x1000>;
// 地址: 0x10000000, 长度: 0x1000 (4KB)
};
};
示例:64 位地址空间
pcie@40000000 {
#address-cells = <3>;
#size-cells = <2>;
pci-ep@0,0 {
reg = <0x0000 0x0 0x0 0x0 0x0>;
// 地址: 3 个单元 (配置空间类型+组+编号), 长度: 2 个单元
};
};
常见错误: 当父节点
#address-cells 为 2 时,子节点 reg 中地址部分必须写满 2 个 32 位单元,否则解析器会错位读取,导致地址或长度异常。
4.2 中断控制器:interrupt-controller 与 #interrupt-cells
中断控制器节点通过 interrupt-controller 属性声明自身为中断控制器,并通过 #interrupt-cells 指定中断描述符所需的单元数。
| 属性 | 类型 | 说明 |
|---|---|---|
interrupt-controller |
空属性 | 标记该节点为中断控制器 |
#interrupt-cells |
u32 | 中断描述符占用的 32 位单元数 |
interrupt-parent |
phandle | 指定设备连接的中断控制器 |
interrupts |
u32 数组 | 中断描述符,格式由 #interrupt-cells 定义 |
典型中断描述符格式:
- ARM GIC (通用中断控制器):
#interrupt-cells = <3>,格式为<中断类型 中断号 触发方式> - 简单中断控制器:
#interrupt-cells = <2>,格式为<中断号 触发方式> - GPIO 中断:
#interrupt-cells = <2>,格式为<GPIO 引脚 触发方式>
示例:ARM GIC 中断映射
gic: interrupt-controller@f0001000 {
compatible = "arm,gic-400";
reg = <0xf0001000 0x1000>;
interrupt-controller;
#interrupt-cells = <3>;
};
uart@10000000 {
compatible = "ns16550";
reg = <0x10000000 0x1000>;
interrupt-parent = <&gic>;
interrupts = <0 33 4>;
// 0: SPI 类型, 33: 中断号, 4: 高电平触发
};
4.3 中断映射规则与流程
中断映射遵循“设备 → 中断控制器 → 中断控制器 → … → CPU”的链式结构。每个设备通过 interrupt-parent 指向其直接中断控制器,中断控制器之间通过 interrupt-map 和 interrupt-map-mask 实现层级映射。
排查要点: 检查中断链中每个节点的
#interrupt-cells 是否与 interrupts 描述符的单元数一致。不一致会导致中断号解析错误。
中断映射核心步骤:
- 设备节点读取
interrupt-parent,找到直接中断控制器。 - 根据中断控制器的
#interrupt-cells解析interrupts描述符。 - 如果中断控制器本身是子控制器,通过
interrupt-map向上级控制器映射。 - 最终到达根中断控制器(如 GIC),生成 CPU 可响应的中断号。
4.4 中断映射常见错误与排查
| 错误类型 | 现象 | 根因 | 解决方案 |
|---|---|---|---|
#interrupt-cells 不匹配 |
中断号解析为 0 或异常大值 | 设备 interrupts 单元数与控制器声明不一致 |
核对控制器 #interrupt-cells,调整 interrupts 数组 |
缺少 interrupt-parent |
设备无法触发中断 | 设备未指定父中断控制器 | 添加 interrupt-parent = <&控制器节点> |
| 中断号冲突 | 多个设备共享同一中断号导致误触发 | 设备树中中断号分配重叠 | 检查 SoC 手册,使用唯一中断号 |
| 触发方式错误 | 中断丢失或频繁误触发 | interrupts 中触发类型与实际硬件不匹配 |
确认硬件触发方式(电平/边沿、高/低) |
| 中断映射链断裂 | 中断无法传递到 CPU | 中间控制器 interrupt-map 配置错误 |
逐级检查 interrupt-map 和 interrupt-map-mask |
4.5 地址与中断联合排查案例
案例:PCIe 设备中断映射失败
某平台 PCIe 控制器下挂载的网卡无法触发中断。排查过程如下:
- 检查 PCIe 控制器节点:
#address-cells = <3>,#size-cells = <2>,interrupt-map存在。 - 检查
interrupt-map中父中断描述符:父控制器为 GIC,#interrupt-cells = <3>,但映射表中只写了 2 个单元。 - 修正映射表,补全中断类型字段(如
<0 33 4>),中断恢复正常。
排查口诀: 地址看 cells,中断看 parent;映射逐级查,单元数要对齐。
4.6 地址与中断映射的协同关系
地址编码与中断映射在设备树中并非孤立存在。许多总线(如 PCIe、AHB)同时定义了地址域和中断域。驱动移植时需同时验证:
- 地址域:
reg属性中的地址与长度是否与父节点#address-cells/#size-cells一致。 - 中断域:
interrupts描述符是否与中断控制器#interrupt-cells匹配。 - 跨域映射: 某些总线节点(如 PCIe)使用
ranges进行地址转换,同时使用interrupt-map进行中断转换,两者需独立验证。
4.7 本章小结
- 地址编码: 父节点
#address-cells和#size-cells决定子节点reg的解析方式,必须严格对齐。 - 中断控制器:
interrupt-controller声明控制器身份,#interrupt-cells定义描述符格式。 - 中断映射: 通过
interrupt-parent和interrupt-map形成链式结构,逐级解析到 CPU。 - 排查重点: 单元数匹配、中断号唯一性、触发方式正确性、映射链完整性。
工程建议: 在设备树调试阶段,使用
dtc -I dtb -O dts 反编译 DTB,人工检查每个节点的 reg 和 interrupts 字段是否符合父节点声明。对于复杂总线(如 PCIe),建议使用 lspci -vvv 配合设备树交叉验证中断路由。