在内核中断初始化完成并且设备注册了响应的中断后,内核就可以响应相应的中断了。
首先要确定如果一个外设发出中断请求,它到底做了什么,设备是不能直接发出中断的,而是借助中断控制器,设备向中断控制器请求中断,这就是IRQ的来历。
对于龙芯1B处理器来说,中断控制器有四条输出线接在了处理器上,也就是四条中断线,INT0~INT3,每条中断线对应32个中断源,而中断控制器寄存器的位域也就跟这32个中断源一一对应。
每个设备控制器都有一条IRQ输出线和中断控制器寄存器的相应位相连,这样当相应设备控制器发出中断时,中断控制器寄存器相应位就置1,中断控制器就通过相应的中断输出线向内核发出中断,CP0协处理器寄存器cause寄存器的IP0~IP7相应位就置1。
内核响应中断就是跳转到事先设定好的异常向量入口,前面说过,对于龙芯1B内核是在0x80000180,首先读取cause寄存器中的例外码,确定是哪一种通用异常,然后跳转到exception_handler数组相应的处理函数中。对于中断就是数组的0号成员,处理函数是handler_int,执行handler_int,这个函数中是检查status寄存器的全局中断标志位IE是否置1,然后跳转到plat_irq_dispatch,这个函数就是中断的下发函数了,读取出status寄存器的IM0~IM7,读出cause寄存器的IP0~IP7,相与就得到具体是产生哪一种中断并且该中断也是被使能了。
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
// unsigned int pending = read_c0_cause() & read_c0_status() & ST0_IM;
unsigned int cause_reg = read_c0_cause() ;
unsigned int status_reg = read_c0_status() ;
unsigned volatile int cause = cause_reg & ST0_IM;
unsigned volatile int status = status_reg & ST0_IM;
unsigned int pending = cause & status;
uint64_t volatile counter1, counter2;
if (pending & CAUSEF_IP7) {
ll_timer_interrupt(MIPS_CPU_IRQ_BASE+7);
}
else if (pending & CAUSEF_IP2) {
sb2f_board_hw_irqdispatch(0);
}
else if (pending & CAUSEF_IP3) {
sb2f_board_hw_irqdispatch(1);
}
else if (pending & CAUSEF_IP4) {
sb2f_board_hw_irqdispatch(2);
}
else if (pending & CAUSEF_IP5) {
sb2f_board_hw_irqdispatch(3);
}
else if (pending & CAUSEF_IP6) {
sb2f_board_hw_irqdispatch(4);
} else {
spurious_interrupt();
}
}
从这个函数中可以看出INT0~INT3对应的是IP2~IP5,这个函数调用sb2f_board_hw_irqdispatch,读出相应中断线的ISR中断状态寄存器,计算出是哪一位被置1,然后计算出中断号,再调用do_IRQ函数。
void sb2f_board_hw_irqdispatch(int n)
{
int irq;
int intstatus = 0;
int status;
/* Receive interrupt signal, compute the irq */
status = read_c0_cause();
intstatus = (sb2f_board_hw0_icregs+n)->int_isr;
irq=ffs(intstatus);
// prom_printf("irq=%d,n=%d,realirq=%d
",irq,n,n*32+irq-1);
if(!irq){
printk("Unknow interrupt status %x intstatus %x
" , status, intstatus);
return;
}
else do_IRQ(n*32+irq-1);
}
do_IRQ函数