当前位置:   article > 正文

Linux休眠后开启中断的方法及中断唤醒过程_linux 中断唤醒

linux 中断唤醒

Linux进入休眠后,会进入dpm_suspend_noirq函数。该函数会遍历所有的中断,进行关闭。

  1. int dpm_suspend_noirq(pm_message_t state)
  2. {
  3. device_wakeup_arm_wake_irqs();
  4. suspend_device_irqs();
  5. }
  6. static inline bool irqd_is_wakeup_set(struct irq_data *d)
  7. {
  8. return __irqd_to_state(d) & IRQD_WAKEUP_STATE;
  9. }
  10. void suspend_device_irqs(void)
  11. {
  12. struct irq_desc *desc;
  13. for_each_irq_desc(irq, desc) {
  14. raw_spin_lock_irqsave(&desc->lock, flags);
  15. sync = suspend_device_irq(desc);
  16. raw_spin_unlock_irqrestore(&desc->lock, flags);
  17. }
  18. }
  19. static bool suspend_device_irq(struct irq_desc *desc)
  20. {
  21. if (!desc->action || irq_desc_is_chained(desc) ||
  22. desc->no_suspend_depth)
  23. return false;
  24. if (irqd_is_wakeup_set(&desc->irq_data)) {
  25. irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
  26. /*
  27. * We return true here to force the caller to issue
  28. * synchronize_irq(). We need to make sure that the
  29. * IRQD_WAKEUP_ARMED is visible before we return from
  30. * suspend_device_irqs().
  31. */
  32. return true;
  33. }
  34. desc->istate |= IRQS_SUSPENDED;
  35. __disable_irq(desc);
  36. /*
  37. * Hardware which has no wakeup source configuration facility
  38. * requires that the non wakeup interrupts are masked at the
  39. * chip level. The chip implementation indicates that with
  40. * IRQCHIP_MASK_ON_SUSPEND.
  41. */
  42. if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
  43. mask_irq(desc);
  44. return true;
  45. }

从上面的代码可以看出,如果no_suspend_depth计数不为0或者有设置IRQD_WAKEUP_STATE标志,就不会关闭该中断。

方法1:可以在申请中断时,加入参数IRQF_NO_SUSPEND,no_suspend_depth计数不为0,其原理如下

  1. static inline int __must_check request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,
  2. const char *name, void *dev)
  3. {
  4. return request_threaded_irq(irq, handler, NULL, flags, name, dev);
  5. }
  6. int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id)
  7. {
  8. desc = irq_to_desc(irq);
  9. if (!desc)
  10. return -EINVAL;
  11. action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
  12. if (!action)
  13. return -ENOMEM;
  14. action->handler = handler;
  15. action->thread_fn = thread_fn;
  16. action->flags = irqflags;
  17. action->name = devname;
  18. action->dev_id = dev_id;
  19. retval = __setup_irq(irq, desc, action);
  20. }
  21. static int __setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
  22. {
  23. irq_pm_install_action(desc, new);
  24. }
  25. void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
  26. {
  27. desc->nr_actions++;
  28. if (action->flags & IRQF_FORCE_RESUME)
  29. desc->force_resume_depth++;
  30. WARN_ON_ONCE(desc->force_resume_depth &&
  31. desc->force_resume_depth != desc->nr_actions);
  32. if (action->flags & IRQF_NO_SUSPEND)
  33. desc->no_suspend_depth++;
  34. else if (action->flags & IRQF_COND_SUSPEND)
  35. desc->cond_suspend_depth++;
  36. WARN_ON_ONCE(desc->no_suspend_depth &&
  37. (desc->no_suspend_depth +

方法2:在进入suspend函数中加入irq_set_irq_wake(irq,1),该方法会设置IRQD_WAKEUP_STATE标志,resume函数中加入irq_set_irq_wake(irq,0)其原理如下

  1. int irq_set_irq_wake(unsigned int irq, unsigned int on)
  2. {
  3.     unsigned long flags;
  4.     struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
  5.     int ret = 0;
  6.     if (on) {
  7.         if (desc->wake_depth++ == 0) {
  8.             ret = set_irq_wake_real(irq, on);
  9.             if (ret)
  10.                 desc->wake_depth = 0;
  11.             else
  12.                 irqd_set(&desc->irq_data, IRQD_WAKEUP_STATE);
  13.         }
  14.     } else {
  15.         if (desc->wake_depth == 0) {
  16.             WARN(1, "Unbalanced IRQ %d wake disable\n", irq);
  17.         } else if (--desc->wake_depth == 0) {
  18.             ret = set_irq_wake_real(irq, on);
  19.             if (ret)
  20.                 desc->wake_depth = 1;
  21.             else
  22.                 irqd_clear(&desc->irq_data, IRQD_WAKEUP_STATE);
  23.         }
  24.     }
  25.     return ret;
  26. }
  27. static int set_irq_wake_real(unsigned int irq, unsigned int on)
  28. {
  29.     struct irq_desc *desc = irq_to_desc(irq);
  30.     int ret = -ENXIO;
  31.     if (irq_desc_get_chip(desc)->flags &  IRQCHIP_SKIP_SET_WAKE)
  32.         return 0;
  33.     if (desc->irq_data.chip->irq_set_wake)
  34.         ret = desc->irq_data.chip->irq_set_wake(&desc->irq_data, on);
  35.     return ret;
  36. }

另外,休眠和唤醒流程中有如下函数

  1. int dpm_suspend_noirq(pm_message_t state)
  2. {
  3.     device_wakeup_arm_wake_irqs();
  4. }
  5. void dpm_resume_noirq(pm_message_t state)
  6. {
  7.     device_wakeup_disarm_wake_irqs();
  8. }
  1. void dev_pm_arm_wake_irq(struct wake_irq *wirq)
  2. {
  3.     if (!wirq)
  4.         return;
  5.     if (device_may_wakeup(wirq->dev))
  6.         enable_irq_wake(wirq->irq);
  7. }
  8. void device_wakeup_arm_wake_irqs(void)
  9. {
  10.     struct wakeup_source *ws;
  11.     int srcuidx;
  12.     srcuidx = srcu_read_lock(&wakeup_srcu);
  13.     list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
  14.         if (ws->wakeirq)
  15.             dev_pm_arm_wake_irq(ws->wakeirq);
  16.     }
  17.     srcu_read_unlock(&wakeup_srcu, srcuidx);
  18. }
  19. void dev_pm_disarm_wake_irq(struct wake_irq *wirq)
  20. {
  21.     if (!wirq)
  22.         return;
  23.     if (device_may_wakeup(wirq->dev))
  24.         disable_irq_wake(wirq->irq);
  25. }
  26. void device_wakeup_disarm_wake_irqs(void)
  27. {
  28.     struct wakeup_source *ws;
  29.     int srcuidx;
  30.     srcuidx = srcu_read_lock(&wakeup_srcu);
  31.     list_for_each_entry_rcu(ws, &wakeup_sources, entry) {
  32.         if (ws->wakeirq)
  33.             dev_pm_disarm_wake_irq(ws->wakeirq);
  34.     }
  35.     srcu_read_unlock(&wakeup_srcu, srcuidx);
  36. }
  37. int device_wakeup_attach_irq(struct device *dev,
  38.                  struct wake_irq *wakeirq)
  39. {
  40.     struct wakeup_source *ws;
  41.     ws = dev->power.wakeup;
  42.     if (!ws) {
  43.         dev_err(dev, "forgot to call call device_init_wakeup?\n");
  44.         return -EINVAL;
  45.     }
  46.     if (ws->wakeirq)
  47.         return -EEXIST;
  48.     ws->wakeirq = wakeirq;
  49.     return 0;
  50. }
  51. static int dev_pm_attach_wake_irq(struct device *dev, int irq,struct wake_irq *wirq)
  52. {
  53.     err = device_wakeup_attach_irq(dev, wirq);
  54. }
  55. int dev_pm_set_wake_irq(struct device *dev, int irq)
  56. {
  57.     struct wake_irq *wirq;
  58.     int err;
  59.     if (irq < 0)
  60.         return -EINVAL;
  61.     wirq = kzalloc(sizeof(*wirq), GFP_KERNEL);
  62.     if (!wirq)
  63.         return -ENOMEM;
  64.     wirq->dev = dev;
  65.     wirq->irq = irq;
  66.     err = dev_pm_attach_wake_irq(dev, irq, wirq);
  67.     if (err)
  68.         kfree(wirq);
  69.     return err;
  70. }
  71. static inline int device_init_wakeup(struct device *dev, bool val)
  72. {
  73.     device_set_wakeup_capable(dev, val);
  74.     device_set_wakeup_enable(dev, val);
  75.     return 0;
  76. }

在初始化中加入如下函数,系统默认会自动调用irq_set_irq_wake开启和关闭中断唤醒。

  1. device_init_wakeup(struct device *dev, bool val)
  2. dev_pm_set_wake_irq(struct device *dev, int irq)

总结

1.申请中断时加入IRQF_NO_SUSPEND标志。

中断唤醒cpu后,如果中断处理里有调用某些资源(如i2c),内核会报i2c处于suspend状态,这是因为i2c还没唤醒,需要进行一定的延时,建议使用irq_set_irq_wake函数,因为按照休眠唤醒逻辑,i2c已经唤醒了。其深度休眠,唤醒流程跟非深度休眠流程一致。

2.休眠时开中断唤醒,唤醒时关闭中断唤醒。

  1. irq_set_irq_wake(irq,1)
  2. irq_set_irq_wake(irq,0)

对应某些场景,我们需要中断总是能唤醒cpu(而不是由用户设置),我们可以在probe函数里直接调用irq_set_irq_wake(irq,1)即可(不需要关闭)。

3.初始化调用如下函数,其原理是自动调用irq_set_irq_wake函数。

  1. device_init_wakeup(struct device *dev, bool val)
  2. dev_pm_set_wake_irq(struct device *dev, int irq)

具体的唤醒流程参考http://www.wowotech.net/irq_subsystem/irq_handle_procedure.html

唤醒后,通过irq_set_irq_wake(irq,1)设置的中断,处理流程如下

  1. bool irq_pm_check_wakeup(struct irq_desc *desc)
  2. {
  3. if (irqd_is_wakeup_armed(&desc->irq_data)) {
  4. irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
  5. desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
  6. desc->depth++;
  7. irq_disable(desc);-----------------------------先关闭irq
  8. pm_system_irq_wakeup(irq_desc_get_irq(desc));
  9. return true;
  10. }
  11. return false;
  12. }
  13. static bool irq_may_run(struct irq_desc *desc)
  14. {
  15. unsigned int mask = IRQD_IRQ_INPROGRESS | IRQD_WAKEUP_ARMED;
  16. /*
  17. * If the interrupt is not in progress and is not an armed
  18. * wakeup interrupt, proceed.
  19. */
  20. if (!irqd_has_set(&desc->irq_data, mask))
  21. return true;
  22. /*
  23. * If the interrupt is an armed wakeup source, mark it pending
  24. * and suspended, disable it and notify the pm core about the
  25. * event.
  26. */
  27. if (irq_pm_check_wakeup(desc))
  28. return false;
  29. /*
  30. * Handle a potential concurrent poll on a different core.
  31. */
  32. return irq_check_poll(desc);
  33. }
  34. static inline void mask_ack_irq(struct irq_desc *desc)
  35. {
  36. if (desc->irq_data.chip->irq_mask_ack)
  37. desc->irq_data.chip->irq_mask_ack(&desc->irq_data);
  38. else {
  39. desc->irq_data.chip->irq_mask(&desc->irq_data);
  40. if (desc->irq_data.chip->irq_ack)
  41. desc->irq_data.chip->irq_ack(&desc->irq_data);
  42. }
  43. irq_state_set_masked(desc);
  44. }
  45. void __enable_irq(struct irq_desc *desc)
  46. {
  47. switch (desc->depth) {
  48. case 0:
  49. err_out:
  50. WARN(1, KERN_WARNING "Unbalanced enable for IRQ %d\n",
  51. irq_desc_get_irq(desc));
  52. break;
  53. case 1: {
  54. if (desc->istate & IRQS_SUSPENDED)
  55. goto err_out;
  56. /* Prevent probing on this irq: */
  57. irq_settings_set_noprobe(desc);
  58. irq_enable(desc);-----------------------------再开启irq
  59. check_irq_resend(desc);
  60. /* fall-through */
  61. }
  62. default:
  63. desc->depth--;
  64. }
  65. }
  66. void irq_enable(struct irq_desc *desc)
  67. {
  68. irq_state_clr_disabled(desc);
  69. if (desc->irq_data.chip->irq_enable)
  70. desc->irq_data.chip->irq_enable(&desc->irq_data);
  71. else
  72. desc->irq_data.chip->irq_unmask(&desc->irq_data);
  73. irq_state_clr_masked(desc);
  74. }
  75. void check_irq_resend(struct irq_desc *desc)
  76. {
  77. /*
  78. * We do not resend level type interrupts. Level type
  79. * interrupts are resent by hardware when they are still
  80. * active. Clear the pending bit so suspend/resume does not
  81. * get confused.
  82. */
  83. if (irq_settings_is_level(desc)) {
  84. desc->istate &= ~IRQS_PENDING;
  85. return;
  86. }
  87. if (desc->istate & IRQS_REPLAY)
  88. return;
  89. if (desc->istate & IRQS_PENDING) {
  90. desc->istate &= ~IRQS_PENDING;
  91. desc->istate |= IRQS_REPLAY;
  92. if (!desc->irq_data.chip->irq_retrigger ||
  93. !desc->irq_data.chip->irq_retrigger(&desc->irq_data)) {
  94. #ifdef CONFIG_HARDIRQS_SW_RESEND
  95. unsigned int irq = irq_desc_get_irq(desc);
  96. /*
  97. * If the interrupt is running in the thread
  98. * context of the parent irq we need to be
  99. * careful, because we cannot trigger it
  100. * directly.
  101. */
  102. if (irq_settings_is_nested_thread(desc)) {
  103. /*
  104. * If the parent_irq is valid, we
  105. * retrigger the parent, otherwise we
  106. * do nothing.
  107. */
  108. if (!desc->parent_irq)
  109. return;
  110. irq = desc->parent_irq;
  111. }
  112. /* Set it pending and activate the softirq: */
  113. set_bit(irq, irqs_resend);
  114. tasklet_schedule(&resend_tasklet);
  115. #endif
  116. }
  117. }
  118. }
  119. void handle_level_irq(struct irq_desc *desc)
  120. {
  121. raw_spin_lock(&desc->lock);
  122. mask_ack_irq(desc);
  123. if (!irq_may_run(desc))
  124. goto out_unlock;
  125. desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
  126. kstat_incr_irqs_this_cpu(desc);
  127. /*
  128. * If its disabled or no action available
  129. * keep it masked and get out of here
  130. */
  131. if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
  132. desc->istate |= IRQS_PENDING;
  133. goto out_unlock;
  134. }
  135. handle_irq_event(desc);
  136. cond_unmask_irq(desc);
  137. out_unlock:
  138. raw_spin_unlock(&desc->lock);
  139. }
  140. void handle_edge_irq(struct irq_desc *desc)
  141. {
  142. raw_spin_lock(&desc->lock);
  143. desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);
  144. if (!irq_may_run(desc)) {
  145. desc->istate |= IRQS_PENDING;
  146. mask_ack_irq(desc);
  147. goto out_unlock;
  148. }
  149. /*
  150. * If its disabled or no action available then mask it and get
  151. * out of here.
  152. */
  153. if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {
  154. desc->istate |= IRQS_PENDING;
  155. mask_ack_irq(desc);
  156. goto out_unlock;
  157. }
  158. kstat_incr_irqs_this_cpu(desc);
  159. /* Start handling the irq */
  160. desc->irq_data.chip->irq_ack(&desc->irq_data);
  161. do {
  162. if (unlikely(!desc->action)) {
  163. mask_irq(desc);
  164. goto out_unlock;
  165. }
  166. /*
  167. * When another irq arrived while we were handling
  168. * one, we could have masked the irq.
  169. * Renable it, if it was not disabled in meantime.
  170. */
  171. if (unlikely(desc->istate & IRQS_PENDING)) {
  172. if (!irqd_irq_disabled(&desc->irq_data) &&
  173. irqd_irq_masked(&desc->irq_data))
  174. unmask_irq(desc);
  175. }
  176. handle_irq_event(desc);
  177. } while ((desc->istate & IRQS_PENDING) &&
  178. !irqd_irq_disabled(&desc->irq_data));
  179. out_unlock:
  180. raw_spin_unlock(&desc->lock);
  181. }

check_irq_resend直译过来就是检查irq并重发(软件模拟)
对于电平中断来说,handle_level_irq关掉中断后,再次开启irq,中断信号还在,会重新调用handle_level_irq
对应边沿中断来说,只会触发一次,handle_edge_irq已经关掉了一次,再次开启irq时,由于没信号过来了,需要软件模拟中断过来,即重新调用一遍desc->handle_irq()/handle_edge_irq

  1. static DECLARE_TASKLET(resend_tasklet, resend_irqs, 0);
  2. static void resend_irqs(unsigned long arg)
  3. {
  4. struct irq_desc *desc;
  5. int irq;
  6. while (!bitmap_empty(irqs_resend, nr_irqs)) {
  7. irq = find_first_bit(irqs_resend, nr_irqs);
  8. clear_bit(irq, irqs_resend);
  9. desc = irq_to_desc(irq);
  10. local_irq_disable();
  11. desc->handle_irq(desc);
  12. local_irq_enable();
  13. }
  14. }

 电平中断和边沿中断的处理流程有差异,见下图

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/817986
推荐阅读
相关标签
  

闽ICP备14008679号