pm.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. /*
  2. * linux/kernel/irq/pm.c
  3. *
  4. * Copyright (C) 2009 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc.
  5. *
  6. * This file contains power management functions related to interrupts.
  7. */
  8. #include <linux/irq.h>
  9. #include <linux/module.h>
  10. #include <linux/interrupt.h>
  11. #include <linux/syscore_ops.h>
  12. #include "internals.h"
  13. /**
  14. * suspend_device_irqs - disable all currently enabled interrupt lines
  15. *
  16. * During system-wide suspend or hibernation device drivers need to be prevented
  17. * from receiving interrupts and this function is provided for this purpose.
  18. * It marks all interrupt lines in use, except for the timer ones, as disabled
  19. * and sets the IRQS_SUSPENDED flag for each of them.
  20. */
  21. void suspend_device_irqs(void)
  22. {
  23. struct irq_desc *desc;
  24. int irq;
  25. for_each_irq_desc(irq, desc) {
  26. unsigned long flags;
  27. raw_spin_lock_irqsave(&desc->lock, flags);
  28. __disable_irq(desc, irq, true);
  29. raw_spin_unlock_irqrestore(&desc->lock, flags);
  30. }
  31. for_each_irq_desc(irq, desc)
  32. if (desc->istate & IRQS_SUSPENDED)
  33. synchronize_irq(irq);
  34. }
  35. EXPORT_SYMBOL_GPL(suspend_device_irqs);
  36. static void resume_irqs(bool want_early)
  37. {
  38. struct irq_desc *desc;
  39. int irq;
  40. for_each_irq_desc_reverse(irq, desc) {
  41. unsigned long flags;
  42. bool is_early = desc->action &&
  43. desc->action->flags & IRQF_EARLY_RESUME;
  44. if (!is_early && want_early)
  45. continue;
  46. raw_spin_lock_irqsave(&desc->lock, flags);
  47. __enable_irq(desc, irq, true);
  48. raw_spin_unlock_irqrestore(&desc->lock, flags);
  49. }
  50. }
  51. /**
  52. * irq_pm_syscore_ops - enable interrupt lines early
  53. *
  54. * Enable all interrupt lines with %IRQF_EARLY_RESUME set.
  55. */
  56. static void irq_pm_syscore_resume(void)
  57. {
  58. resume_irqs(true);
  59. }
  60. static struct syscore_ops irq_pm_syscore_ops = {
  61. .resume = irq_pm_syscore_resume,
  62. };
  63. static int __init irq_pm_init_ops(void)
  64. {
  65. register_syscore_ops(&irq_pm_syscore_ops);
  66. return 0;
  67. }
  68. device_initcall(irq_pm_init_ops);
  69. /**
  70. * resume_device_irqs - enable interrupt lines disabled by suspend_device_irqs()
  71. *
  72. * Enable all non-%IRQF_EARLY_RESUME interrupt lines previously
  73. * disabled by suspend_device_irqs() that have the IRQS_SUSPENDED flag
  74. * set as well as those with %IRQF_FORCE_RESUME.
  75. */
  76. void resume_device_irqs(void)
  77. {
  78. resume_irqs(false);
  79. }
  80. EXPORT_SYMBOL_GPL(resume_device_irqs);
  81. /**
  82. * check_wakeup_irqs - check if any wake-up interrupts are pending
  83. */
  84. int check_wakeup_irqs(void)
  85. {
  86. struct irq_desc *desc;
  87. int irq;
  88. for_each_irq_desc(irq, desc) {
  89. if (irqd_is_wakeup_set(&desc->irq_data)) {
  90. if (desc->istate & IRQS_PENDING) {
  91. pr_info("Wakeup IRQ %d %s pending, suspend aborted\n",
  92. irq,
  93. desc->action && desc->action->name ?
  94. desc->action->name : "");
  95. return -EBUSY;
  96. }
  97. continue;
  98. }
  99. /*
  100. * Check the non wakeup interrupts whether they need
  101. * to be masked before finally going into suspend
  102. * state. That's for hardware which has no wakeup
  103. * source configuration facility. The chip
  104. * implementation indicates that with
  105. * IRQCHIP_MASK_ON_SUSPEND.
  106. */
  107. if (desc->istate & IRQS_SUSPENDED &&
  108. irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND)
  109. mask_irq(desc);
  110. }
  111. return 0;
  112. }