pm.c 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  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. static void resume_irq(struct irq_desc *desc)
  130. {
  131. struct irq_data *irqd = &desc->irq_data;
  132. irqd_clear(irqd, IRQD_WAKEUP_ARMED);
  133. if (irqd_is_enabled_on_suspend(irqd)) {
  134. /*
  135. * Interrupt marked for wakeup was enabled during suspend
  136. * entry. Disable such interrupts to restore them back to
  137. * original state.
  138. */
  139. __disable_irq(desc);
  140. irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND);
  141. }
  142. if (desc->istate & IRQS_SUSPENDED)
  143. goto resume;
  144. /* Force resume the interrupt? */
  145. if (!desc->force_resume_depth)
  146. return;
  147. /* Pretend that it got disabled ! */
  148. desc->depth++;
  149. irq_state_set_disabled(desc);
  150. irq_state_set_masked(desc);
  151. resume:
  152. desc->istate &= ~IRQS_SUSPENDED;
  153. __enable_irq(desc);
  154. }
  155. static void resume_irqs(bool want_early)
  156. {
  157. struct irq_desc *desc;
  158. int irq;
  159. for_each_irq_desc(irq, desc) {
  160. unsigned long flags;
  161. bool is_early = desc->action &&
  162. desc->action->flags & IRQF_EARLY_RESUME;
  163. if (!is_early && want_early)
  164. continue;
  165. if (irq_settings_is_nested_thread(desc))
  166. continue;
  167. raw_spin_lock_irqsave(&desc->lock, flags);
  168. resume_irq(desc);
  169. raw_spin_unlock_irqrestore(&desc->lock, flags);
  170. }
  171. }
  172. /**
  173. * rearm_wake_irq - rearm a wakeup interrupt line after signaling wakeup
  174. * @irq: Interrupt to rearm
  175. */
  176. void rearm_wake_irq(unsigned int irq)
  177. {
  178. unsigned long flags;
  179. struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, IRQ_GET_DESC_CHECK_GLOBAL);
  180. if (!desc)
  181. return;
  182. if (!(desc->istate & IRQS_SUSPENDED) ||
  183. !irqd_is_wakeup_set(&desc->irq_data))
  184. goto unlock;
  185. desc->istate &= ~IRQS_SUSPENDED;
  186. irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED);
  187. __enable_irq(desc);
  188. unlock:
  189. irq_put_desc_busunlock(desc, flags);
  190. }
  191. /**
  192. * irq_pm_syscore_resume - enable interrupt lines early
  193. *
  194. * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
  195. */
  196. static void irq_pm_syscore_resume(void)
  197. {
  198. resume_irqs(true);
  199. }
  200. static struct syscore_ops irq_pm_syscore_ops = {
  201. .resume = irq_pm_syscore_resume,
  202. };
  203. static int __init irq_pm_init_ops(void)
  204. {
  205. register_syscore_ops(&irq_pm_syscore_ops);
  206. return 0;
  207. }
  208. device_initcall(irq_pm_init_ops);
  209. /**
  210. * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs()
  211. *
  212. * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously
  213. * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag
  214. * set as well as those with %IRQF_FORCE_RESUME.
  215. */
  216. void resume_device_irqs(void)
  217. {
  218. resume_irqs(false);
  219. }