linux 下ARC的中断机制
一、Idu 中断控制器初始化
Idu 是arc 处理器内部中断控制模块, 类似于arm 内部的gic 中断控制模块
首先,Idu中断控制器在初始化时, 会解析DTS信息中定义了几个idu控制器,每个Idu控制器注册一个struct irq_domain数据结构。
DTS(arch/arc/boot/dts)和中断相关的信息如下:
struct irq_domain用于描述一个中断控制器, 这个结构体很重要, dts 的信息会保存在这里面, 另外这里面还会保存hwirq和virq 的对应关系。
初始化的流程如下:
linux 正常流程会先进入Start_kernel 函数, 然后进入Init_IRQ进行初始化,后面会进入Irqchip_init函数,
__irqchip_of_table 就是内核irq chip table的首地址,这个table也就保存了kernel支持的所有的中断控制器的ID信息(用于和device node的匹
配),这些信息保存在.init.ramfs
section 中, of_irq_init 函数实际上就是根据DTS 的内容去进行搜索匹配,一旦匹配到,就调用该interrupt
controller的初始化函数,并把该中断控制器的device node以及parent中断控制器的device node作为参数传递给irq chip driver。
__irqchip_of_table 的内容是通过IRQCHIP_DECLARE的宏来实现的,如下图所示,这个宏其实就是初始化了一个struct of_device_id的静态
常量,并放置在__irqchip_of_table_end中。
idu_of_init 函数的功能如下:
(a). 读取IDU的最大支持的中断数目
(b). 分配一个 irq_domain 的结构,一个 irq_domain 代表了一个 IDU控制器
©. 在irq_default_domain中,将hwirq和virq进行映射,irq_create_mapping
中domain 参数是NULL, 所以使用default domain, 所以,virq 是等于 hwirq
的,不做转换。然后设置中断响应函数入口为:idu_cascade_isr
二、中断处理流程
处理器正常中断处理程序(handler)一般包含4个步骤:
(1) 保存现场,保存各个寄存器的值
(2) 运行处理中断的需要的命令
(3) 恢复现场,恢复各个寄存器的值
(4) 返回到进行中断处理前的地方继续运行
以 arc 处理器为例, 外部产生中断信号后的流程如下:
首先, 进入异常向量表(vectors), 跳转到相应的异常中断(arch/arc/kernel/entry-arcv2.S),正常会跳转到handle_interrupt
函数, 该函数会进行 保存现场,处理中断和恢复现场 的工作,具体如下:
在arc中 vector table, 如下图所示,前面16个是异常向量表, 后面开始是中断向量表, 都会跳转到handle_interrupt 函数
主要工作分为三个部分:
1、保护现场:
将arc 的寄存器进行压栈处理,需要注意的是,arc 处理器会对大部分寄存器进行自动压栈,但是,仍然有少部分寄存器需要我们
手动压栈(r12, r25, r30, sp, fp, gp,ACCL pair)
2、中断处理:
保存完现场后,就会执行中断跳转函数 arch_do_IRQ
b.d arch_do_IRQ
arch_do_IRQ
函数会把硬件中断号传递给handle_domain_irq
函数, handle_domain_irq
函数内部调用__handle_domain_irq
函数
__handle_domain_irq
函数是中断处理的核心函数, 内部需要关注的函数有四个, 分别如下:
其中,由于触发中断后,不知道domain信息,所以 irq_find_mapping中domain 的参数为NULL,默认在irq_default_domain中根据hwirq找到
virq (Linux的irq), generic_handle_irq参数是irq号,这个是linux 中断号,irq_to_desc()根据irq号找到对应的struct irq_desc。
然后调用irq_desc->handle_irq处理对应的中断, 会先调用idu_cascade_isr的中断处理函数(一级中断函数),这是一个idu提供的标准中断,
后面再调用具体中断函数。
三、初始化&处理具体中断
这里简单举一下pcie中断的例子: