新聞中心

EEPW首頁 > 嵌入式系統(tǒng) > 設計應用 > ARM-Linux s3c2440 之中斷分析(三)

ARM-Linux s3c2440 之中斷分析(三)

作者: 時間:2016-11-19 來源:網絡 收藏
Linux通過以下函數(shù)來注冊中斷以及中斷相關的入口函數(shù)handle,只有先注冊IRQ,才能正常使用。

本文引用地址:http://2s4d.com/article/201611/318130.htm
  1. intset_irq_chip(unsignedintirq,structirq_chip*chip)
  2. staticinlinevoidset_irq_handler(unsignedintirq,irq_flow_handler_thandle)
  3. staticinlinevoidset_irq_chained_handler(unsignedintirq,irq_flow_handler_thandle)

實現(xiàn)的代碼如下:


  1. for(irqno=IRQ_EINT4t7;irqno<=IRQ_ADCPARENT;irqno++){
  2. /*setallthes3c2410internalirqs*/
  3. switch(irqno){
  4. /*dealwiththespecialIRQs(cascaded)*/
  5. caseIRQ_EINT4t7:
  6. caseIRQ_EINT8t23:
  7. caseIRQ_UART0:
  8. caseIRQ_UART1:
  9. caseIRQ_UART2:
  10. caseIRQ_ADCPARENT:
  11. set_irq_chip(irqno,&s3c_irq_level_chip);
  12. set_irq_handler(irqno,handle_level_irq);//電平觸發(fā)型
  13. break;
  14. caseIRQ_RESERVED6:
  15. caseIRQ_RESERVED24:
  16. /*noIRQhere*/
  17. break;
  18. default:
  19. //irqdbf("registeringirq%d(s3cirq)n",irqno);
  20. set_irq_chip(irqno,&s3c_irq_chip);
  21. set_irq_handler(irqno,handle_edge_irq);//邊緣觸發(fā)型
  22. set_irq_flags(irqno,IRQF_VALID);
  23. }
  24. /*級聯(lián)中斷的注冊*/
  25. set_irq_chained_handler(IRQ_EINT4t7,s3c_irq_demux_extint4t7);
  26. set_irq_chained_handler(IRQ_EINT8t23,s3c_irq_demux_extint8);
  27. set_irq_chained_handler(IRQ_UART0,s3c_irq_demux_uart0);
  28. set_irq_chained_handler(IRQ_UART1,s3c_irq_demux_uart1);
  29. set_irq_chained_handler(IRQ_UART2,s3c_irq_demux_uart2);
  30. set_irq_chained_handler(IRQ_ADCPARENT,s3c_irq_demux_adc);
  31. /*externalinterrupts*/
  32. for(irqno=IRQ_EINT0;irqno<=IRQ_EINT3;irqno++){
  33. irqdbf("registeringirq%d(extint)n",irqno);
  34. set_irq_chip(irqno,&s3c_irq_eint0t4);
  35. set_irq_handler(irqno,handle_edge_irq);
  36. set_irq_flags(irqno,IRQF_VALID);
  37. }
  38. for(irqno=IRQ_EINT4;irqno<=IRQ_EINT23;irqno++){
  39. irqdbf("registeringirq%d(extendeds3cirq)n",irqno);
  40. set_irq_chip(irqno,&s3c_irqext_chip);
  41. set_irq_handler(irqno,handle_edge_irq);
  42. set_irq_flags(irqno,IRQF_VALID);
  43. }
  44. /*registertheuartinterrupts*/
  45. irqdbf("s3c2410:registeringexternalinterruptsn");
  46. for(irqno=IRQ_S3CUART_RX0;irqno<=IRQ_S3CUART_ERR0;irqno++){
  47. irqdbf("registeringirq%d(s3cuart0irq)n",irqno);
  48. set_irq_chip(irqno,&s3c_irq_uart0);
  49. set_irq_handler(irqno,handle_level_irq);
  50. set_irq_flags(irqno,IRQF_VALID);
  51. }
  52. for(irqno=IRQ_S3CUART_RX1;irqno<=IRQ_S3CUART_ERR1;irqno++){
  53. irqdbf("registeringirq%d(s3cuart1irq)n",irqno);
  54. set_irq_chip(irqno,&s3c_irq_uart1);
  55. set_irq_handler(irqno,handle_level_irq);
  56. set_irq_flags(irqno,IRQF_VALID);
  57. }
  58. for(irqno=IRQ_S3CUART_RX2;irqno<=IRQ_S3CUART_ERR2;irqno++){
  59. irqdbf("registeringirq%d(s3cuart2irq)n",irqno);
  60. set_irq_chip(irqno,&s3c_irq_uart2);
  61. set_irq_handler(irqno,handle_level_irq);
  62. set_irq_flags(irqno,IRQF_VALID);
  63. }
  64. for(irqno=IRQ_TC;irqno<=IRQ_ADC;irqno++){//具體注冊IRQ_TC、IRQ_ADC
  65. irqdbf("registeringirq%d(s3cadcirq)n",irqno);
  66. set_irq_chip(irqno,&s3c_irq_adc);
  67. set_irq_handler(irqno,handle_edge_irq);
  68. set_irq_flags(irqno,IRQF_VALID);
  69. }

從以上代碼中可以看出,注冊中斷主要是注冊中斷服務程序入口。Linux中將所有的中斷號用一個stuctirq_desc數(shù)據結構進行統(tǒng)一管理,每個中斷號或者一組中斷號(如級聯(lián)的中斷),對應一個structirq_desc, 所以根據相應的中斷號,可以獲取對應的中斷描述結構irq_desc:


  1. structirq_desc*desc=irq_to_desc(irq);
  2. irq_desc數(shù)據結構如下:
  3. structirq_desc{
  4. unsignedintirq;//中斷號
  5. irq_flow_handler_thandle_irq;//系統(tǒng)中斷處理的入口函數(shù)
  6. structirq_chip*chip;//對應的irq_chip結構,定義了與中斷處理有關的函數(shù)
  7. structirqaction*action;//用戶的中斷處理函數(shù),調用request_irq時添加
  8. unsignedintstatus;//IRQ的狀態(tài)
  9. spinlock_tlock;
  10. constchar*name;
  11. }____cacheline_internodealigned_in_smp;

irq_chip結構定義了各種中斷相關的處理行為,如開啟或禁止中斷以及中斷服務完成的之后對相關的中斷寄存器進行處理。關于電平觸發(fā)型和邊緣型觸發(fā)中斷入口函數(shù)可以從irq_chip結構中的看出只有ack函數(shù)的處理不同:


  1. structirq_chips3c_irq_level_chip={
  2. .name="s3c-level",
  3. .ack=s3c_irq_maskack,
  4. .mask=s3c_irq_mask,
  5. .unmask=s3c_irq_unmask,
  6. .set_wake=s3c_irq_wake
  7. };
  8. structirq_chips3c_irq_chip={
  9. .name="s3c",
  10. .ack=s3c_irq_ack,
  11. .mask=s3c_irq_mask,
  12. .unmask=s3c_irq_unmask,
  13. .set_wake=s3c_irq_wake
  14. };

在s3cirq_maskack中多了以下代碼:

mask = __raw_readl(S3C2410_INTMSK);

__raw_writel(mask|bitval, S3C2410_INTMSK);

實現(xiàn)的功能是,屏蔽相應的中斷。

在early_trap_init()中已經進行了中斷(異常)向量的初始化,將異常向量表從物理地址0x00000000拷貝到虛擬0xffff0000的虛擬地址處。異常向量在arch/arm/kernel/entry-armv.S中定義:


  1. .globl__vectors_start
  2. rs_start:
  3. swiSYS_ERROR0
  4. bvector_und+stubs_offset
  5. ldrpc,.LCvswi+stubs_offset
  6. bvector_pabt+stubs_offset
  7. bvector_dabt+stubs_offset
  8. bvector_addrexcptn+stubs_offset
  9. bvector_irq+stubs_offset
  10. bvector_fiq+stubs_offset
  11. .globl__vectors_end

那么當有中斷產生時:

  1. /*
  2. *Interrupthandling.Preservesr7,r8,r9
  3. */
  4. .macroirq_handler
  5. get_irqnr_preambler5,lr
  6. 1:get_irqnr_and_baser0,r6,r5,lr
  7. movner1,sp
  8. @
  9. @routinecalledwithr0=irqnumber,r1=structpt_regs*
  10. @
  11. adrnelr,1b
  12. bneasm_do_IRQ//跳轉到這里中斷的總入口函數(shù)

這里的asm_do_IRQ對應arch/arm/kernel/irq.c中:


  1. asmlinkagevoid__exceptionasm_do_IRQ(unsignedintirq,structpt_regs*regs)

所以這是Linux中所有中斷的總入口函數(shù)。asm_doIRQ()調用generic_handle_irq()再后調用

generic_handle_irq_desc(),最后到各個irq_desc的處理。


  1. staticinlinevoidgeneric_handle_irq_desc(unsignedintirq,structirq_desc*desc)
  2. {
  3. #ifdefCONFIG_GENERIC_HARDIRQS_NO__DO_IRQ
  4. desc->handle_irq(irq,desc);
  5. #else
  6. if(likely(desc->handle_irq))
  7. desc->handle_irq(irq,desc);
  8. else
  9. __do_IRQ(irq);
  10. #endif
  11. }

從總入口到用戶的中斷處理函數(shù)的流程:

asm_do_IRQ() –-> generic_handle_irq() –->irq_dsc->handle() à handle_IRQ_event() à irq_des->action()

最后對中斷處理流程進行簡單總結:

(1)總入口函數(shù)asm_do_IRQ,獲取中斷號irq

(2)asm_do_IRQ根據中斷號調用各中斷號所注冊的中斷入口函數(shù)irq_desc[irq]->handle_irq

(3)最后在中斷入口函數(shù)中調用handle_IRQ_event()依次執(zhí)行用戶的中斷處理函數(shù)action



關鍵詞: ARMLinuxs3c2440中

評論


技術專區(qū)

關閉