ARM-Linux s3c2440 之中断分析(三)
- intset_irq_chip(unsignedintirq,structirq_chip*chip)
- staticinlinevoidset_irq_handler(unsignedintirq,irq_flow_handler_thandle)
- staticinlinevoidset_irq_chained_handler(unsignedintirq,irq_flow_handler_thandle)
实现的代码如下:
- for(irqno=IRQ_EINT4t7;irqno<=IRQ_ADCPARENT;irqno++){
- /*setallthes3c2410internalirqs*/
- switch(irqno){
- /*dealwiththespecialIRQs(cascaded)*/
- caseIRQ_EINT4t7:
- caseIRQ_EINT8t23:
- caseIRQ_UART0:
- caseIRQ_UART1:
- caseIRQ_UART2:
- caseIRQ_ADCPARENT:
- set_irq_chip(irqno,&s3c_irq_level_chip);
- set_irq_handler(irqno,handle_level_irq);//电平触发型
- break;
- caseIRQ_RESERVED6:
- caseIRQ_RESERVED24:
- /*noIRQhere*/
- break;
- default:
- //irqdbf("registeringirq%d(s3cirq)n",irqno);
- set_irq_chip(irqno,&s3c_irq_chip);
- set_irq_handler(irqno,handle_edge_irq);//边缘触发型
- set_irq_flags(irqno,IRQF_VALID);
- }
- /*级联中断的注册*/
- set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7);
- set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8);
- set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0);
- set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1);
- set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2);
- set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc);
- /*externalinterrupts*/
- for(irqno=IRQ_EINT0;irqno<=IRQ_EINT3;irqno++){
- irqdbf("registeringirq%d(extint)n",irqno);
- set_irq_chip(irqno,&s3c_irq_eint0t4);
- set_irq_handler(irqno,handle_edge_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
- for(irqno=IRQ_EINT4;irqno<=IRQ_EINT23;irqno++){
- irqdbf("registeringirq%d(extendeds3cirq)n",irqno);
- set_irq_chip(irqno,&s3c_irqext_chip);
- set_irq_handler(irqno,handle_edge_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
- /*registertheuartinterrupts*/
- irqdbf("s3c2410:registeringexternalinterruptsn");
- for(irqno=IRQ_S3CUART_RX0;irqno<=IRQ_S3CUART_ERR0;irqno++){
- irqdbf("registeringirq%d(s3cuart0irq)n",irqno);
- set_irq_chip(irqno,&s3c_irq_uart0);
- set_irq_handler(irqno,handle_level_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
- for(irqno=IRQ_S3CUART_RX1;irqno<=IRQ_S3CUART_ERR1;irqno++){
- irqdbf("registeringirq%d(s3cuart1irq)n",irqno);
- set_irq_chip(irqno,&s3c_irq_uart1);
- set_irq_handler(irqno,handle_level_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
- for(irqno=IRQ_S3CUART_RX2;irqno<=IRQ_S3CUART_ERR2;irqno++){
- irqdbf("registeringirq%d(s3cuart2irq)n",irqno);
- set_irq_chip(irqno,&s3c_irq_uart2);
- set_irq_handler(irqno,handle_level_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
- for(irqno=IRQ_TC;irqno<=IRQ_ADC;irqno++){//具体注册IRQ_TC、IRQ_ADC
- irqdbf("registeringirq%d(s3cadcirq)n",irqno);
- set_irq_chip(irqno,&s3c_irq_adc);
- set_irq_handler(irqno,handle_edge_irq);
- set_irq_flags(irqno,IRQF_VALID);
- }
从以上代码中可以看出,注册中断主要是注册中断服务程序入口。Linux中将所有的中断号用一个stuctirq_desc数据结构进行统一管理,每个中断号或者一组中断号(如级联的中断),对应一个structirq_desc, 所以根据相应的中断号,可以获取对应的中断描述结构irq_desc:
- structirq_desc*desc=irq_to_desc(irq);
- irq_desc数据结构如下:
- structirq_desc{
- unsignedintirq;//中断号
- …
- irq_flow_handler_thandle_irq;//系统中断处理的入口函数
- structirq_chip*chip;//对应的irq_chip结构,定义了与中断处理有关的函数
- …
- structirqaction*action;//用户的中断处理函数,调用request_irq时添加
- unsignedintstatus;//IRQ的状态
- …
- spinlock_tlock;
- …
- constchar*name;
- }____cacheline_internodealigned_in_smp;
irq_chip结构定义了各种中断相关的处理行为,如开启或禁止中断以及中断服务完成的之后对相关的中断寄存器进行处理。关于电平触发型和边缘型触发中断入口函数可以从irq_chip结构中的看出只有ack函数的处理不同:
- structirq_chips3c_irq_level_chip={
- .name="s3c-level",
- .ack=s3c_irq_maskack,
- .mask=s3c_irq_mask,
- .unmask=s3c_irq_unmask,
- .set_wake=s3c_irq_wake
- };
- structirq_chips3c_irq_chip={
- .name="s3c",
- .ack=s3c_irq_ack,
- .mask=s3c_irq_mask,
- .unmask=s3c_irq_unmask,
- .set_wake=s3c_irq_wake
- };
在s3cirq_maskack中多了以下代码:
mask = __raw_readl(S3C2410_INTMSK);
__raw_writel(mask|bitval, S3C2410_INTMSK);
实现的功能是,屏蔽相应的中断。
在early_trap_init()中已经进行了中断(异常)向量的初始化,将异常向量表从物理地址0x00000000拷贝到虚拟0xffff0000的虚拟地址处。异常向量在arch/arm/kernel/entry-armv.S中定义:
- .globl__vectors_start
- rs_start:
- swiSYS_ERROR0
- bvector_und+stubs_offset
- ldrpc,.LCvswi+stubs_offset
- bvector_pabt+stubs_offset
- bvector_dabt+stubs_offset
- bvector_addrexcptn+stubs_offset
- bvector_irq+stubs_offset
- bvector_fiq+stubs_offset
- .globl__vectors_end
那么当有中断产生时:
- /*
- *Interrupthandling.Preservesr7,r8,r9
- */
- .macroirq_handler
- get_irqnr_preambler5,lr
- 1:get_irqnr_and_baser0,r6,r5,lr
- movner1,sp
- @
- @routinecalledwithr0=irqnumber,r1=structpt_regs*
- @
- adrnelr,1b
- bneasm_do_IRQ//跳转到这里中断的总入口函数
这里的asm_do_IRQ对应arch/arm/kernel/irq.c中:
- asmlinkagevoid__exceptionasm_do_IRQ(unsignedintirq,structpt_regs*regs)
所以这是Linux中所有中断的总入口函数。asm_doIRQ()调用generic_handle_irq()再后调用
generic_handle_irq_desc(),最后到各个irq_desc的处理。
- staticinlinevoidgeneric_handle_irq_desc(unsignedintirq,structirq_desc*desc)
- {
- #ifdefCONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
- desc->handle_irq(irq,desc);
- #else
- if(likely(desc->handle_irq))
- desc->handle_irq(irq,desc);
- else
- __do_IRQ(irq);
- #endif
- }
从总入口到用户的中断处理函数的流程:
asm_do_IRQ() –-> generic_handle_irq() –->irq_dsc->handle() à handle_IRQ_event() à irq_des->action()
最后对中断处理流程进行简单总结:
(1)总入口函数asm_do_IRQ,获取中断号irq
(2)asm_do_IRQ根据中断号调用各中断号所注册的中断入口函数irq_desc[irq]->handle_irq
(3)最后在中断入口函数中调用handle_IRQ_event()依次执行用户的中断处理函数action
评论