linux IRQ Management(九)- 驅動中斷常用API

  • 瞭解驅動常用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出現,只要在準備使能喚醒系統功能的時候,纔會發起一些特別的配置和設定。
在這裏插入圖片描述
  外設的中斷信號被送到“通用的中斷信號處理模塊”和“特定中斷信號接收模塊”。正常工作的時候,我們會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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章