07
01

Linux porting on different platform HowTo(2)

承之前的文章, Linux porting的步驟如下所示
1.根據你的平台和相關硬體,找出一個跟此結構最相似的architecture
2.拷貝這個結構的硬體範本到新結構的目錄下.通常只需拷貝以下幾個資料夾
  <1>arch/xxx/mach-xxx
  <2>include/arch/xxx/mach-xxx
3.試著編譯新架構並修改相關的define和Kconfig檔案
上面的步驟完成後,你已經有一個新架構的殼在那裡了

接著再談到在新的骨架下填肉的問題,這邊我們所提到的肉,就是IRQ和linux kernel timer
我們先談IRQ在Linux的生死流程
當硬體發出IRQ中斷時,Linux第一個接觸到此中斷的程式是entry-armv.S,這個檔程式負責找出發出IRQ中斷的號碼,並呼叫do_irq這個函式去處理相關的硬體中斷,do_irq函式返回後,entry-armv.S會處理context restored的一些流程,而在這邊我們所需要修改的程式只有entry-macro這隻巨集,此巨具會找到發出中斷的編號

在修改entry-macro.S這隻程式前,我以為取得2410中斷編號的方法應該跟4510一樣簡單,只要讀取interrupt offset這個暫存器就可以了,結果發現並不是那麼一回事,如果entry-macro.S寫得像下面那樣

  1. .macro  get_irqnr_and_base, irqnr, irqstat, base, tmp
  2.                 ldr     \tmp, =rINTOFFSET
  3.                 ldr     \irqnr, [\tmp]          @ get irq no
  4.                 teq     \irqnr, #NR_IRQS
  5.         .endm

