TWL6030 電源管理芯片中斷註冊,處理過程


TI  TWL6030 是一款功能強大的電源管理芯片。集成了很多功能,可以對整個板卡上的各設備進行供電和電源管理,功能大致和PC上的電源類似,就是一端插上電源,另一端

分出來好多電源線,分別給處理器,內存,硬盤等供電。只不過有了電源管理芯片,各個電壓可以配置。


本文主要講TWL6030上各設備中斷處理過程。爲什麼用這個來講,主要是它很特殊,我們知道,普通的設備只有一根中斷線接到CPU的引腳上(或PIC PIN),6030也是這樣,只有一根INT連接到CPU的中斷引腳,但是因爲它功能很多,要處理很多中斷。比如:電源按下,USB插入,電量計的檢測等,那麼這麼多的中斷怎麼報給處理器呢?

       由於只有一根INT線,在這根INT線上註冊一箇中斷處理函數,當有中斷髮生時(這個是自動的,6030會做處理,比如你按下電源,或者插入USB,6030會自動在INT上報一箇中斷給CPU),在中斷處理函數中讀取寄存器,看看到底是哪個功能模塊上報的中斷,然後調用這個模塊的處理函數。然而實現方法卻不是這樣。

且看代碼


  1. 247 /* 
  2. 248  * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt. 
  3. 249  * This is a chained interrupt, so there is no desc->action method for it. 
  4. 250  * Now we need to query the interrupt controller in the twl6030 to determine 
  5. 251  * which module is generating the interrupt request. <span style="background-color: rgb(255, 255, 255);"><span style="color:#ff0000;"> However, we can't do i2c 
  6. 252  * transactions in interrupt context, so we must defer that work to a kernel 
  7. 253  * thread.  All we do here is acknowledge and mask the interrupt and wakeup 
  8. 254  * the kernel thread.</span></span> 
  9. 255  */  
  10. 256 static irqreturn_t handle_twl6030_pih(int irq, void *devid)  
  11. 257 {  
  12. 258     disable_irq_nosync(irq);  
  13. 259     complete(devid);  
  14. 260     return IRQ_HANDLED;  
  15. 261 }  

看到紅色部分了吧,在註冊中斷的同時,開啓一個內核線程,這個內核線程是個死循環,不停的進行等待中斷的發生(這裏是等待devid, 被喚醒後繼續),

  1. task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");//這裏創建了一個內核線程,看看<pre name="code" class="cpp">twl6030_irq_thread都幹了些什麼事情吧  
  1. </pre>  


  1. 165 /* 
  2. 166  * This thread processes interrupts reported by the Primary Interrupt Handler. 
  3. 167  */  
  4. 168 static int twl6030_irq_thread(void *data)  
  5. 169 {  
  6. 170     long irq = (long)data;  
  7. 171     static unsigned i2c_errors;  
  8. 172     static const unsigned max_i2c_errors = 100;  
  9. 173     int ret;  
  10. 174   
  11. 175     current->flags |= PF_NOFREEZE;  
  12. 176   
  13. 177     while (!kthread_should_stop()) {  
  14. 178         int i;  
  15. 179         union {  
  16. 180         u8 bytes[4];  
  17. 181         u32 int_sts;  
  18. 182         } sts;  
  19. 183         u32 int_sts; /* sts.int_sts converted to CPU endianness */  
  20. 184   
  21. 185         /* Wait for IRQ, then read PIH irq status (also blocking) */  
  22. 186         wait_for_completion_interruptible(&irq_event);  
  23. 187   
  24. 188         /* read INT_STS_A, B and C in one shot using a burst read */  
  25. 189         ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,  
  26. 190                 REG_INT_STS_A, 3);  
  27. 191         if (ret) {  
  28. 192             pr_warning("twl6030: I2C error %d reading PIH ISR\n",  
  29. 193                     ret);  
  30. 194             if (++i2c_errors >= max_i2c_errors) {  
  31. 195                 printk(KERN_ERR "Maximum I2C error count"  
  32. 196                         " exceeded.  Terminating %s.\n",  
  33. 197                         __func__);  
  34. 198                 break;  
  35. 199             }  
  36. 200             complete(&irq_event);  
  37. 201             continue;  
  38. 202         }  
  39. 203   
  40. 204   
  41. 205   
  42. 206         sts.bytes[3] = 0; /* Only 24 bits are valid*/  
  43. 207   
  44. 208         /* 
  45. 209          * Since VBUS status bit is not reliable for VBUS disconnect 
  46. 210          * use CHARGER VBUS detection status bit instead. 
  47. 211          */  
  48. 212         if (sts.bytes[2] & 0x10)  
  49. 213             sts.bytes[2] |= 0x08;  
  50. 214   
  51. 215         int_sts = le32_to_cpu(sts.int_sts);  
  52. 216         for (i = 0; int_sts; int_sts >>= 1, i++) {  
  53. 217             local_irq_disable();  
  54. 218             if (int_sts & 0x1) {  
  55. 219                 int module_irq = twl6030_irq_base +  
  56. 220                     twl6030_interrupt_mapping[i];  
  57. 221                <span style="color:#ff0000;"> generic_handle_irq(module_irq);</span>  
  58. 222   
  59. 223             }  
  60. 224         local_irq_enable();  
  61. 225         }  
  62. 226   
  63. 227         /* 
  64. 228          * NOTE: 
  65. 229          * Simulation confirms that documentation is wrong w.r.t the 
  66. 230          * interrupt status clear operation. A single *byte* write to 
  67. 231          * any one of STS_A to STS_C register results in all three 
  68. 232          * STS registers being reset. Since it does not matter which 
  69. 233          * value is written, all three registers are cleared on a 
  70. 234          * single byte write, so we just use 0x0 to clear. 
  71. 235          */  
  72. 236         ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);  
  73. 237         if (ret)  
  74. 238             pr_warning("twl6030: I2C error in clearing PIH ISR\n");  
  75. 239   
  76. 240         enable_irq(irq);  
  77. 241     }  
  78. 242   
  79. 243     return 0;  
  80. 244 }  
紅色部分是關鍵點,在該中斷處理函數(確切的說是內核線程了,不在是中斷處理函數了),又調用了中斷處理函數,這些函數是其他模塊註冊的(就是剛纔說的power鍵,USB等註冊進內核的)。比如,檢測到是電源鍵按下或擡起,這個時候,module_irq就是電源鍵中斷申請時申請的irq號。

看看系統中有哪些6030中斷和內核線程

root@android:/ # cat /proc/interrupts |grep 6030                               
 39:         12          0       GIC  TWL6030-PIH
368:          0          4   twl6030  twl6030_pwrbutton
369:          0          0   twl6030  twl6030_gpadc
370:          2          0   twl6030  twl_bci_ctrl
371:          0          0   twl6030  twl6030_gpadc
372:          0          0   twl6030  twl6030_usb
374:          0          0   twl6030  TWL6030-VLOW
378:          2          0   twl6030  twl6030_usb
379:          0          0   twl6030  rtc0
384:          0          0   twl6030  mmc1


root@android:/ # ps |grep 6030                                                 
root      17    2     0      0     c025c410 00000000 S twl6030-irq
root      18    2     0      0     c0103838 00000000 S irq/374-TWL6030
root      19    2     0      0     c0103838 00000000 S irq/372-twl6030
root      20    2     0      0     c0103838 00000000 S irq/378-twl6030
root      31    2     0      0     c0103838 00000000 S irq/371-twl6030
root      32    2     0      0     c0103838 00000000 S irq/369-twl6030
root      34    2     0      0     c0103838 00000000 S irq/368-twl6030
看到了吧,是不是有很多個。其中,能直接中斷CPU的只有

 39:         12          0       GIC  TWL6030-PIH

當這個中斷髮生時,內核線程twl6030-irq會進行查看具體是哪個中斷產生了(這點有點想PIC掩碼機制了),然後直接調用這個中斷相應的處理函數。

也就是說,

368:          0          4   twl6030  twl6030_pwrbutton
369:          0          0   twl6030  twl6030_gpadc
370:          2          0   twl6030  twl_bci_ctrl
371:          0          0   twl6030  twl6030_gpadc
372:          0          0   twl6030  twl6030_usb
374:          0          0   twl6030  TWL6030-VLOW
378:          2          0   twl6030  twl6030_usb

這些註冊進內核的中斷,僅僅是在內核中斷全局描述表中佔個位子,其被調用的過程不像普通的中斷處理函數,而是僅僅類似

一個普通的函數,被另一個內核線程調用而已!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章