TWL6030 电源管理芯片中断注册,处理过程


TI  TWL6030 是一款功能强大的电源管理芯片。集成了很多功能,可以对整个板卡上的各设备进行供电和电源管理,功能大致和PC上的电源类似,就是一端插上电源,另一端

分出来好多电源线,分别给处理器,内存,硬盘等供电。只不过有了电源管理芯片,各个电压可以配置。


本文主要讲TWL6030上各设备中断处理过程。为什么用这个来讲,主要是它很特殊,我们知道,普通的设备只有一根中断线接到CPU的引脚上(或PIC PIN),6030也是这样,只有一根INT连接到CPU的中断引脚,但是因为它功能很多,要处理很多中断。比如:电源按下,USB插入,电量计的检测等,那么这么多的中断怎么报给处理器呢?

       由于只有一根INT线,在这根INT线上注册一个中断处理函数,当有中断发生时(这个是自动的,6030会做处理,比如你按下电源,或者插入USB,6030会自动在INT上报一个中断给CPU),在中断处理函数中读取寄存器,看看到底是哪个功能模块上报的中断,然后调用这个模块的处理函数。然而实现方法却不是这样。

且看代码


[cpp]  view plain copy
  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, 被唤醒后继续),

[cpp]  view plain copy
  1. task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");//这里创建了一个内核线程,看看<pre name="code" class="cpp">twl6030_irq_thread都干了些什么事情吧  
[cpp]  view plain copy
  1. </pre>  

 
  
[cpp]  view plain copy
  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

这些注册进内核的中断,仅仅是在内核中断全局描述表中占个位子,其被调用的过程不像普通的中断处理函数,而是仅仅类似

一个普通的函数,被另一个内核线程调用而已!

你可能感兴趣的:(thread,c,android,Module,byte)