- 瞭解驅動常用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