你會發現你有收不完的EINT0外部中斷,而導製整個系統locking.為什麼呢?因為interrupt offset也有可能沒有值,但卻不是EINT0外部中斷,所以下面這段程式除了檢查interrupt pending之外,還需檢查CPSR的flag是否有設定正確

  1. #include <asm/arch/map.h> 
  2. #if defined(CONFIG_ARCH_QT2410)
  3.  
  4. .macro get_irqnr_and_base, irqnr, irqstat, base, tmp 
  5. mov \tmp, #S3C2410_PA_IRQ 
  6. ldr \irqnr, [ \tmp, #0x14 ] @ get irq no 
  7. 30000:
  8. teq \irqnr, #4 
  9. teqne \irqnr, #5 
  10. beq 1002f @ external irq reg 
  11.  
  12. @ debug check to see if interrupt reported is the same 
  13. @ as the offset....
  14.  
  15. teq \irqnr, #0 
  16. beq 20002f 
  17. ldr \irqstat, [ \tmp, #0x10 ] @ INTPND 
  18. mov \irqstat, \irqstat, lsr \irqnr 
  19. tst \irqstat, #1 
  20. bne 20002f 
  21.  
  22. /* debug/warning if we get an invalud response from the
  23. * INTOFFSET register */ 
  24. #if 1
  25. stmfd r13!, { r0 - r4 , r8-r12, r14 } 
  26. ldr r1, [ \tmp, #0x14 ] @ INTOFFSET 
  27. ldr r2, [ \tmp, #0x10 ] @ INTPND 
  28. ldr r3, [ \tmp, #0x00 ] @ SRCPND 
  29. adr r0, 20003f 
  30. bl printk 
  31. b 20004f 
  32.  
  33. 20003:
  34. .ascii "<7>irq: err - bad offset %d, intpnd=%08x, srcpnd=%08x\n" 
  35. .byte 0 
  36. .align 4 
  37. 20004:
  38. mov r1, #1 
  39. mov \tmp, #S3C24XX_PA_IRQ 
  40. ldmfd r13!, { r0 - r4 , r8-r12, r14 } 
  41. #endif
  42.  
  43. @ try working out interrupt number for ourselves 
  44. mov \irqnr, #0 
  45. ldr \irqstat, [ \tmp, #0x10 ] @ INTPND 
  46. 10021:
  47. movs \irqstat, \irqstat, lsr#1 
  48. bcs 30000b @ try and re-start the proccess 
  49. add \irqnr, \irqnr, #1 
  50. cmp \irqnr, #32 
  51. ble 10021b 
  52.  
  53. @ found no interrupt, set Z flag and leave 
  54. movs \irqnr, #0 
  55. b 1001f 
  56.  
  57. 20005:
  58. 20002: @ exit 
  59. @ we base the s3c2410x interrupts at 16 and above to allow 
  60. @ isa peripherals to have their standard interrupts, also 
  61. @ ensure that Z flag is un-set on exit 
  62.  
  63. @ note, we cannot be sure if we get IRQ_EINT0 (0) that 
  64. @ there is simply no interrupt pending, so in all other 
  65. @ cases we jump to say we have found something, otherwise 
  66. @ we check to see if the interrupt really is assrted 
  67. @ Joey:Add 16 to current interrupt number?? It is not necessay for qt2410 
  68. @adds \irqnr, \irqnr, #IRQ_EINT0 
  69. teq \irqnr, #nEXT0_INT 
  70. bne 1001f @ exit 
  71. ldr \irqstat, [ \tmp, #0x10 ] @ INTPND 
  72. teq \irqstat, #0 
  73. moveq \irqnr, #0 
  74. b 1001f 
  75.  
  76. @ we get here from no main or external interrupts pending 
  77. 1002:
  78. @add \tmp, \tmp, #S3C2410_PA_GPIO - S3C24XX_PA_IRQ 
  79. mov \tmp, #S3C2410_PA_GPIO 
  80. ldr \irqstat, [ \tmp, # 0xa8 ] @ EXTINTPEND 
  81. ldr \irqnr, [ \tmp, # 0xa4 ] @ EXTINTMASK 
  82.  
  83. bic \irqstat, \irqstat, \irqnr @ clear masked irqs 
  84.  
  85. mov \irqnr, #nEXT4_7_INT @ start extint nos 
  86. mov \irqstat, \irqstat, lsr#4 @ ignore bottom 4 bits 
  87. 10021:
  88. movs \irqstat, \irqstat, lsr#1 
  89. bcs 1004f 
  90. add \irqnr, \irqnr, #1 
  91. cmp \irqnr, #nEXT8_23_INT 
  92. ble 10021b 
  93.  
  94. @ found no interrupt, set Z flag and leave 
  95. movs \irqnr, #0 
  96.  
  97. 1004: @ ensure Z flag clear in case our MOVS shifted out the last bit 
  98. teq \irqnr, #0 
  99. 1001:
  100. @ exit irq routine 
  101. .endm 
  102.  
  103.  
  104. /* currently don't need an disable_fiq macro */ 
  105.  
  106. .macro disable_fiq 
  107. .endm 
  108.  
  109.  
  110. #endif



修正完entry-macro.S這個檔案後,接著我們要註冊IRQ的處理函式

在arch/arm/mach-qt2410/的資料夾下會有一個arch.c的檔案,此檔案中有一段程式如下

  1. MACHINE_START(QT2410, "QT2410(SMDK2410)")
  2.     /* Maintainer: Joey Cheng <<a href=\"mailto:jemicheng@gmail.com\">jemicheng@gmail.com</a>> */
  3.     .init_irq    = qt2410_init_irq,
  4.     .init_machine = qt2410_init_machine,
  5.     .boot_params    = 0x30000100,
  6.     .timer = &qt2410_timer,
  7. MACHINE_END

當linux核心做初始化的動作時,至少會呼叫到3個architecture的初始化函式,分別為init_irq,init_machine和timer
所以我們為IRQ而改的檔案就很明確,就是arch/arm/mach-qt2410/irq.c
而IRQ的initial過程只有兩個步驟
<1>mask所有的中斷來源
<2>註冊中斷chip

在IRQ.c這邊其實可以看到4510與2410極大的不同點,4510的IRQ.c真的很簡單,它只有新增一個irq chip,並把所有的中斷編號都註冊到此chip
中斷chip在linux kernel只幹三件事,mask interrupt, unmask interrupt和ack interrupt, 簡單的irq chip使用方法如下

  1. struct irqchip qt2410_irq_level_chip = {
  2.     .ack       = qt2410_irq_maskack,
  3.     .mask       = qt2410_irq_mask,
  4.     .unmask       = qt2410_irq_unmask,
  5. };

而我們在寫driver時經由setup_irq或request_irq所註冊的中斷處理函式會由do_level_irq或do_edge_irq呼叫執行,註冊的範例如下

  1. for (irqno = 0; irqno < NR_IRQS; irqno++) 
  2. { 
  3. switch (irqno) 
  4. { 
  5. /* deal with the special IRQs (cascaded) */ 
  6.  
  7. case nUART0_INT:
  8. case nUART1_INT:
  9. case nUART2_INT:
  10. case nADC_INT:
  11. set_irq_chip(irqno, &qt2410_irq_level_chip);
  12. set_irq_handler(irqno, do_level_IRQ);
  13. break;
  14. default:
  15. set_irq_chip(irqno, &qt2410_irq_chip);
  16. set_irq_handler(irqno, do_edge_IRQ);
  17. set_irq_flags(irqno, IRQF_VALID);
  18. } 
  19. }

當然,2410 IRQ複雜的不只是一顆IRQ chip就可以handle了,就以uart為例,在處理uart tx或rx中斷時,還必需去讀取subpending這個register,並以讀取的結果判別我們要處理的是tx還是rx中斷,所以為了這個動作,必需再新增一個chip並註冊chained handler

真正處理uart tx,rx中斷的地方是在chained handler, chained handler的註冊方式如下

  1. set_irq_chained_handler(nUART0_INT, qt2410_irq_demux_uart0);

而我們為uart新增了一個interrupt chip,也必需新增uart tx, rx中斷編號,並註冊相關的函式
  1. for (irqno = nUART0_RX0_INT; irqno <= nUART0_TX0_INT; irqno++)
  2.     {
  3.         set_irq_chip(irqno, &qt2410_irq_uart0);
  4.         set_irq_handler(irqno, do_edge_IRQ);
  5.         set_irq_flags(irqno, IRQF_VALID);
  6.     }

所以IRQ的程式我們大概處理完了,接下來我會講解timer...待續

 

標籤: embedded
評論: 0 | 引用: 0 | 閱讀: 7821
發表評論
暱 稱: 密 碼:
網 址: E - mail:
驗證碼: 驗證碼圖片 選 項:
頭 像:
內 容: