分出來好多電源線,分別給處理器,內存,硬盤等供電。只不過有了電源管理芯片,各個電壓可以配置。
本文主要講TWL6030上各設備中斷處理過程。爲什麼用這個來講,主要是它很特殊,我們知道,普通的設備只有一根中斷線接到CPU的引腳上(或PIC PIN),6030也是這樣,只有一根INT連接到CPU的中斷引腳,但是因爲它功能很多,要處理很多中斷。比如:電源按下,USB插入,電量計的檢測等,那麼這麼多的中斷怎麼報給處理器呢?
由於只有一根INT線,在這根INT線上註冊一箇中斷處理函數,當有中斷髮生時(這個是自動的,6030會做處理,比如你按下電源,或者插入USB,6030會自動在INT上報一箇中斷給CPU),在中斷處理函數中讀取寄存器,看看到底是哪個功能模塊上報的中斷,然後調用這個模塊的處理函數。然而實現方法卻不是這樣。
且看代碼
- 247 /*
- 248 * handle_twl6030_int() is the desc->handle method for the twl6030 interrupt.
- 249 * This is a chained interrupt, so there is no desc->action method for it.
- 250 * Now we need to query the interrupt controller in the twl6030 to determine
- 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
- 252 * transactions in interrupt context, so we must defer that work to a kernel
- 253 * thread. All we do here is acknowledge and mask the interrupt and wakeup
- 254 * the kernel thread.</span></span>
- 255 */
- 256 static irqreturn_t handle_twl6030_pih(int irq, void *devid)
- 257 {
- 258 disable_irq_nosync(irq);
- 259 complete(devid);
- 260 return IRQ_HANDLED;
- 261 }
看到紅色部分了吧,在註冊中斷的同時,開啓一個內核線程,這個內核線程是個死循環,不停的進行等待中斷的發生(這裏是等待devid, 被喚醒後繼續),
- task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");//這裏創建了一個內核線程,看看<pre name="code" class="cpp">twl6030_irq_thread都幹了些什麼事情吧
- </pre>
- 165 /*
- 166 * This thread processes interrupts reported by the Primary Interrupt Handler.
- 167 */
- 168 static int twl6030_irq_thread(void *data)
- 169 {
- 170 long irq = (long)data;
- 171 static unsigned i2c_errors;
- 172 static const unsigned max_i2c_errors = 100;
- 173 int ret;
- 174
- 175 current->flags |= PF_NOFREEZE;
- 176
- 177 while (!kthread_should_stop()) {
- 178 int i;
- 179 union {
- 180 u8 bytes[4];
- 181 u32 int_sts;
- 182 } sts;
- 183 u32 int_sts; /* sts.int_sts converted to CPU endianness */
- 184
- 185 /* Wait for IRQ, then read PIH irq status (also blocking) */
- 186 wait_for_completion_interruptible(&irq_event);
- 187
- 188 /* read INT_STS_A, B and C in one shot using a burst read */
- 189 ret = twl_i2c_read(TWL_MODULE_PIH, sts.bytes,
- 190 REG_INT_STS_A, 3);
- 191 if (ret) {
- 192 pr_warning("twl6030: I2C error %d reading PIH ISR\n",
- 193 ret);
- 194 if (++i2c_errors >= max_i2c_errors) {
- 195 printk(KERN_ERR "Maximum I2C error count"
- 196 " exceeded. Terminating %s.\n",
- 197 __func__);
- 198 break;
- 199 }
- 200 complete(&irq_event);
- 201 continue;
- 202 }
- 203
- 204
- 205
- 206 sts.bytes[3] = 0; /* Only 24 bits are valid*/
- 207
- 208 /*
- 209 * Since VBUS status bit is not reliable for VBUS disconnect
- 210 * use CHARGER VBUS detection status bit instead.
- 211 */
- 212 if (sts.bytes[2] & 0x10)
- 213 sts.bytes[2] |= 0x08;
- 214
- 215 int_sts = le32_to_cpu(sts.int_sts);
- 216 for (i = 0; int_sts; int_sts >>= 1, i++) {
- 217 local_irq_disable();
- 218 if (int_sts & 0x1) {
- 219 int module_irq = twl6030_irq_base +
- 220 twl6030_interrupt_mapping[i];
- 221 <span style="color:#ff0000;"> generic_handle_irq(module_irq);</span>
- 222
- 223 }
- 224 local_irq_enable();
- 225 }
- 226
- 227 /*
- 228 * NOTE:
- 229 * Simulation confirms that documentation is wrong w.r.t the
- 230 * interrupt status clear operation. A single *byte* write to
- 231 * any one of STS_A to STS_C register results in all three
- 232 * STS registers being reset. Since it does not matter which
- 233 * value is written, all three registers are cleared on a
- 234 * single byte write, so we just use 0x0 to clear.
- 235 */
- 236 ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);
- 237 if (ret)
- 238 pr_warning("twl6030: I2C error in clearing PIH ISR\n");
- 239
- 240 enable_irq(irq);
- 241 }
- 242
- 243 return 0;
- 244 }
看看系統中有哪些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
這些註冊進內核的中斷,僅僅是在內核中斷全局描述表中佔個位子,其被調用的過程不像普通的中斷處理函數,而是僅僅類似
一個普通的函數,被另一個內核線程調用而已!