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