linux GIC IRQ(hwirq virq)中断介绍

GIC介绍

        GIC,Generic Interrupt Controller。是ARM公司提供的一个通用的中断控制器。主要作用为:接受硬件中断信号,并经过一定处理后,分发给对应的CPU进行处理。

        前GIC 有四个版本,GIC v1~v4, 本文主要介绍GIC v3控制器。

        GICv3定义了以下中断类型:

        SGI(Software Generated Interrupt):软件触发的中断。软件可以通过写 GICD_SGIR 寄存器来触发一个中断事件,一般用于核间通信,内核中的 IPI:inter-processor interrupts 就是基于 SGI。

        PPI(Private Peripheral Interrupt):私有外设中断。这是每个核心私有的中断。PPI会送达到指定的CPU上,应用场景有CPU本地时钟

        SPI(Shared Peripheral Interrupt):公用的外部设备中断,也定义为共享中断。中断产生后,可以分发到某一个CPU上。比如按键触发一个中断,手机触摸屏触发的中断。

        LPI(Locality-specific Peripheral Interrupt):LPI 是 GICv3 中的新特性,它们在很多方面与其他类型的中断不同。LPI 始终是基于消息的中断,它们的配置保存在表中而不是寄存器。比如 PCIe 的 MSI/MSI-x 中断。       

中断类型硬件中断号
SGI0-15
PPI16-31
SPI32-1019
reserved......

LPI

8192-MAX

                        

        GIC v3 组成

 

 

linux hwirq和virq介绍

        当我们的系统中只有一两个中断控制器时,上面的方法很有用,可以给每一个中断预先确定好他的虚拟中断号,但是当中断控制器越来越多、当中断越来越多,上述方法(virq和hwirq固定绑定)有缺陷:
a. 增加工作量, 你需要给每一个中断确定它的中断号, 写出对应的宏, 可能有成百上千个
b. 你要确保每一个硬件中断对应的中断号互不重复 

新中断体系中, 怎么使用中断:
a.以前是request_irq发起,
  现在是先在设备树文件中声明想使用哪一个中断(哪一个中断控制器下的哪一个中断)

b. 内核解析设备树时,
   会根据"中断控制器"确定irq_domain,
   根据"哪一个中断"确定hwirq, 
   然后在irq_desc数组中找出一个空闲项, 它的位置就是virq
   并且把virq和hwirq的关系保存在irq_domain中: irq_domain.linear_revmap[hwirq] = virq;

c. 驱动程序 request_irq(virq, my_handler)

d. 发生硬件中断时,
内核读取硬件信息, 确定hwirq, 确定 virq =  irq_domain.linear_revmap[hwirq];
然后调用 irq_desc[virq].handle_irq, 最终会用到my_handler

假设要使用子中断控制器(subintc)的n号中断, 它发生时会导致父中断控制器(intc)的m号中断:
a. 设备树表明要使用<subintc n>
   subintc表示要使用<intc m>
