autosleep.c 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * kernel/power/autosleep.c
  4. *
  5. * Opportunistic sleep support.
  6. *
  7. * Copyright (C) 2012 Rafael J. Wysocki <rjw@sisk.pl>
  8. */
  9. #include <linux/device.h>
  10. #include <linux/mutex.h>
  11. #include <linux/pm_wakeup.h>
  12. #include "power.h"
  13. static suspend_state_t autosleep_state;
  14. static struct workqueue_struct *autosleep_wq;
  15. /*
  16. * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
  17. * is active, otherwise a deadlock with try_to_suspend() is possible.
  18. * Alternatively mutex_lock_interruptible() can be used. This will then fail
  19. * if an auto_sleep cycle tries to freeze processes.
  20. */
  21. static DEFINE_MUTEX(autosleep_lock);
  22. static struct wakeup_source *autosleep_ws;
  23. static void try_to_suspend(struct work_struct *work)
  24. {
  25. unsigned int initial_count, final_count;
  26. if (!pm_get_wakeup_count(&initial_count, true))
  27. goto out;
  28. mutex_lock(&autosleep_lock);
  29. if (!pm_save_wakeup_count(initial_count) ||
  30. system_state != SYSTEM_RUNNING) {
  31. mutex_unlock(&autosleep_lock);
  32. goto out;
  33. }
  34. if (autosleep_state == PM_SUSPEND_ON) {
  35. mutex_unlock(&autosleep_lock);
  36. return;
  37. }
  38. if (autosleep_state >= PM_SUSPEND_MAX)
  39. hibernate();
  40. else
  41. pm_suspend(autosleep_state);
  42. mutex_unlock(&autosleep_lock);
  43. if (!pm_get_wakeup_count(&final_count, false))
  44. goto out;
  45. /*
  46. * If the wakeup occured for an unknown reason, wait to prevent the
  47. * system from trying to suspend and waking up in a tight loop.
  48. */
  49. if (final_count == initial_count)
  50. schedule_timeout_uninterruptible(HZ / 2);
  51. out:
  52. queue_up_suspend_work();
  53. }
  54. static DECLARE_WORK(suspend_work, try_to_suspend);
  55. void queue_up_suspend_work(void)
  56. {
  57. if (autosleep_state > PM_SUSPEND_ON)
  58. queue_work(autosleep_wq, &suspend_work);
  59. }
  60. suspend_state_t pm_autosleep_state(void)
  61. {
  62. return autosleep_state;
  63. }
  64. int pm_autosleep_lock(void)
  65. {
  66. return mutex_lock_interruptible(&autosleep_lock);
  67. }
  68. void pm_autosleep_unlock(void)
  69. {
  70. mutex_unlock(&autosleep_lock);
  71. }
  72. int pm_autosleep_set_state(suspend_state_t state)
  73. {
  74. #ifndef CONFIG_HIBERNATION
  75. if (state >= PM_SUSPEND_MAX)
  76. return -EINVAL;
  77. #endif
  78. __pm_stay_awake(autosleep_ws);
  79. mutex_lock(&autosleep_lock);
  80. autosleep_state = state;
  81. __pm_relax(autosleep_ws);
  82. if (state > PM_SUSPEND_ON) {
  83. pm_wakep_autosleep_enabled(true);
  84. queue_up_suspend_work();
  85. } else {
  86. pm_wakep_autosleep_enabled(false);
  87. }
  88. mutex_unlock(&autosleep_lock);
  89. return 0;
  90. }
  91. int __init pm_autosleep_init(void)
  92. {
  93. autosleep_ws = wakeup_source_register(NULL, "autosleep");
  94. if (!autosleep_ws)
  95. return -ENOMEM;
  96. autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
  97. if (autosleep_wq)
  98. return 0;
  99. wakeup_source_unregister(autosleep_ws);
  100. return -ENOMEM;
  101. }