TWL6030 电源管理芯片中断注册,处理过程
2012-05-10 20:23
309 查看
TI TWL6030 是一款功能强大的电源管理芯片。集成了很多功能,可以对整个板卡上的各设备进行供电和电源管理,功能大致和PC上的电源类似,就是一端插上电源,另一端
分出来好多电源线,分别给处理器,内存,硬盘等供电。只不过有了电源管理芯片,各个电压可以配置。
本文主要讲TWL6030上各设备中断处理过程。为什么用这个来讲,主要是它很特殊,我们知道,普通的设备只有一根中断线接到CPU的引脚上(或PIC PIN),6030也是这样,只有一根INT连接到CPU的中断引脚,但是因为它功能很多,要处理很多中断。比如:电源按下,USB插入,电量计的检测等,那么这么多的中断怎么报给处理器呢?
由于只有一根INT线,在这根INT线上注册一个中断处理函数,当有中断发生时(这个是自动的,6030会做处理,比如你按下电源,或者插入USB,6030会自动在INT上报一个中断给CPU),在中断处理函数中读取寄存器,看看到底是哪个功能模块上报的中断,然后调用这个模块的处理函数。然而实现方法却不是这样。
且看代码
[cpp] view
plaincopy
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, 被唤醒后继续),
[cpp] view
plaincopy
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");//这里创建了一个内核线程,看看<pre name="code" class="cpp">twl6030_irq_thread都干了些什么事情吧
[cpp] view
plaincopy
</pre>
[cpp] view
plaincopy
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 }
红色部分是关键点,在该中断处理函数(确切的说是内核线程了,不在是中断处理函数了),又调用了中断处理函数,这些函数是其他模块注册的(就是刚才说的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
这些注册进内核的中断,仅仅是在内核中断全局描述表中占个位子,其被调用的过程不像普通的中断处理函数,而是仅仅类似
一个普通的函数,被另一个内核线程调用而已!
分出来好多电源线,分别给处理器,内存,硬盘等供电。只不过有了电源管理芯片,各个电压可以配置。
本文主要讲TWL6030上各设备中断处理过程。为什么用这个来讲,主要是它很特殊,我们知道,普通的设备只有一根中断线接到CPU的引脚上(或PIC PIN),6030也是这样,只有一根INT连接到CPU的中断引脚,但是因为它功能很多,要处理很多中断。比如:电源按下,USB插入,电量计的检测等,那么这么多的中断怎么报给处理器呢?
由于只有一根INT线,在这根INT线上注册一个中断处理函数,当有中断发生时(这个是自动的,6030会做处理,比如你按下电源,或者插入USB,6030会自动在INT上报一个中断给CPU),在中断处理函数中读取寄存器,看看到底是哪个功能模块上报的中断,然后调用这个模块的处理函数。然而实现方法却不是这样。
且看代码
[cpp] view
plaincopy
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, 被唤醒后继续),
[cpp] view
plaincopy
task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");//这里创建了一个内核线程,看看<pre name="code" class="cpp">twl6030_irq_thread都干了些什么事情吧
[cpp] view
plaincopy
</pre>
[cpp] view
plaincopy
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 }
红色部分是关键点,在该中断处理函数(确切的说是内核线程了,不在是中断处理函数了),又调用了中断处理函数,这些函数是其他模块注册的(就是刚才说的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
这些注册进内核的中断,仅仅是在内核中断全局描述表中占个位子,其被调用的过程不像普通的中断处理函数,而是仅仅类似
一个普通的函数,被另一个内核线程调用而已!
相关文章推荐
- uCOS2中的中断处理过程
- ARM Linux对中断的处理--中断注册方法
- 中断处理基础(二) 处理过程
- Windows CE 6.0中断处理过程(转载)
- Exynos4412 中断驱动开发(一)—— 中断基础及中断的注册过程
- 分析system_call中断处理过程
- gtk中Gource注册过程和将xevent加入本地队列后处理
- (六)中断处理过程
- 分析system_call中断处理过程
- Realview MDK中编译器对中断处理的过程详解
- linux中断(与异常)处理过程
- 《Linux内核分析》 week5作业-system call中断处理过程
- LINUX驱动注册过程失败处理不当引起的恶果
- Linux中断处理过程
- 第三十课 Spark中的应用程序的注册过程及处理
- ARM中断处理过程
- uCOS II在ARM处理器上移植过程中的中断处理
- 分析write中断处理过程
- 缺页中断处理过程
- Linux内核设计第五周学习总结 分析system_call中断处理过程