2. 中断控制器与GIC:GIC架构、中断分发、中断路由、SPI/PPI/SGI
2.1 GIC在中断子系统中的位置
通用中断控制器(Generic Interrupt Controller, GIC)是ARM架构中管理中断的核心硬件单元。它位于外设与CPU核心之间,负责中断的收集、优先级排序、分发与路由。驱动开发者理解GIC架构,是正确注册中断、配置触发方式、保证并发安全的前提。
核心认知: GIC不是“一根线”,而是一个包含分发器(Distributor)、CPU接口(CPU Interface)和虚拟化支持(VGIC)的复杂状态机。中断的“注册”本质上是配置GIC的路由表与CPU接口的使能位。
2.2 GIC架构演进与版本对比
| 特性 | GICv2 | GICv3 | GICv4 |
|---|---|---|---|
| CPU接口 | 内存映射(MMIO) | 系统寄存器(SysReg) | 系统寄存器 + 直接注入 |
| 中断路由 | 固定路由到指定CPU | 支持亲和性路由(Affinity Routing) | 支持虚拟LPI直接注入 |
| 最大中断数 | 最多1020个SPI | 几乎无限(基于ITS) | 几乎无限 + 虚拟中断 |
| 虚拟化支持 | 基础VGIC | 增强VGIC + 虚拟LPI | 直接注入(Direct Injection) |
| 驱动关注点 | irq_domain + 静态映射 | irq_domain + ITS + MSI | irq_domain + 虚拟中断映射 |
驱动提示: 当前主流嵌入式平台(如Cortex-A72/A76)多采用GICv3。驱动代码中通过
#ifdef CONFIG_ARM_GIC_V3 区分版本,中断号分配逻辑在GICv3中依赖 irq_domain 的线性映射。
2.3 GIC内部架构与中断流
GIC由三个主要逻辑单元组成:Distributor(分发器)、CPU Interface(CPU接口)和ITS(中断翻译服务,GICv3+)。中断从外设产生到CPU处理的完整路径如下:
- 外设断言中断线 → 进入Distributor的SPI/PPI输入。
- Distributor 检查中断使能、优先级、目标CPU掩码。
- Distributor 将最高优先级的中断转发给目标CPU的 CPU Interface。
- CPU Interface 向CPU核心发送IRQ信号,CPU跳转到中断向量表。
- CPU读取
ICC_IAR0_EL1(GICv3)获取中断ID,进入Linux中断处理流程。
2.4 中断类型:SPI、PPI、SGI
GIC将中断分为三种类型,驱动开发者在 device tree 或 ACPI 中描述中断时,必须明确指定类型和编号。
| 类型 | 全称 | 中断ID范围 | 目标CPU | 典型用途 | 驱动关注点 |
|---|---|---|---|---|---|
| SPI | Shared Peripheral Interrupt | 32 ~ 1019 (GICv2) 32 ~ 1023 (GICv3) |
可路由到任意CPU或CPU组 | 外设中断(UART、I2C、DMA、网卡) | 设备树 interrupts = <0 33 4>;第一个参数0表示SPI |
| PPI | Private Peripheral Interrupt | 16 ~ 31 | 仅属于单个CPU私有 | 本地定时器、PMU、GIC本地中断 | 设备树 interrupts = <1 13 4>;第一个参数1表示PPI |
| SGI | Software Generated Interrupt | 0 ~ 15 | 由软件写入ICDSGIR触发, 可定向到指定CPU |
核间通信(IPI)、 CPU唤醒、调度 |
Linux使用SGI实现 smp_call_function;驱动一般不直接使用 |
常见错误: 在设备树中错误地将PPI中断号(如29)写为SPI类型(第一个参数写0),会导致GIC分发器无法正确路由中断,驱动
request_irq 返回 -EINVAL。务必核对SoC手册中中断号与类型的对应关系。
2.5 中断分发与路由机制
GIC的分发与路由是中断到达正确CPU的关键。GICv2使用静态路由表,GICv3引入亲和性路由,支持更灵活的中断分发策略。
2.5.1 GICv2 分发流程
- Distributor 维护每个SPI的
ICDIPTRn寄存器,指定目标CPU掩码(8位掩码,每位对应一个CPU)。 - 当多个CPU共享一个SPI时,GIC使用 轮询(Round-Robin) 或 最低优先级 策略选择目标CPU。
- PPI固定路由到其所属CPU,SGI由发送端指定目标CPU列表。
2.5.2 GICv3 亲和性路由
- 使用
GICR_TYPER和GICD_IROUTERn寄存器,支持 8位亲和性(PE:PE Group:Cluster:Core)。 - 支持 中断分组:Group 0(安全中断,EL3处理)、Group 1(非安全中断,EL1/EL2处理)。
- 驱动通过
irq_set_affinity动态调整中断亲和性,实现负载均衡。
2.6 中断ID与irq_domain映射
Linux内核使用 irq_domain 将硬件中断ID(hwirq)映射到Linux中断号(irq)。GIC驱动注册 irq_domain 时,根据中断类型建立不同的映射策略。
/* GICv3 irq_domain 注册示例(drivers/irqchip/irq-gic-v3.c) */
static int gicv3_populate_rdist(struct device_node *node)
{
/* 为每个CPU Interface分配GICR基地址 */
/* 注册PPI/SGI的irq_domain */
domain = irq_domain_add_linear(node, 16 + 16, /* 16 SGI + 16 PPI */
&gicv3_ppi_sgi_domain_ops, NULL);
/* 注册SPI的irq_domain */
domain = irq_domain_add_linear(node, gic_data.rdists.spi_nr,
&gicv3_spi_domain_ops, NULL);
/* 注册ITS的irq_domain(用于MSI/LPI) */
domain = irq_domain_add_tree(node, &gicv3_its_domain_ops, NULL);
}
映射关系速查:
- SGI: hwirq 0~15 → Linux irq 通常从 32 开始(取决于平台)
- PPI: hwirq 16~31 → Linux irq 紧接SGI之后
- SPI: hwirq 32~1019 → Linux irq 从 32+16+16=64 开始(典型值)
- LPI: hwirq 8192+ → 通过ITS动态分配,使用irq_domain_tree
2.7 中断触发类型与配置
GIC支持四种中断触发类型,驱动在设备树或 irq_set_type 中配置。错误配置会导致中断丢失或误触发。
| 触发类型 | 设备树标志 | GIC配置寄存器 | 适用场景 | 注意事项 |
|---|---|---|---|---|
| 电平高有效 | IRQ_TYPE_LEVEL_HIGH (4) |
ICDICFRn 配置为电平触发 | 共享中断线、需要电平检测的外设 | 必须确保中断处理完成前外设释放电平,否则死锁 |
| 电平低有效 | IRQ_TYPE_LEVEL_LOW (8) |
ICDICFRn 配置为电平触发 + 极性反转 | 低电平有效的外设(如某些GPIO控制器) | 同上,电平释放时机关键 |
| 边沿上升沿 | IRQ_TYPE_EDGE_RISING (1) |
ICDICFRn 配置为边沿触发 | 按键、网络包到达、DMA完成 | 边沿触发不会锁存,丢失后无法恢复 |
| 边沿下降沿 | IRQ_TYPE_EDGE_FALLING (2) |
ICDICFRn 配置为边沿触发 + 极性反转 | 下降沿有效的外设 | 同上,边沿触发需确保信号完整性 |
电平触发陷阱: 电平触发中断在
handle_level_irq 中会 屏蔽中断线 直到处理完成。如果驱动在中断上下文中调用 disable_irq_nosync 后未正确重新使能,会导致该中断线永久关闭。边沿触发则使用 handle_edge_irq,不会屏蔽中断线,但可能丢失快速连续边沿。
2.8 中断优先级与抢占
GIC支持256级优先级(GICv2为16级,GICv3扩展为256级)。高优先级中断可以抢占低优先级中断的处理。驱动通过 irq_set_priority 设置优先级,但Linux内核默认将所有中断优先级设为相同值(通常为0xA0),关闭抢占以简化并发模型。
/* 设置中断优先级示例(ARM64汇编级操作) */
static void gic_set_priority(struct irq_data *d, u32 priority)
{
/* GICv3: 写入ICC_PMR_EL1 或 GICR_IPRIORITYR */
if (gic_irq(d)->type == SPI) {
write_gicreg(priority, GICD_IPRIORITYR + d->hwirq * 4);
} else {
/* PPI/SGI 使用GICR_IPRIORITYR */
write_gicreg(priority, GICR_IPRIORITYR + (d->hwirq & 0x1F) * 4);
}
}
驱动建议: 除非有严格的实时性要求(如PREEMPT_RT),否则不要修改默认优先级。修改优先级可能导致中断嵌套,增加并发竞态风险。如果必须使用,请配合
raw_spin_lock 保护共享数据。
2.9 本章小结
- GIC架构 是中断子系统的硬件基础,Distributor负责路由,CPU Interface负责向CPU递交中断。
- SPI/PPI/SGI 三种中断类型决定了中断的路由范围、ID分配和驱动配置方式。
- 中断路由 在GICv3中通过亲和性寄存器实现动态分发,驱动可使用
irq_set_affinity实现负载均衡。 - irq_domain 是连接硬件中断ID与Linux中断号的桥梁,理解映射关系是正确注册中断的前提。
- 触发类型与优先级 的配置直接影响中断的可靠性和并发行为,电平触发需特别注意释放时机。