pm.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
  4. *
  5. * This file contains power management functions related to interrupts.
  6. */
  7. #include <linux/irq.h>
  8. #include <linux/module.h>
  9. #include <linux/interrupt.h>
  10. #include <linux/suspend.h>
  11. #include <linux/syscore_ops.h>
  12. #include "internals.h"
  13. bool irq_pm_check_wakeup(struct irq_desc *desc)
  14. {
  15. if (irqd_is_wakeup_armed(&desc->irq_data)) {
  16. irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED);
  17. desc->istate |= IRQS_SUSPENDED | IRQS_PENDING;
  18. desc->depth++;
  19. irq_disable(desc);
  20. pm_system_irq_wakeup(irq_desc_get_irq(desc));
  21. return true;
  22. }
  23. return false;
  24. }
  25. /*
  26. * Called from __setup_irq() with desc->lock held after @action has
  27. * been installed in the action chain.
  28. */
  29. void irq_pm_install_action(struct irq_desc *desc, struct irqaction *action)
  30. {
  31. desc->nr_actions++;
  32. if (action->flags & IRQF_FORCE_RESUME)
  33. desc->force_resume_depth++;
  34. WARN_ON_ONCE(desc->force_resume_depth &&
  35. desc->force_resume_depth != desc->nr_actions);
  36. if (action->flags & IRQF_NO_SUSPEND)
  37. desc->no_suspend_depth++;
  38. else if (action->flags & IRQF_COND_SUSPEND)
  39. desc->cond_suspend_depth++;
  40. WARN_ON_ONCE(desc->no_suspend_depth &&
  41. (desc->no_suspend_depth +
  42. desc->cond_suspend_depth) != desc->nr_actions);
  43. }
  44. /*
  45. * Called from __free_irq() with desc->lock held after @action has
  46. * been removed from the action chain.
  47. */
  48. void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action)
  49. {
  50. desc->nr_actions--;
  51. if (action->flags & IRQF_FORCE_RESUME)
  52. desc->force_resume_depth--;
  53. if (action->flags & IRQF_NO_SUSPEND)
  54. desc->no_suspend_depth--;
  55. else if (action->flags & IRQF_COND_SUSPEND)
  56. desc->cond_suspend_depth--;
  57. }
  58. static bool suspend_device_irq(struct irq_desc *desc)
  59. {
  60. unsigned long chipflags = irq_desc_get_chip(desc)->flags;
  61. struct irq_data *irqd = &desc->irq_data;
  62. if (!desc->action || irq_desc_is_chained(desc) ||
  63. desc->no_suspend_depth)
  64. return false;
  65. if (irqd_is_wakeup_set(irqd)) {
  66. irqd_set(irqd, IRQD_WAKEUP_ARMED);
  67. if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) &&
  68. irqd_irq_disabled(irqd)) {
  69. /*
  70. * Interrupt marked for wakeup is in disabled state.
  71. * Enable interrupt here to unmask/enable in irqchip
  72. * to be able to resume with such interrupts.
  73. */
  74. __enable_irq(desc);
  75. irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
  76. }
  77. /*
  78. * We return true here to force the caller to issue
  79. * synchronize_irq(). We need to make sure that the
  80. * IRQD_WAKEUP_ARMED is visible before we return from
  81. * suspend_device_irqs().
  82. */
  83. return true;
  84. }
  85. desc->istate |= IRQS_SUSPENDED;
  86. __disable_irq(desc);
  87. /*
  88. * Hardware which has no wakeup source configuration facility
  89. * requires that the non wakeup interrupts are masked at the
  90. * chip level. The chip implementation indicates that with
  91. * IRQCHIP_MASK_ON_SUSPEND.
  92. */
  93. if (chipflags & IRQCHIP_MASK_ON_SUSPEND)
  94. mask_irq(desc);
  95. return true;
  96. }
  97. /**
  98. * suspend_device_irqs - disable all currently enabled interrupt lines
  99. *
  100. * During system-wide suspend or hibernation device drivers need to be
  101. * prevented from receiving interrupts and this function is provided
  102. * for this purpose.
  103. *
  104. * So we disable all interrupts and mark them IRQS_SUSPENDED except
  105. * for those which are unused, those which are marked as not
  106. * suspendable via an interrupt request with the flag IRQF_NO_SUSPEND
  107. * set and those which are marked as active wakeup sources.
  108. *
  109. * The active wakeup sources are handled by the flow handler entry
  110. * code which checks for the IRQD_WAKEUP_ARMED flag, suspends the
  111. * interrupt and notifies the pm core about the wakeup.
  112. */
  113. void suspend_device_irqs(void)
  114. {
  115. struct irq_desc *desc;
  116. int irq;
  117. for_each_irq_desc(irq, desc) {
  118. unsigned long flags;
  119. bool sync;
  120. if (irq_settings_is_nested_thread(desc))
  121. continue;
  122. raw_spin_lock_irqsave(&desc->lock, flags);
  123. sync = suspend_device_irq(desc);
  124. raw_spin_unlock_irqrestore(&desc->lock, flags);
  125. if (sync)
  126. synchronize_irq(irq);
  127. }
  128. }
  129. EXPORT_SYMBOL_GPL(suspend_device_irqs);
  130. static void resume_irq(struct irq_desc *desc)
  131. {
  132. struct irq_data *irqd = &desc->irq_data;
  133. irqd_clear(irqd, IRQD_WAKEUP_ARMED);
  134. if (irqd_is_enabled_on_suspend(irqd)) {
  135. /*
  136. * Interrupt marked for wakeup was enabled during suspend
  137. * entry. Disable such interrupts to restore them back to
  138. * original state.
  139. */
  140. __disable_irq(desc);
  141. irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
  142. }
  143. if (desc->istate & IRQS_SUSPENDED)
  144. goto resume;
  145. /* Force resume the interrupt? */
  146. if (!desc->force_resume_depth)
  147. return;
  148. /* Pretend that it got disabled ! */
  149. desc->depth++;
  150. irq_state_set_disabled(desc);
  151. irq_state_set_masked(desc);
  152. resume:
  153. desc->istate &= ~IRQS_SUSPENDED;
  154. __enable_irq(desc);
  155. }
  156. static void resume_irqs(bool want_early)
  157. {
  158. struct irq_desc *desc;
  159. int irq;
  160. for_each_irq_desc(irq, desc) {
  161. unsigned long flags;
  162. bool is_early = desc->action &&
  163. desc->action->flags & IRQF_EARLY_RESUME;
  164. if (!is_early && want_early)
  165. continue;
  166. if (irq_settings_is_nested_thread(desc))
  167. continue;
  168. raw_spin_lock_irqsave(&desc->lock, flags);
  169. resume_irq(desc);
  170. raw_spin_unlock_irqrestore(&desc->lock, flags);
  171. }
  172. }
  173. /**
  174. * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
  175. * @irq: Interrupt to rearm
  176. */
  177. void rearm_wake_irq(unsigned int irq)
  178. {
  179. unsigned long flags;
  180. struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
  181. if (!desc)
  182. return;
  183. if (!(desc->istate & IRQS_SUSPENDED) ||
  184. !irqd_is_wakeup_set(&desc->irq_data))
  185. goto unlock;
  186. desc->istate &= ~IRQS_SUSPENDED;
  187. irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
  188. __enable_irq(desc);
  189. unlock:
  190. irq_put_desc_busunlock(desc, flags);
  191. }
  192. /**
  193. * irq_pm_syscore_ops - enable interrupt lines early
  194. *
  195. * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
  196. */
  197. static void irq_pm_syscore_resume(void)
  198. {
  199. resume_irqs(true);
  200. }
  201. static struct syscore_ops irq_pm_syscore_ops = {
  202. .resume = irq_pm_syscore_resume,
  203. };
  204. static int __init irq_pm_init_ops(void)
  205. {
  206. register_syscore_ops(&irq_pm_syscore_ops);
  207. return 0;
  208. }
  209. device_initcall(irq_pm_init_ops);
  210. /**
  211. * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs()
  212. *
  213. * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously
  214. * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag
  215. * set as well as those with %IRQF_FORCE_RESUME.
  216. */
  217. void resume_device_irqs(void)
  218. {
  219. resume_irqs(false);
  220. }
  221. EXPORT_SYMBOL_GPL(resume_device_irqs);