b. 解析设备树时,
   会为<subintc n>找到空闲项 irq_desc[virq'], sub irq_domain.linear_revmap[n] = virq';
   
   会为<intc m>   找到空闲项 irq_desc[virq], irq_domain.linear_revmap[m] = virq;
   并且设置它的handle_irq为某个分析函数demux_func

c. 驱动程序 request_irq(virq', my_handler)

d. 发生硬件中断时,
内核读取intc硬件信息, 确定hwirq = m, 确定 virq =  irq_domain.linear_revmap[m];
然后调用 irq_desc[m].handle_irq, 即demux_func

e. demux_func:
读取sub intc硬件信息, 确定hwirq = n, 确定 virq' =  sub irq_domain.linear_revmap[n];
然后调用 irq_desc[n].handle_irq, 即my_handler

include/linux/irqdesc.h

 我们这个处理流程是el1模式的,在el1模式的时候,汇编级别的共用中断函数为el1_irq。图中我们可以看见几个箭头指示的点,1、handle_arch_irq;2、desc->handle_irq;3、irqaction->handler。我们知道,通常ARM体系结构的CPU都和GIC中断控制器相连,详细《ARM中断控制器-GICv2》,因此,handle_arch_irq这个全局函数指针,是有通用的中断控制器GIC驱动设置的(GIC驱动调用set_handle_irq函数设置),这样CPU上的所有异常中断都经过GIC的回调函数处理。GIC的这个回调函数gic_handle_irq(就是handle_arch_irq),负责mask中断和eoi中断(eoi就是ack控制器)。在这个回调函数里面,我们可以通过寄存器值获取到当前控制的那个中断号(该domain的hirq)触发了中断,然后通过hirq可以获取到对应的virq,从而获取到irq_desc描述,最后调用irq_desc描述的handle_irq来处理该中断。由图中,我们还看到handle_irq这个函数其实也是注册的,但是为了方便统一,这个回调通常都是gic驱动为每个中断注册一个统一的回调函数(handle_fasteoi_irq);handle_fasteoi_irq这个函数支持中断共享,然后依次调用挂在irq_desc下面的irqaction结构中的handle函数(玩家每调用依次request_irq就会在对应的virq上增加一个irqaction。

硬件中断号获取和映射过程

 

 

 

 

Linux arm64 request_irq处理流程:

[  212.833039] <0>-(0)[0:swapper/0] dump_backtrace+0x0/0x158
[  212.833766] <0>-(0)[0:swapper/0] show_stack+0x24/0x30
[  212.834452] <0>-(0)[0:swapper/0] dump_stack+0xa0/0xdc
[  212.835144] <0>-(0)[0:swapper/0] spi_gpio_irq_notify+0x20/0x2c [myspi]
[  212.836019] <0>-(0)[0:swapper/0] __handle_irq_event_percpu+0xb0/0x270
[  212.836881] <0>-(0)[0:swapper/0] handle_irq_event_percpu+0x40/0x90
[  212.837708] <0>-(0)[0:swapper/0] handle_irq_event+0x50/0x80
[  212.838459] <0>-(0)[0:swapper/0] handle_level_irq+0x11c/0x140
[  212.839231] <0>-(0)[0:swapper/0] generic_handle_irq+0x2c/0x44
[  212.840007] <0>-(0)[0:swapper/0] xxx_eint_irq_handler+0x1a8/0x278
[  212.840823] <0>-(0)[0:swapper/0] generic_handle_irq+0x2c/0x44
[  212.841595] <0>-(0)[0:swapper/0] __handle_domain_irq+0x1f4/0x444
[  212.842401] <0>-(0)[0:swapper/0] gic_handle_irq+0xcc/0x164
[  212.843137] <0>-(0)[0:swapper/0] el1_irq+0xec/0x198
[  212.843802] <0>-(0)[0:swapper/0] cpuidle_enter_state+0x234/0x330
[  212.844607] <0>-(0)[0:swapper/0] cpuidle_enter+0x34/0x44
[  212.845324] <0>-(0)[0:swapper/0] call_cpuidle+0x64/0x68
[  212.846029] <0>-(0)[0:swapper/0] do_idle+0x1ac/0x238
[  212.846699] <0>-(0)[0:swapper/0] cpu_startup_entry+0x28/0x2c
[  212.847463] <0>-(0)[0:swapper/0] rest_init+0xd0/0xdc
[  212.848138] <0>-(0)[0:swapper/0] start_kernel+0x490/0x4b8

Linux中断查看方法以及interrupts节点生成代码位置

cat proc/interrupts (节点信息生成参考以下位置代码)

----fs/proc/interrupts.c

---kernel/irq/proc.c 

如下为spi的中断,其中比如spi0的硬中断为193,软中断为355

行 2282: [ 2.381698] <0>.(0)[1:swapper/0]spi irq is 322
行 2284: [ 2.384881] <0>.(0)[1:swapper/0]spi irq is 323
行 2286: [ 2.388540] <0>.(0)[1:swapper/0]spi irq is 324

355: 0 0 0 0 GICv3 193 Level 2100001.spi0
356: 0 0 0 0 GICv3 194 Level 31010002.spi1
357: 0 0 0 0 GICv3 195 Level 41010003.spi2

Linux gic irq相关代码

---drivers/irqchip/irq-gic-v3.c

---kernel/irq/irqdomain.c

---drivers/irqchip/irq-gic.c

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a2591748032-随心所记

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值