快速连接
👉👉👉【精选】ARMv8/ARMv9架构入门到精通-目录 👈👈👈
GICV2
1、gicv2的中断模型
在SOC中,中断产生后,怎么讲信息发送给CPU的呢,如下图所示,画了一个简要说明:
ARM CORE只有4根线用于接受中断,nIRQ、nFIQ、nvIRQ、nvFIQ. 未enable hypevisor时,我们只看nIRQ、nFIQ就可以了;
SOC中的所有中断都接到gic上,然后gic再输出nIRQ、nFIQ、nvIRQ、nvFIQ四根信号给ARM core;
ARM CORE在收到中断信号后,会通过AXI总线去读写GIC的寄存器(软件上是通过memory-map的方式去读写),继而获取是哪个中断号产生的中断。
当一个device需要使用中断,该device会输出一个中断信号线,该中断线会接到gic上,gic中的cpu interface组件中的GICC_IAR寄存器就会跟着发生变化, 例如:
指纹模组产生了一个中断,该中断线接到了gic上,gic收到该中断后,相应的GICC_IAR寄存器就会发送变化。 这些都是由ASIC来设计的,例如我们由一个指纹中断,中断号是59,所谓的该中断号是59,其实就是该引脚产生中断信号后,相应的GICC_IAR寄存器会被写入59数值。
2、gicv2寄存器
(1)、Distributor register
(2)、CPU interface register
3、中断处理的过程
在中断产生后,ARM Core接收到IRQ或FIQ信号会跳转到ARM的IRQ/FIQ异步异常向量表.
该向量表是在linux kernel开机初始化时设置,在该向量表的处理中,会跳转到gicv2的hander函数:gic_handle_irq()
static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqstat, irqnr;
struct gic_chip_data *gic = &gic_data[0];
void __iomem *cpu_base = gic_data_cpu_base(gic);
do {
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
if (likely(irqnr > 15 && irqnr < 1021)) {
if (static_key_true(&supports_deactivate))
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
handle_domain_irq(gic->domain, irqnr, regs);
continue;
}
if (irqnr < 16) {
writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
if (static_key_true(&supports_deactivate))
writel_relaxed(irqstat, cpu_base + GIC_CPU_DEACTIVATE);
#ifdef CONFIG_SMP
/*
* Ensure any shared data written by the CPU sending
* the IPI is read after we've read the ACK register
* on the GIC.
*
* Pairs with the write barrier in gic_raise_softirq
*/
smp_rmb();
handle_IPI(irqnr, regs);
#endif
continue;
}
break;
} while (1);
}
我们剖析一下这段代码:
irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
irqnr = irqstat & GICC_IAR_INT_ID_MASK;
GIC_CPU_INTACK是0X0C,对应gicv2文档中的GICC_IAR,也就是读取gic中的物理中断号
if (likely(irqnr > 15 && irqnr < 1021)) 如果是PPI/SPI中断,则执行handle_domain_irq()
if (irqnr < 16)如果是SGI中断,则会执行后面的handle_IPI()
GICV3
1、gicv3的中断模型
在gicv3中,发生了很大的变化,原gicv2组件中的cpu interface移到了ARM Core中,
ARM Core通过系统寄存器的方式访问cpu interface寄存器(msr/mrs指令),不再是memory-map方式了。
当然了,访问destributor/redestributor还是通过memory-map方式.
gic同ARM Core的接口,也变成了AXI stream.
当有中断进来时,gic组件会通过AXI Stream传输信息给cpu interface,cpu interface仲裁后,再将FIQ/IRQ信号发送给真正的ARM CORE.
2、gicv3的寄存器
gicv3有组件中:detributor、redetributor、its再gicv3中, cpu interface挪到了ARM Core中
gicv3的寄存器有两种访问方式:一类是通过memory-map方式访问、一类是通过系统寄存器方式访问;
- GICD 开头的寄存器是detributor寄存器,只能通过memory-map方式
- GICR 开头的寄存器是redetributor寄存器,只能通过memory-map方式
- GITS 开头的寄存器是its寄存器,只能通过memory-map方式
- GICC 开头的寄存器是cpu interface寄存器,可以通过memory-map方式
- ICC 开头的寄存器是cpu interface寄存器,可以通过系统寄存器方式访问 —— 对于cpu
interface寄存器,我们一般采取这种访问
3、中断处理的过程
在中断产生后,ARM Core接收到IRQ或FIQ信号会跳转到ARM的IRQ/FIQ异步异常向量表.
该向量表是在linux kernel开机初始化时设置,在该向量表的处理中,会跳转到gicv2的hander函数:gic_handle_irq()
static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
u32 irqnr;
do {
irqnr = gic_read_iar();
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
int err;
if (static_key_true(&supports_deactivate))
gic_write_eoir(irqnr);
err = handle_domain_irq(gic_data.domain, irqnr, regs);
if (err) {
WARN_ONCE(true, "Unexpected interrupt received!\n");
if (static_key_true(&supports_deactivate)) {
if (irqnr < 8192)
gic_write_dir(irqnr);
} else {
gic_write_eoir(irqnr);
}
}
continue;
}
if (irqnr < 16) {
gic_write_eoir(irqnr);
if (static_key_true(&supports_deactivate))
gic_write_dir(irqnr);
#ifdef CONFIG_SMP
/*
* Unlike GICv2, we don't need an smp_rmb() here.
* The control dependency from gic_read_iar to
* the ISB in gic_write_eoir is enough to ensure
* that any shared data read by handle_IPI will
* be read after the ACK.
*/
handle_IPI(irqnr, regs);
#else
WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
continue;
}
} while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}
我们剖析一下这段代码:
irqnr = gic_read_iar() 使用mrs指令读取cpu interface寄存器中的ICC_IAR_EL1寄存器,该寄存器记录着物理中断号;
if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) 如果是PPI、SPI、LPI中断
if (irqnr < 16)如果是SGI中断,则会执行后面的handle_IPI()
关注"Arm精选"公众号,备注进ARM交流讨论区。