一、GIC介绍
GIC(Generic Interrupt Controller)是ARM公司提供的一个通用的中断控制器,目前有4个版本GICv1 ~ GICv4(GICv1已弃用;GICv2最多支持8个ARM Core;GICv3/GICv4支持更多的ARM Core)。
GIC的核心功能:对SOC中外设的中断源的管理,并提供给软件,配置以及控制这些中断源。当对应的中断源有效时,GIC根据中断源配置,决定是否将该中断信号发给CPU。如果有多个中断源有效,那么GIC还会进行仲裁,选择最高优先级中断,发送给CPU。当CPU接收到GIC发送的中断,通过读取GIC的寄存器,可以知道中断的来源,从而做对应的处理。当CPU处理完中断后,会配置GIC寄存器,表示该中断已处理完毕。GIC接收到信息后,就将该中断源取消,避免又重新发送该中断给CPU,以及允许中断抢占。
二、GIC IP
对于不同的GIC版本,ARM公司设计了对应的GIC IP。
1)GIC-400:支持GICv2版本。
2)GIC-500:支持GICv3版本。
3)GIC-600:支持GICv3版本。
4)GIC-700:支持GICv3/GICv4.1版本。
CPU和GIC之间兼容性见下图:

三、GIC框架
GICv3架构包括:
1、Distributor
Distributor:SPI外设中断分发器,外设经特定硬件中断线连接到Distributor。Distributor判断SPI中断的优先级,决定优先处理哪个中断,使用中断重映射表决定中断的目的PE,同时维护中断的active/pending/acknowledged状态。
2、Redistributor
Redistributor:管理SGI/PPI/LPI中断,决定他们的优先级,触发方式,控制他们的状态,以及enable/disable特定中断。
3、CPU interface
CPU interface:传输中断给Core。每个Redistributor连接一个CPU interface,它负责打开和关闭PE的中断处理能力,acknowledge中断,为PE维护一个中断优先级掩码(只响应更高优先级中断),定义中断抢占策略,执行中断降级。
4、ITS
ITS(Interrupt Translation Service):接收LPI消息中断,根据消息携带的event id和device id,翻译得到物理中断线以及目标PE。ITS与device之间通过系统总线连接,device采用内存地址的形式发一个中断消息给ITS。
5、 PE
PE(Process element)处理器单元,中断的最终接收者和处理者。

GICv2比GICv3少了Redistributor和ITS等功能。

GICv4与GICv3的功能基本相同,增加了直接注入虚拟中断的能力。
四、GIC中断类型
GIC中断类型包括:
1、 SGI(Software Generated Interrupt)
软中断,主要用于核间通信,通过写SGI寄存器产生。
2、PPI(Private Peripheral Interrupt)
私有外设中断,为某个核的私有中断。例(arch/arm64/boot/dts/rockchip/rk3399.dtsi):
# RK3399小核CortexA53 pmu PPI中断
pmu_a53 {
compatible = "arm,cortex-a53-pmu";
interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &ppi_cluster0>;
};
# RK3399大核CortexA72 pmu PPI中断
pmu_a72 {
compatible = "arm,cortex-a72-pmu";
interrupts = <GIC_PPI 7 IRQ_TYPE_LEVEL_LOW &ppi_cluster1>;
};
...
# ARMV8 timer PPI中断
timer {
compatible = "arm,armv8-timer";
interrupts = <GIC_PPI 13 IRQ_TYPE_LEVEL_LOW 0>,
<GIC_PPI 14 IRQ_TYPE_LEVEL_LOW 0>,
<GIC_PPI 11 IRQ_TYPE_LEVEL_LOW 0>,
<GIC_PPI 10 IRQ_TYPE_LEVEL_LOW 0>;
arm,no-tick-in-suspend;
};
3、SPI(Shared Peripheral Interrupt)
共享外设中断,外设中断可以发送到任何一个连接的core。例(arch/arm64/boot/dts/rockchip/rk3399.dtsi):
# RK3399 uart SPI中断
uart0: serial@ff180000 {
compatible = "rockchip,rk3399-uart", "snps,dw-apb-uart";
reg = <0x0 0xff180000 0x0 0x100>;
clocks = <&cru SCLK_UART0>, <&cru PCLK_UART0>;
clock-names = "baudclk", "apb_pclk";
interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH 0>;
reg-shift = <2>;
reg-io-width = <4>;
pinctrl-names = "default";
pinctrl-0 = <&uart0_xfer &uart0_cts &uart0_rts>;
status = "disabled";
};
4、 LPI(Locality-specific Peripheral Interrupt)
特定区域外设中断。只在GICv3和GICv4上支持。
不同中断类型的INITD范围如下:


注:目前Linux内核驱动中,使用的是IRQ中断,没有使用FIQ中断,同时禁止IRQ中断嵌套。
五、GIC中断触发方式
GIC中断触发方式包括:
1、edge-triggered interrupt
边沿触发中断。支持的中断类型有:SGI/PPI/SPI/LPI。
2、 level-sensitive interrupt
电平触发中断。支持的中断类型有:PPI/SPI。
注:GICv2/v3 SPI中断只支持上升沿或高电平触发。
代码如下:
drivers/irqchip/irq-gic-v3.c
static int gic_set_type(struct irq_data *d, unsigned int type)
{
unsigned int irq = gic_irq(d);
void (*rwp_wait)(void);
void __iomem *base;
/* Interrupt configuration for SGIs can't be changed */
if (irq < 16)
return -EINVAL;
## GICv3 SPI只支持高电平和上升沿触发中断
/* SPIs have restrictions on the supported types */
if (irq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
...
}
drivers/irqchip/irq-gic.c
static int gic_set_type(struct irq_data *d, unsigned int type)
{
void __iomem *base = gic_dist_base(d);
unsigned int gicirq = gic_irq(d);
/* Interrupt configuration for SGIs can't be changed */
if (gicirq < 16)
return -EINVAL;
## GICv2 SPI只支持高电平和上升沿触发中断
/* SPIs have restrictions on the supported types */
if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
...
}
六、GIC中断状态
GIC中断状态包括:
1、 Inactive
该中断源当前无效,未发生中断。
2、 Pending
已产生中断,未被PE响应。
3、 Active
该中断源已经发生并且已被PE响应。
4、 Active and pending
这个中断已被响应,再一次的中断正在被pending。
GIC中断处理状态机如下:

七、GIC中断周期
GIC中断处理基于GIC中断生命周期,中断生命周期为描述中断处理过程,包括:
1、Generate interrupt
外设或软件产生一个中断。
2、Distribute
IRI(Interrupt Router Interface)实现中断分组、中断优先级控制、并控制中断转发到CPU接口。PPI/SGI是各个Core独有的中断,不参与目的Core仲裁。SPI是所有Core共享的,根据配置决定中断发往的Core。最后选择优先级最高的中断发给CPU interface。寄存器用GICD_做前缀。
3、Deliver
CPU接口将中断发给PE。将GICD发送的中断信息,通过IRQ、FIRQ引脚传输给Core。寄存器使用GICC_做前缀。
4、Activate
PE通过读取GICC_IAR寄存器识别中断,设置最高优先级的SGI/PPI/SPI中断为激活状态。
5、Priority drop
PE通过配置GICC_EOIR寄存器,实现优先级重置。
6、Deactivation
PE通过配置GICC_DIR寄存器,使该中断无效。
中断生命周期流程如下:

参考:
https://developer.arm.com/documentation/ka002107/1-0
IHI0048B_b_gic_architecture_specification.pdf
IHI0069H_gic_architecture_specification.pdf