一、GIC介紹
GIC(Generic Interrupt Controller)是ARM公司提供的一個通用的中斷控制器,目前有4個版本GICv1 ~ GICv4(GICv1已棄用;GICv2最多支持8個ARM Core;GICv3/GICv4支持更多的ARM Core)。
GIC的核心功能:對SOC中外設的中斷源的管理,并提供給軟件,配置以及控制這些中斷源。當對應的中斷源有效時,GIC根據(jù)中斷源配置,決定是否將該中斷信號發(fā)給CPU。如果有多個中斷源有效,那么GIC還會進行仲裁,選擇最高優(yōu)先級中斷,發(fā)送給CPU。當CPU接收到GIC發(fā)送的中斷,通過讀取GIC的寄存器,可以知道中斷的來源,從而做對應的處理。當CPU處理完中斷后,會配置GIC寄存器,表示該中斷已處理完畢。GIC接收到信息后,就將該中斷源取消,避免又重新發(fā)送該中斷給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架構(gòu)包括:
1、Distributor
Distributor:SPI外設中斷分發(fā)器,外設經(jīng)特定硬件中斷線連接到Distributor。Distributor判斷SPI中斷的優(yōu)先級,決定優(yōu)先處理哪個中斷,使用中斷重映射表決定中斷的目的PE,同時維護中斷的active/pending/acknowledged狀態(tài)。
2、Redistributor
Redistributor:管理SGI/PPI/LPI中斷,決定他們的優(yōu)先級,觸發(fā)方式,控制他們的狀態(tài),以及enable/disable特定中斷。
3、CPU interface
CPU interface:傳輸中斷給Core。每個Redistributor連接一個CPU interface,它負責打開和關(guān)閉PE的中斷處理能力,acknowledge中斷,為PE維護一個中斷優(yōu)先級掩碼(只響應更高優(yōu)先級中斷),定義中斷搶占策略,執(zhí)行中斷降級。
4、ITS
ITS(Interrupt Translation Service):接收LPI消息中斷,根據(jù)消息攜帶的event id和device id,翻譯得到物理中斷線以及目標PE。ITS與device之間通過系統(tǒng)總線連接,device采用內(nèi)存地址的形式發(fā)一個中斷消息給ITS。
5、 PE
PE(Process element)處理器單元,中斷的最終接收者和處理者。

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

GICv4與GICv3的功能基本相同,增加了直接注入虛擬中斷的能力。
四、GIC中斷類型
GIC中斷類型包括:
1、 SGI(Software Generated Interrupt)
軟中斷,主要用于核間通信,通過寫SGI寄存器產(chǎn)生。
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)
共享外設中斷,外設中斷可以發(fā)送到任何一個連接的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)
特定區(qū)域外設中斷。只在GICv3和GICv4上支持。
不同中斷類型的INITD范圍如下:


注:目前Linux內(nèi)核驅(qū)動中,使用的是IRQ中斷,沒有使用FIQ中斷,同時禁止IRQ中斷嵌套。
五、GIC中斷觸發(fā)方式
GIC中斷觸發(fā)方式包括:
1、edge-triggered interrupt
邊沿觸發(fā)中斷。支持的中斷類型有:SGI/PPI/SPI/LPI。
2、 level-sensitive interrupt
電平觸發(fā)中斷。支持的中斷類型有:PPI/SPI。
注:GICv2/v3 SPI中斷只支持上升沿或高電平觸發(fā)。
代碼如下:
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只支持高電平和上升沿觸發(fā)中斷
/* 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只支持高電平和上升沿觸發(fā)中斷
/* SPIs have restrictions on the supported types */
if (gicirq >= 32 && type != IRQ_TYPE_LEVEL_HIGH &&
type != IRQ_TYPE_EDGE_RISING)
return -EINVAL;
...
}
六、GIC中斷狀態(tài)
GIC中斷狀態(tài)包括:
1、 Inactive
該中斷源當前無效,未發(fā)生中斷。
2、 Pending
已產(chǎn)生中斷,未被PE響應。
3、 Active
該中斷源已經(jīng)發(fā)生并且已被PE響應。
4、 Active and pending
這個中斷已被響應,再一次的中斷正在被pending。
GIC中斷處理狀態(tài)機如下:

七、GIC中斷周期
GIC中斷處理基于GIC中斷生命周期,中斷生命周期為描述中斷處理過程,包括:
1、Generate interrupt
外設或軟件產(chǎn)生一個中斷。
2、Distribute
IRI(Interrupt Router Interface)實現(xiàn)中斷分組、中斷優(yōu)先級控制、并控制中斷轉(zhuǎn)發(fā)到CPU接口。PPI/SGI是各個Core獨有的中斷,不參與目的Core仲裁。SPI是所有Core共享的,根據(jù)配置決定中斷發(fā)往的Core。最后選擇優(yōu)先級最高的中斷發(fā)給CPU interface。寄存器用GICD_做前綴。
3、Deliver
CPU接口將中斷發(fā)給PE。將GICD發(fā)送的中斷信息,通過IRQ、FIRQ引腳傳輸給Core。寄存器使用GICC_做前綴。
4、Activate
PE通過讀取GICC_IAR寄存器識別中斷,設置最高優(yōu)先級的SGI/PPI/SPI中斷為激活狀態(tài)。
5、Priority drop
PE通過配置GICC_EOIR寄存器,實現(xiàn)優(yōu)先級重置。
6、Deactivation
PE通過配置GICC_DIR寄存器,使該中斷無效。
中斷生命周期流程如下:

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