分出来好多电源线,分别给处理器,内存,硬盘等供电。只不过有了电源管理芯片,各个电压可以配置。
本文主要讲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
这些注册进内核的中断,仅仅是在内核中断全局描述表中占个位子,其被调用的过程不像普通的中断处理函数,而是仅仅类似
一个普通的函数,被另一个内核线程调用而已!