Merge tag 'v3.10.85' into update
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / kernel / power / autosleep.c
CommitLineData
7483b4a4
RW
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
6fa3eb70
S
15//<20130327> <marc.huang> add autosleep dubug log
16#define _TAG_AUTOSLEEP "AUTOSLEEP"
17#define autosleep_log(fmt, ...) pr_debug("[%s][%s]" fmt, _TAG_AUTOSLEEP, __func__, ##__VA_ARGS__)
18#define autosleep_warn(fmt, ...) pr_warn("[%s][%s]" fmt, _TAG_AUTOSLEEP, __func__, ##__VA_ARGS__)
19
20#define HIB_AUTOSLEEP_DEBUG 1
21#define _TAG_HIB_M "HIB/AUTOSLEEP"
22#if (HIB_AUTOSLEEP_DEBUG)
23#define hib_autoslp_log(fmt, ...) pr_warn("[%s][%s]" fmt, _TAG_HIB_M, __func__, ##__VA_ARGS__);
24#else
25#define hib_autoslp_log(fmt, ...)
26#endif
27#define hib_autoslp_warn(fmt, ...) pr_warn("[%s][%s]" fmt, _TAG_HIB_M, __func__, ##__VA_ARGS__);
28
29
7483b4a4
RW
30static suspend_state_t autosleep_state;
31static struct workqueue_struct *autosleep_wq;
32/*
33 * Note: it is only safe to mutex_lock(&autosleep_lock) if a wakeup_source
34 * is active, otherwise a deadlock with try_to_suspend() is possible.
35 * Alternatively mutex_lock_interruptible() can be used. This will then fail
36 * if an auto_sleep cycle tries to freeze processes.
37 */
38static DEFINE_MUTEX(autosleep_lock);
39static struct wakeup_source *autosleep_ws;
40
6fa3eb70
S
41#ifdef CONFIG_MTK_HIBERNATION
42extern bool system_is_hibernating;
43extern int mtk_hibernate_via_autosleep(suspend_state_t *autoslp_state);
44#endif
7483b4a4
RW
45static void try_to_suspend(struct work_struct *work)
46{
47 unsigned int initial_count, final_count;
48
6fa3eb70
S
49 //<20130327> <marc.huang> add autosleep dubug log
50 autosleep_log("pm_get_wakeup_count\n");
7483b4a4
RW
51 if (!pm_get_wakeup_count(&initial_count, true))
52 goto out;
53
54 mutex_lock(&autosleep_lock);
55
6fa3eb70
S
56 //<20130327> <marc.huang> add autosleep dubug log
57 autosleep_log("pm_save_wakeup_count\n");
7b4fc5f5
LS
58 if (!pm_save_wakeup_count(initial_count) ||
59 system_state != SYSTEM_RUNNING) {
7483b4a4
RW
60 mutex_unlock(&autosleep_lock);
61 goto out;
62 }
63
64 if (autosleep_state == PM_SUSPEND_ON) {
6fa3eb70
S
65#ifdef CONFIG_MTK_HIBERNATION
66 system_is_hibernating = false;
67#endif
68 //<20130327> <marc.huang> add autosleep dubug log
69 autosleep_warn("abort due to autosleep_state: %d\n", autosleep_state);
7483b4a4
RW
70 mutex_unlock(&autosleep_lock);
71 return;
72 }
6fa3eb70
S
73#ifdef CONFIG_MTK_HIBERNATION
74 if (autosleep_state >= PM_SUSPEND_MAX) {
75 mtk_hibernate_via_autosleep(&autosleep_state);
76 }
77 else {
78 hib_autoslp_log("pm_suspend: state(%d)\n", autosleep_state);
79 if (!system_is_hibernating) {
80 hib_autoslp_warn("calling pm_suspend() state(%d)\n", autosleep_state);
81 pm_suspend(autosleep_state);
82 }
83 else {
84 hib_autoslp_warn("system is hibernating: so changing state(%d->%d)\n", autosleep_state, PM_SUSPEND_MAX);
85 autosleep_state = PM_SUSPEND_MAX;
86 }
87 }
88#else // !CONFIG_MTK_HIBERNATION
7483b4a4
RW
89 if (autosleep_state >= PM_SUSPEND_MAX)
90 hibernate();
91 else
6fa3eb70
S
92 {
93 //<20130327> <marc.huang> add autosleep dubug log
94 autosleep_log("pm_suspend, autosleep_state: %d\n", autosleep_state);
7483b4a4 95 pm_suspend(autosleep_state);
6fa3eb70
S
96 }
97#endif // CONFIG_MTK_HIBERNATION
7483b4a4
RW
98 mutex_unlock(&autosleep_lock);
99
100 if (!pm_get_wakeup_count(&final_count, false))
101 goto out;
102
103 /*
104 * If the wakeup occured for an unknown reason, wait to prevent the
105 * system from trying to suspend and waking up in a tight loop.
106 */
107 if (final_count == initial_count)
108 schedule_timeout_uninterruptible(HZ / 2);
109
110 out:
6fa3eb70
S
111 //<20130327> <marc.huang> add autosleep dubug log
112 autosleep_log("queue_up_suspend_work again\n");
7483b4a4
RW
113 queue_up_suspend_work();
114}
115
116static DECLARE_WORK(suspend_work, try_to_suspend);
117
118void queue_up_suspend_work(void)
119{
ed1ac6e9 120 if (autosleep_state > PM_SUSPEND_ON)
6fa3eb70
S
121 {
122 //<20130327> <marc.huang> add autosleep dubug log
123 autosleep_log("autosleep_state: %d\n", autosleep_state);
7483b4a4 124 queue_work(autosleep_wq, &suspend_work);
6fa3eb70 125 }
7483b4a4
RW
126}
127
128suspend_state_t pm_autosleep_state(void)
129{
130 return autosleep_state;
131}
132
133int pm_autosleep_lock(void)
134{
135 return mutex_lock_interruptible(&autosleep_lock);
136}
137
138void pm_autosleep_unlock(void)
139{
140 mutex_unlock(&autosleep_lock);
141}
142
143int pm_autosleep_set_state(suspend_state_t state)
144{
145
146#ifndef CONFIG_HIBERNATION
147 if (state >= PM_SUSPEND_MAX)
148 return -EINVAL;
149#endif
150
151 __pm_stay_awake(autosleep_ws);
152
153 mutex_lock(&autosleep_lock);
154
155 autosleep_state = state;
156
157 __pm_relax(autosleep_ws);
158
55850945 159 if (state > PM_SUSPEND_ON) {
6fa3eb70
S
160 //<20130327> <marc.huang> add autosleep dubug log
161 autosleep_log("pm_wakep_autosleep_enabled(true)\n");
55850945 162 pm_wakep_autosleep_enabled(true);
7483b4a4 163 queue_up_suspend_work();
55850945 164 } else {
6fa3eb70
S
165 //<20130327> <marc.huang> add autosleep dubug log
166 autosleep_log("pm_wakep_autosleep_enabled(false)\n");
55850945
RW
167 pm_wakep_autosleep_enabled(false);
168 }
7483b4a4
RW
169
170 mutex_unlock(&autosleep_lock);
171 return 0;
172}
173
174int __init pm_autosleep_init(void)
175{
176 autosleep_ws = wakeup_source_register("autosleep");
177 if (!autosleep_ws)
178 return -ENOMEM;
179
180 autosleep_wq = alloc_ordered_workqueue("autosleep", 0);
181 if (autosleep_wq)
182 return 0;
183
184 wakeup_source_unregister(autosleep_ws);
185 return -ENOMEM;
186}