armlinux的中断系统.docx
《armlinux的中断系统.docx》由会员分享,可在线阅读,更多相关《armlinux的中断系统.docx(12页珍藏版)》请在冰豆网上搜索。
armlinux的中断系统
浅析armlinux中断irq分发例程的派发流程之根基文章来源应用程序运行在user模式,对应arm的cpsr&15的值为0,而内核代码运行在svc模式,对应arm的cpsr&15的值为3,所以,如果应用程序在运行期间,即usr模式下,arm发生了irq中断,那么中断处理代码得知后会调用__irq_usr分发处理例程处理irq中断,如果在系统调用syscall之类使程序运行在内核空间执行内核程序的时候,即svc模式下,arm发生了irq中断,那么中断处理代码得知后会调用__irq_svc分发处理例程处理irq中断,中断程序是怎么识别svc和usr模式,进而派发相应的__irq_usr和__irq_svc分发例程的呢,来看看代码部分:
..LCvswi:
.word :
.word :
.word :
.word __temp_abt__stubs_end:
.equ __real_stubs_start, .LCvectors + :
swi SYS_ERROR0 b __real_stubs_start + (vector_undefinstr - __stubs_start) ldr pc, __real_stubs_start + (.LCvswi - __stubs_start) b __real_stubs_start + (vector_prefetch - __stubs_start) b __real_stubs_start + (vector_data - __stubs_start) b __real_stubs_start + (vector_addrexcptn - __stubs_start)..vector_IRQ:
@ @savemodespecificregisters @ ldr r13, .LCsirq ord__irq_usr域和.word__irq_svc域 ldr lr, [pc, lr, lsl #2]ord __irq_usr @0 (USR_26 / USR_32)ord __irq_invalid @1 (FIQ_26 / FIQ_32) .word __irq_invalid @2 (IRQ_26 / IRQ_32) .word __irq_svc @3 (SVC_26 / SVC_32)ord __irq_invalid @4 .word __irq_invalid @5 .word __irq_invalid @6 .word __irq_invalid @7 .word __irq_invalid @8 .word __irq_invalid @9 .word __irq_invalid @a .word __irq_invalid @b .word __irq_invalid @c .word __irq_invalid @d .word __irq_invalid @e .word __irq_invalid @f....LCvswi:
.word :
.word :
.word :
.word __temp_abt
ARMLINUX的中断系统
0 概述
本文描述ARMPXA255系统中断部分的实现原理和代码分析。
1ARM系统异常中断介绍
异常中断种类
ARM支持7类异常中断,所以中断向量表设8个条目,每个条目4字节,共32字节。
异常名称
中断向量
异常中断模式
优先级
复位
0x0
特权模式
1
未定义的指令
0x4
未定义指令中止模式
6
软件中断
0x8
特权模式
6
指令预取中止
0x0c
中止模式
5
数据访问中止
0x10
中止模式
2
保留
0x14
外部中断请求IRQ
0x18
IRQ模式
4
快速中断请求FIQ
0x1c
FIQ模式
3
GPIO引脚
PXA255拥有81个GPIO引脚。
另外各个外围模块有自己的中断线。
ThePXA255processorenablesandcontrolsits81GPIOpinsthroughtheuseof27registerswhichconfigurethepindirection(inputoroutput),pinfunction,pinstate(outputsonly),pinleveldetection(inputsonly),andselectionofalternatefunctions.
由InterruptControllerPendingRegister(ICPR)寄存器可知:
前8个中断保留,其它的都分配相应的外围模块。
且其中8和9是反映GPIO0/1的。
IS10用来指示GPIO2-80是否有中断触发
这样,ARMlinux的IRQ中断数组structirqdescirq_desc[NR_IRQS]中NR_IRQS应该为:
#define NR_IRQS (IRQ_GPIO(80)+1) cvectors处给出了ARM启动时的中断向量表内容。
系统通过R1-R7,ip,lr等8个寄存器做中转,把向量表转存到r0(是传入的参数,即向量表的基址)对应的地址中。
另外,在向量表基址后面512字节处,拷贝了向量表具体的处理代码。
.LCvectors:
swi SYS_ERROR0
b __real_stubs_start+(vector_undefinstr-__stubs_start)
ldr pc,__real_stubs_start+(.LCvswi-__stubs_start)
b __real_stubs_start+(vector_prefetch-__stubs_start)
b __real_stubs_start+(vector_data-__stubs_start)
b __real_stubs_start+(vector_addrexcptn-__stubs_start)
b __real_stubs_start+(vector_IRQ-__stubs_start)
b __real_stubs_start+(vector_FIQ-__stubs_start)
ENTRY(__trap_init)
stmfd sp!
{r4-r6,lr}
adr r1,.LCvectors @setupthevectors
ldmia r1,{r1,r2,r3,r4,r5,r6,ip,lr}
stmia r0,{r1,r2,r3,r4,r5,r6,ip,lr}
add r2,r0,#0x200
adr r0,__stubs_start @copystubsto0x200
adr r1,__stubs_end
1:
ldr r3,[r0],#4
str r3,[r2],#4
cmp r0,r1
blt 1b
LOADREGS(fd,sp!
{r4-r6,pc})
IRQ中断处理流程
区分系统模式
下面我们以IRQ为例,分析中断处理的过程。
根据向量表,其跳转到:
vector_IRQ
把lr值减4后保存到.Lcsirq位置。
再保存spsr的值到.Lcsirq后4字节的位置。
再通过设置spsr_c切换系统到特权模式。
最后一步就是跳转到优先级最高的中断对应的处理函数。
这里要解释一下,
and lr,lr,#15 取出lr(实际是spsr)的低4位。
即M0-M3,也就是原来系统所处的处理器模式。
ldr lr,[pc,lr,lsl#2] 将lr的值乘4后加到PC,再将其所指地址赋给lr。
注意,这里的pc值为当前指针,其加上模式值后,就会指向.LCtab_irq:
表中的对应项。
应该只有用户模式和特权模式会产生IRQ中断,所以就只有两个有效的对应指针:
__irq_usr和__irq_svc。
vector_IRQ:
@
@savemodespecificregisters
@
ldr r13,.LCsirq
sub lr,lr,#4
str lr,[r13] @savelr_IRQ
mrs lr,spsr
str lr,[r13,#4] @savespsr_IRQ
@
@nowbranchtothereleventMODEhandlingroutine
@
mov r13,#I_BIT|MODE_SVC
msr spsr_c,r13 @switchtoSVC_32mode
and lr,lr,#15
ldr lr,[pc,lr,lsl#2]
movs pc,lr @Changesmodeandbranches
.LCtab_irq:
.word __irq_usr @ 0 (USR_26/USR_32)
.word __irq_invalid @ 1 (FIQ_26/FIQ_32)
.word __irq_invalid @ 2 (IRQ_26/IRQ_32)
.word __irq_svc @ 3 (SVC_26/SVC_32)
.word __irq_invalid @ 4
.word __irq_invalid @ 5
.word __irq_invalid @ 6
.LCsirq:
.word __temp_irq
__temp_irq:
.word 0 @savedlr_irq
.word 0 @savedspsr_irq
.word -1 @old_r0
用户模式下的IRQ处理
上一小节根据IRQ发生时系统模式的不同跳转到不同的处理函数。
处理流程如下:
首先从堆栈中留出S_FRAME_SIZE大小的空间,保存了r0-r12.
再把用户模式下的sp,lr值保存起来。
(note:
表面位置在下一句之后,但保存位置却更靠栈顶)。
再把原来保存在.Lcirq中的lr_irq,spsr_irq,old_r0保存到堆栈中。
get_irqnr_and_base是一个宏,负责从中断的寄存器中读取ICIP,ICMR值,再判断是否中断,判断方法是:
首先看是否存在中断,没有返回0。
若系统有中断,判断ICIP的8-15位,若在低位中找到中断,不再查找高位的中断标志。
其中,r0返回最小的中断号,r6等于ICIP&ICMR。
R5是ICMR寄存器的虚拟地址。
Lr没有使用。
由此可以得到:
中断号越小,中断程序越优先执行。
然后,通过对lr的赋值,目的是实现中断的串行化处理。
然后,针对找出的中断号,调用do_IRQ处理函数。
get_current_tasktsk 把sp的低13清0。
tsk .req r9 @currenttask
__irq_usr:
sub sp,sp,#S_FRAME_SIZE Cirq
add r8,sp,#S_PC
ldmia r4,{r5-r7} @getsavedPC,SPSR
stmia r8,{r5-r7} @savepc,psr,old_r0
stmdb r8,{sp,lr}^
alignment_trapr4,r7,__temp_irq
zero_fp
1:
get_irqnr_and_baser0,r6,r5,lr
movne r1,sp @将当前堆栈指针赋给r1
adrsvc ne,lr,1b@通过设置lr,使实现了对中断的串行处理
@
@routinecalledwithr0=irqnumber,r1=structpt_regs*
@
bne do_IRQ
mov why,#0
get_current_tasktsk
b ret_to_user
do_irq处理
主要的处理流程为:
desc=irq_desc+irq; vector_FIQ:
disable_fiq
subs pc,lr,#4
PXA255开发板中断的初始化
start_kernel()函数的初始化过程中,调用init_IRQ()进行开发板具体中断的初始化。
{
for(irq=0;irq irq_desc[irq].probe_ok=0;
irq_desc[irq].valid =0;
irq_desc[irq].noautoenable=0;
irq_desc[irq].mask_ack=dummy_mask_unmask_irq;
irq_desc[irq].mask =dummy_mask_unmask_irq;
irq_desc[irq].unmask =dummy_mask_unmask_irq;
}
init_arch_irq();
init_dma();
}
其中关键函数为:
init_arch_irq();
在/arch/arm/函数中有定义:
init_arch_irq=mdesc->init_irq;
而mdesc->init_irq就是在/match-pxa/中定义的。
staticvoid__initxhyper255_init_irq(void)
{
pxa_init_irq();
set_GPIO_IRQ_edge(0,GPIO_RISING_EDGE); /*EthernetInterrupt*/
#ifdefCONFIG_ARCH_XHYPER255B
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_GPIO_ADS7843),GPIO_FALLING_EDGE);/*ADS7843touchcontroller*/
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_GPIO_EZHOST), GPIO_RISING_EDGE); /*EZ-HostUSBHOSTcontroller*/
#elifdefined(CONFIG_ARCH_XHYPER255A)
set_GPIO_IRQ_edge(IRQ_TO_GPIO_2_80(IRQ_GPIO_ADS7843),GPIO_FALLING_EDGE);/*ADS7843touchcontroller*/
#endif
}
通过调用pxa_init_irq()完成PXA255的中断初始化。
包括:
设置相关寄存器:
/*disableallIRQs*/
ICMR=0; */
for(irq=IRQ_GPIO0;irq<=IRQ_GPIO1;irq++){
irq_desc[irq].valid =0;
irq_desc[irq].probe_ok =1;
irq_desc[irq].mask_ack =pxa_mask_and_ack_GPIO_0_1_irq;
irq_desc[irq].mask =pxa_mask_GPIO_0_1_irq;
irq_desc[irq].unmask =pxa_unmask_GPIO_0_1_irq;
}
for(irq=IRQ_GPIO_2_80;irq<=IRQ_RTCAlrm;irq++){
irq_desc[irq].valid =1;
irq_desc[irq].probe_ok =0;
irq_desc[irq].mask_ack =pxa_mask_irq;
irq_desc[irq].mask =pxa_mask_irq;
irq_desc[irq].unmask =pxa_unmask_irq;
}
/*Thosearereserved*/
irq_desc[PXA_IRQ(15)].valid=0;
irq_desc[PXA_IRQ(16)].valid=0;
for(irq=IRQ_GPIO
(2);irq<=IRQ_GPIO(80);irq++){
irq_desc[irq].valid =0;
irq_desc[irq].probe_ok =1;
irq_desc[irq].mask_ack =pxa_mask_and_ack_GPIO_2_80_irq;
irq_desc[irq].mask =pxa_mask_GPIO_2_80_irq;
irq_desc[irq].unmask =pxa_unmask_GPIO_2_80_irq;
}
设置ARM的ICPR的第10位,用来指示GPIO2-80引脚是否有中断发生。
GPIO[80:
2]EdgeDetectInterruptPending
0–InterruptNOTpendingduetoedgedetectonone(ormore)ofGPIO[80:
2].
1–Interruptpendingduetoedgedetectonone(ormore)ofGPIO[80:
2].
注意,GPIO2-GPIO80这些引脚是没有直接接中断线的,它们是否有边沿触发,完全通过ICPR的第10来判断。
当发现GPIO2-80引脚有中断时,首先触发的是ICPR中的第10位中断,该中断通过调用setu