Blame view

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