- 了解驅動常用API
1.irq的打開和關閉
最基本的一對:
- enable_irq(unsigned int irq);
- disable_irq(unsigned int irq);
這兩個API應該配對使用,disable_irq可以被多次嵌套調用,要想重新打開irq,enable_irq必須也要被調用同樣的次數,為此,irq_desc結構中的depth字段專門用于這兩個API嵌套深度的管理。當某個irq首次被驅動程式申請時,預設情況下,設定depth的初始值是0,對應的irq處于打開狀态。
1.1.enable_irq
16 void enable_irq(unsigned int irq)
17 {
18 unsigned long flags;
19 #根據irq得到其對應的中斷描述符
20 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
21 ...
28 #繼續調用__enable_irq 使能中斷
29 __enable_irq(desc);
30 out:
31 irq_put_desc_busunlock(desc, flags);
32 }
33
35 void __enable_irq(struct irq_desc *desc)
36 {
37 switch (desc->depth) {
38 case 0:
39 err_out:
40 WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
41 irq_desc_get_irq(desc));
42 break;
43 #正常情況下第一個調用enable_irq的時候desc->depth 應該是1,如果是0的話,後面進行--操作的話就成負數了
44 case 1: {
45 #如果正處于suspend的過程中,則直接退出
46 if (desc->istate & IRQS_SUSPENDED)
47 goto err_out;
48
49 /* Prevent probing on this irq: */
50 irq_settings_set_noprobe(desc);
51 #通過chip來使能irq
52 irq_enable(desc);
53 check_irq_resend(desc);
54 /* fall-through */
55 }
56 #從這裡可以知道enable_irq 是可以嵌套的,即同一個irq 可以多次調用enable_irq
57 default:
58 desc->depth--;
59 }
60 }
61
68 void irq_enable(struct irq_desc *desc)
69 {
70 irq_state_clr_disabled(desc);
71 #正常情況下回調用chip來使能irq
72 if (desc->irq_data.chip->irq_enable)
73 desc->irq_data.chip->irq_enable(&desc->irq_data);
74 else
75 desc->irq_data.chip->irq_unmask(&desc->irq_data);
76 irq_state_clr_masked(desc);
77 }
當depth的值為1時,才真正地調用irq_enable(),它最終通過chip->unmask或chip->enable回調開啟中斷控制器中相應的中斷線,如果depth不是1,隻是簡單地減去1。如果已經是0,驅動還要調用enable_irq,說明驅動程式處理不當,造成enable與disable不平衡,核心會列印一句警告資訊:Unbalanced enable for IRQ xxx。
1.2.disable_irq
在全局範圍内屏蔽某一個中斷号(irq num),該irq num對應的irq handler不會在任何一個CPU上執行。這個操作是通過設定中斷控制器中的寄存器來對指定中斷進行屏蔽,而其他未屏蔽的中斷依然可以正常送往CPU。disable_irq 首先調用 disable_irq_nosync,然後調用 synchronize_irq 同步。
413 void disable_irq(unsigned int irq)
414 {
415 if (!__disable_irq_nosync(irq))
416 synchronize_irq(irq);
417 }
372 static int __disable_irq_nosync(unsigned int irq)
373 {
374 unsigned long flags;
375 struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_ CHECK_GLOBAL);
376
377 if (!desc)
378 return -EINVAL;
379 __disable_irq(desc, irq, false);
380 irq_put_desc_busunlock(desc, flags);
381 return 0;
382 }
383
360 void __disable_irq(struct irq_desc *desc, unsigned int irq, bool suspend)
361 {
362 if (suspend) {
363 if (!desc->action || (desc->action->flags & IRQF_NO_SUSPEND))
364 return;
365 desc->istate |= IRQS_SUSPENDED;
366 }
367
368 if (!desc->depth++)
369 irq_disable(desc);
370 }
chip.c
216 void irq_disable(struct irq_desc *desc)
217 {
218 irq_state_set_disabled(desc);
219 if (desc->irq_data.chip->irq_disable) {
220 desc->irq_data.chip->irq_disable(&desc->irq_data);
221 irq_state_set_masked(desc);
222 }
223 }
160 static void irq_state_set_disabled(struct irq_desc *desc)
161 {
162 irqd_set(&desc->irq_data, IRQD_IRQ_DISABLED);
163 }
隻有之前的depth為0,才會通過irq_disable函數,調用中斷控制器的回調chip->irq_mask,否則隻是簡單地把depth的值加1。irq_disable函數還會通過irq_state_set_disabled和irq_state_set_masked,設定irq_data.flag的IRQD_IRQ_DISABLED和IRQD_IRQ_MASK标志。
1.3. disable_irq和disable_irq_nosync差別:
disable_irq關閉中斷并等待中斷處理完後傳回, 而disable_irq_nosync立即傳回。
- disable_irq:在非中斷處理函數中使用,會阻塞;
- disable_irq_nosync:在中斷處理函數中使用,不會阻塞;用于屏蔽相應中斷;
由于在disable_irq中會調用synchronize_irq函數等待中斷傳回, 是以在中斷處理程式中不能使用disable_irq, 否則會導緻cpu被synchronize_irq獨占而發生系統崩潰.
1.4.系統中斷喚醒接口:enable_irq_wake() 和 disable_irq_wake()
有些中斷可以将系統從睡眠狀态中喚醒,稱之“可以喚醒系統的中斷”,當然,“可以喚醒系統的中斷”需要配置才能啟動喚醒系統這樣的功能。這樣的中斷一般在工作狀态的時候就是作為普通I/O interrupt出現,隻要在準備使能喚醒系統功能的時候,才會發起一些特别的配置和設定。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyRQpkL0QzN3QjMykTMyEDNwAjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
外設的中斷信号被送到“通用的中斷信号處理子產品”和“特定中斷信号接收子產品”。正常工作的時候,我們會turn on“通用的中斷信号處理子產品”的處理邏輯,而turn off“特定中斷信号接收子產品” 的處理邏輯。但是,在系統進入睡眠狀态的時候,有可能“通用的中斷信号處理子產品”已經off了,這時候,我們需要啟動“特定中斷信号接收子產品”來接收中斷信号,進而讓系統suspend-resume子產品(它往往是suspend狀态時候唯一能夠工作的HW block了)可以正常的被該中斷信号喚醒。一旦喚醒,我們最好是turn off“特定中斷信号接收子產品”,讓外設的中斷處理回到正常的工作模式,同時,也避免了系統suspend-resume子產品收到不必要的幹擾。
- enable_irq_wake():用來打開該外設中斷線通往系統電源管理子產品(也就是上面的suspend-resume子產品)之路;
- disable_irq_wake():用來關閉該外設中斷線通往系統電源管理子產品路徑上的各種HW block。
refer to
- https://www.kernel.org/doc/htmldocs/genericirq/index.html