本文介绍 IMX6ULL的IRQ中断函数实现,具体是汇编实现。对汇编实现过程进行一下初步的了解。
IRQ 中断函数涉及 CP15协处理器 与 GIC中断控制器。
start.S 汇编文件中 IMX6ULL的 IRQ中断函数,代码实现如下:
/* IRQ中断!重点!!!!! */
IRQ_Handler:
push {lr} /* 保存lr地址 */
push {r0-r3, r12} /* 保存r0-r3,r12寄存器 */
mrs r0, spsr /* 读取spsr寄存器 */
push {r0} /* 保存spsr寄存器 */
mrc p15, 4, r1, c15, c0, 0 /* 从CP15的C0寄存器内的值到R1寄存器中
* 参考文档ARM Cortex-A(armV7)编程手册V4.0.pdf P49
* Cortex-A7 Technical ReferenceManua.pdf P68 P138
*/
add r1, r1, #0X2000 /* GIC基地址加0X2000,也就是GIC的CPU接口端基地址 */
ldr r0, [r1, #0XC] /* GIC的CPU接口端基地址加0X0C就是GICC_IAR寄存器,
* GICC_IAR寄存器保存这当前发生中断的中断号,我们要根据
* 这个中断号来绝对调用哪个中断服务函数
*/
push {r0, r1} /* 保存r0,r1 */
cps #0x13 /* 进入SVC模式,允许其他中断再次进去 */
push {lr} /* 保存SVC模式的lr寄存器 */
ldr r2, =system_irqhandler /* 加载C语言中断处理函数到r2寄存器中*/
blx r2 /* 运行C语言中断处理函数,带有一个参数,保存在R0寄存器中 */
pop {lr} /* 执行完C语言中断服务函数,lr出栈 */
cps #0x12 /* 进入IRQ模式 */
pop {r0, r1}
str r0, [r1, #0X10] /* 中断执行完成,写EOIR */
pop {r0}
msr spsr_cxsf, r0 /* 恢复spsr */
pop {r0-r3, r12} /* r0-r3,r12出栈 */
pop {lr} /* lr出栈 */
subs pc, lr, #4 /* 将lr-4赋给pc */
以上代码分析如下:
mrc p15, 4, r1, c15, c0, 0 //读取CP15的CBAR寄存器。
CBAR寄存器保存了GIC控制器的寄存器组首地址。GIC寄存器组偏移0x1000~0x1fff为GIC的分发器。0x2000~0x3fff为CPU接口端。意味我们可以访问GIC控制器了!
代码中,R1寄存器将保存着GIC控制器的CPU接口端基地址。
读取CPU接口端的 GICC_IAR寄存器的值保存到 R0寄存器里面。
可以从GICC_IAR的bit9~0读取中断ID,我们读取中断ID的目的就是为了得到对应的中断处理函数。
system_irqhandler 就是具体的中断处理函数,此函数有一个参数,为 GICC_IAR寄存器的值。
system_irqhandler 处理完具体的中断以后,需要将对应的中断ID 值写入到 GICC_EOIR寄存器里面。
关于如下代码:
subs pc, lr, #4 /* 将lr-4赋给pc */
0X2000 MOV R1, R0 ;执行
0X2004 MOV R2, R3 ;译指
0X2008 MOV R4, R5 ;取值 PC