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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章