aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/sleep
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2008-06-12 17:24:06 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2008-06-12 17:25:09 -0400
commitd8f3de0d2412bb91639cfefc5b3c79dbf3812212 (patch)
tree05d0530d06e898b7eeac5c781ee9f00972a7f67a /drivers/acpi/sleep
parent53eb2fbeb9e68e1a9a23945de8450999c46270ce (diff)
Suspend-related patches for 2.6.27
ACPI PM: Add possibility to change suspend sequence There are some systems out there that don't work correctly with our current suspend/hibernation code ordering. Provide a workaround for these systems allowing them to pass 'acpi_sleep=old_ordering' in the kernel command line so that it will use the pre-ACPI 2.0 ("old") suspend code ordering. Unfortunately, this requires us to add a platform hook to the resuming of devices for recovering the platform in case one of the device drivers' .suspend() routines returns error code. Namely, ACPI 1.0 specifies that _PTS should be called before suspending devices, but _WAK still should be called before resuming them in order to undo the changes made by _PTS. However, if there is an error during suspending devices, they are automatically resumed without returning control to the PM core, so the _WAK has to be called from within device_resume() in that cases. The patch also reorders and refactors the ACPI suspend/hibernation code to avoid duplication as far as reasonably possible. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Acked-by: Pavel Machek <pavel@suse.cz> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
Diffstat (limited to 'drivers/acpi/sleep')
-rw-r--r--drivers/acpi/sleep/main.c276
1 files changed, 163 insertions, 113 deletions
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 0f2caea2fc83..4addf8ad50ae 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -24,10 +24,6 @@
24 24
25u8 sleep_states[ACPI_S_STATE_COUNT]; 25u8 sleep_states[ACPI_S_STATE_COUNT];
26 26
27#ifdef CONFIG_PM_SLEEP
28static u32 acpi_target_sleep_state = ACPI_STATE_S0;
29#endif
30
31static int acpi_sleep_prepare(u32 acpi_state) 27static int acpi_sleep_prepare(u32 acpi_state)
32{ 28{
33#ifdef CONFIG_ACPI_SLEEP 29#ifdef CONFIG_ACPI_SLEEP
@@ -50,9 +46,96 @@ static int acpi_sleep_prepare(u32 acpi_state)
50 return 0; 46 return 0;
51} 47}
52 48
53#ifdef CONFIG_SUSPEND 49#ifdef CONFIG_PM_SLEEP
54static struct platform_suspend_ops acpi_suspend_ops; 50static u32 acpi_target_sleep_state = ACPI_STATE_S0;
51
52/*
53 * ACPI 1.0 wants us to execute _PTS before suspending devices, so we allow the
54 * user to request that behavior by using the 'acpi_old_suspend_ordering'
55 * kernel command line option that causes the following variable to be set.
56 */
57static bool old_suspend_ordering;
58
59void __init acpi_old_suspend_ordering(void)
60{
61 old_suspend_ordering = true;
62}
63
64/**
65 * acpi_pm_disable_gpes - Disable the GPEs.
66 */
67static int acpi_pm_disable_gpes(void)
68{
69 acpi_hw_disable_all_gpes();
70 return 0;
71}
72
73/**
74 * __acpi_pm_prepare - Prepare the platform to enter the target state.
75 *
76 * If necessary, set the firmware waking vector and do arch-specific
77 * nastiness to get the wakeup code to the waking vector.
78 */
79static int __acpi_pm_prepare(void)
80{
81 int error = acpi_sleep_prepare(acpi_target_sleep_state);
82
83 if (error)
84 acpi_target_sleep_state = ACPI_STATE_S0;
85 return error;
86}
87
88/**
89 * acpi_pm_prepare - Prepare the platform to enter the target sleep
90 * state and disable the GPEs.
91 */
92static int acpi_pm_prepare(void)
93{
94 int error = __acpi_pm_prepare();
95
96 if (!error)
97 acpi_hw_disable_all_gpes();
98 return error;
99}
100
101/**
102 * acpi_pm_finish - Instruct the platform to leave a sleep state.
103 *
104 * This is called after we wake back up (or if entering the sleep state
105 * failed).
106 */
107static void acpi_pm_finish(void)
108{
109 u32 acpi_state = acpi_target_sleep_state;
110
111 if (acpi_state == ACPI_STATE_S0)
112 return;
55 113
114 printk(KERN_INFO PREFIX "Waking up from system sleep state S%d\n",
115 acpi_state);
116 acpi_disable_wakeup_device(acpi_state);
117 acpi_leave_sleep_state(acpi_state);
118
119 /* reset firmware waking vector */
120 acpi_set_firmware_waking_vector((acpi_physical_address) 0);
121
122 acpi_target_sleep_state = ACPI_STATE_S0;
123}
124
125/**
126 * acpi_pm_end - Finish up suspend sequence.
127 */
128static void acpi_pm_end(void)
129{
130 /*
131 * This is necessary in case acpi_pm_finish() is not called during a
132 * failing transition to a sleep state.
133 */
134 acpi_target_sleep_state = ACPI_STATE_S0;
135}
136#endif /* CONFIG_PM_SLEEP */
137
138#ifdef CONFIG_SUSPEND
56extern void do_suspend_lowlevel(void); 139extern void do_suspend_lowlevel(void);
57 140
58static u32 acpi_suspend_states[] = { 141static u32 acpi_suspend_states[] = {
@@ -66,7 +149,6 @@ static u32 acpi_suspend_states[] = {
66 * acpi_suspend_begin - Set the target system sleep state to the state 149 * acpi_suspend_begin - Set the target system sleep state to the state
67 * associated with given @pm_state, if supported. 150 * associated with given @pm_state, if supported.
68 */ 151 */
69
70static int acpi_suspend_begin(suspend_state_t pm_state) 152static int acpi_suspend_begin(suspend_state_t pm_state)
71{ 153{
72 u32 acpi_state = acpi_suspend_states[pm_state]; 154 u32 acpi_state = acpi_suspend_states[pm_state];
@@ -83,25 +165,6 @@ static int acpi_suspend_begin(suspend_state_t pm_state)
83} 165}
84 166
85/** 167/**
86 * acpi_suspend_prepare - Do preliminary suspend work.
87 *
88 * If necessary, set the firmware waking vector and do arch-specific
89 * nastiness to get the wakeup code to the waking vector.
90 */
91
92static int acpi_suspend_prepare(void)
93{
94 int error = acpi_sleep_prepare(acpi_target_sleep_state);
95
96 if (error) {
97 acpi_target_sleep_state = ACPI_STATE_S0;
98 return error;
99 }
100
101 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
102}
103
104/**
105 * acpi_suspend_enter - Actually enter a sleep state. 168 * acpi_suspend_enter - Actually enter a sleep state.
106 * @pm_state: ignored 169 * @pm_state: ignored
107 * 170 *
@@ -109,7 +172,6 @@ static int acpi_suspend_prepare(void)
109 * assembly, which in turn call acpi_enter_sleep_state(). 172 * assembly, which in turn call acpi_enter_sleep_state().
110 * It's unfortunate, but it works. Please fix if you're feeling frisky. 173 * It's unfortunate, but it works. Please fix if you're feeling frisky.
111 */ 174 */
112
113static int acpi_suspend_enter(suspend_state_t pm_state) 175static int acpi_suspend_enter(suspend_state_t pm_state)
114{ 176{
115 acpi_status status = AE_OK; 177 acpi_status status = AE_OK;
@@ -166,39 +228,6 @@ static int acpi_suspend_enter(suspend_state_t pm_state)
166 return ACPI_SUCCESS(status) ? 0 : -EFAULT; 228 return ACPI_SUCCESS(status) ? 0 : -EFAULT;
167} 229}
168 230
169/**
170 * acpi_suspend_finish - Instruct the platform to leave a sleep state.
171 *
172 * This is called after we wake back up (or if entering the sleep state
173 * failed).
174 */
175
176static void acpi_suspend_finish(void)
177{
178 u32 acpi_state = acpi_target_sleep_state;
179
180 acpi_disable_wakeup_device(acpi_state);
181 acpi_leave_sleep_state(acpi_state);
182
183 /* reset firmware waking vector */
184 acpi_set_firmware_waking_vector((acpi_physical_address) 0);
185
186 acpi_target_sleep_state = ACPI_STATE_S0;
187}
188
189/**
190 * acpi_suspend_end - Finish up suspend sequence.
191 */
192
193static void acpi_suspend_end(void)
194{
195 /*
196 * This is necessary in case acpi_suspend_finish() is not called during a
197 * failing transition to a sleep state.
198 */
199 acpi_target_sleep_state = ACPI_STATE_S0;
200}
201
202static int acpi_suspend_state_valid(suspend_state_t pm_state) 231static int acpi_suspend_state_valid(suspend_state_t pm_state)
203{ 232{
204 u32 acpi_state; 233 u32 acpi_state;
@@ -218,10 +247,39 @@ static int acpi_suspend_state_valid(suspend_state_t pm_state)
218static struct platform_suspend_ops acpi_suspend_ops = { 247static struct platform_suspend_ops acpi_suspend_ops = {
219 .valid = acpi_suspend_state_valid, 248 .valid = acpi_suspend_state_valid,
220 .begin = acpi_suspend_begin, 249 .begin = acpi_suspend_begin,
221 .prepare = acpi_suspend_prepare, 250 .prepare = acpi_pm_prepare,
251 .enter = acpi_suspend_enter,
252 .finish = acpi_pm_finish,
253 .end = acpi_pm_end,
254};
255
256/**
257 * acpi_suspend_begin_old - Set the target system sleep state to the
258 * state associated with given @pm_state, if supported, and
259 * execute the _PTS control method. This function is used if the
260 * pre-ACPI 2.0 suspend ordering has been requested.
261 */
262static int acpi_suspend_begin_old(suspend_state_t pm_state)
263{
264 int error = acpi_suspend_begin(pm_state);
265
266 if (!error)
267 error = __acpi_pm_prepare();
268 return error;
269}
270
271/*
272 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
273 * been requested.
274 */
275static struct platform_suspend_ops acpi_suspend_ops_old = {
276 .valid = acpi_suspend_state_valid,
277 .begin = acpi_suspend_begin_old,
278 .prepare = acpi_pm_disable_gpes,
222 .enter = acpi_suspend_enter, 279 .enter = acpi_suspend_enter,
223 .finish = acpi_suspend_finish, 280 .finish = acpi_pm_finish,
224 .end = acpi_suspend_end, 281 .end = acpi_pm_end,
282 .recover = acpi_pm_finish,
225}; 283};
226#endif /* CONFIG_SUSPEND */ 284#endif /* CONFIG_SUSPEND */
227 285
@@ -229,22 +287,9 @@ static struct platform_suspend_ops acpi_suspend_ops = {
229static int acpi_hibernation_begin(void) 287static int acpi_hibernation_begin(void)
230{ 288{
231 acpi_target_sleep_state = ACPI_STATE_S4; 289 acpi_target_sleep_state = ACPI_STATE_S4;
232
233 return 0; 290 return 0;
234} 291}
235 292
236static int acpi_hibernation_prepare(void)
237{
238 int error = acpi_sleep_prepare(ACPI_STATE_S4);
239
240 if (error) {
241 acpi_target_sleep_state = ACPI_STATE_S0;
242 return error;
243 }
244
245 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
246}
247
248static int acpi_hibernation_enter(void) 293static int acpi_hibernation_enter(void)
249{ 294{
250 acpi_status status = AE_OK; 295 acpi_status status = AE_OK;
@@ -274,52 +319,55 @@ static void acpi_hibernation_leave(void)
274 acpi_leave_sleep_state_prep(ACPI_STATE_S4); 319 acpi_leave_sleep_state_prep(ACPI_STATE_S4);
275} 320}
276 321
277static void acpi_hibernation_finish(void) 322static void acpi_pm_enable_gpes(void)
278{ 323{
279 acpi_disable_wakeup_device(ACPI_STATE_S4); 324 acpi_hw_enable_all_runtime_gpes();
280 acpi_leave_sleep_state(ACPI_STATE_S4);
281
282 /* reset firmware waking vector */
283 acpi_set_firmware_waking_vector((acpi_physical_address) 0);
284
285 acpi_target_sleep_state = ACPI_STATE_S0;
286} 325}
287 326
288static void acpi_hibernation_end(void) 327static struct platform_hibernation_ops acpi_hibernation_ops = {
289{ 328 .begin = acpi_hibernation_begin,
290 /* 329 .end = acpi_pm_end,
291 * This is necessary in case acpi_hibernation_finish() is not called 330 .pre_snapshot = acpi_pm_prepare,
292 * during a failing transition to the sleep state. 331 .finish = acpi_pm_finish,
293 */ 332 .prepare = acpi_pm_prepare,
294 acpi_target_sleep_state = ACPI_STATE_S0; 333 .enter = acpi_hibernation_enter,
295} 334 .leave = acpi_hibernation_leave,
335 .pre_restore = acpi_pm_disable_gpes,
336 .restore_cleanup = acpi_pm_enable_gpes,
337};
296 338
297static int acpi_hibernation_pre_restore(void) 339/**
340 * acpi_hibernation_begin_old - Set the target system sleep state to
341 * ACPI_STATE_S4 and execute the _PTS control method. This
342 * function is used if the pre-ACPI 2.0 suspend ordering has been
343 * requested.
344 */
345static int acpi_hibernation_begin_old(void)
298{ 346{
299 acpi_status status; 347 int error = acpi_sleep_prepare(ACPI_STATE_S4);
300
301 status = acpi_hw_disable_all_gpes();
302
303 return ACPI_SUCCESS(status) ? 0 : -EFAULT;
304}
305 348
306static void acpi_hibernation_restore_cleanup(void) 349 if (!error)
307{ 350 acpi_target_sleep_state = ACPI_STATE_S4;
308 acpi_hw_enable_all_runtime_gpes(); 351 return error;
309} 352}
310 353
311static struct platform_hibernation_ops acpi_hibernation_ops = { 354/*
312 .begin = acpi_hibernation_begin, 355 * The following callbacks are used if the pre-ACPI 2.0 suspend ordering has
313 .end = acpi_hibernation_end, 356 * been requested.
314 .pre_snapshot = acpi_hibernation_prepare, 357 */
315 .finish = acpi_hibernation_finish, 358static struct platform_hibernation_ops acpi_hibernation_ops_old = {
316 .prepare = acpi_hibernation_prepare, 359 .begin = acpi_hibernation_begin_old,
360 .end = acpi_pm_end,
361 .pre_snapshot = acpi_pm_disable_gpes,
362 .finish = acpi_pm_finish,
363 .prepare = acpi_pm_disable_gpes,
317 .enter = acpi_hibernation_enter, 364 .enter = acpi_hibernation_enter,
318 .leave = acpi_hibernation_leave, 365 .leave = acpi_hibernation_leave,
319 .pre_restore = acpi_hibernation_pre_restore, 366 .pre_restore = acpi_pm_disable_gpes,
320 .restore_cleanup = acpi_hibernation_restore_cleanup, 367 .restore_cleanup = acpi_pm_enable_gpes,
368 .recover = acpi_pm_finish,
321}; 369};
322#endif /* CONFIG_HIBERNATION */ 370#endif /* CONFIG_HIBERNATION */
323 371
324int acpi_suspend(u32 acpi_state) 372int acpi_suspend(u32 acpi_state)
325{ 373{
@@ -461,13 +509,15 @@ int __init acpi_sleep_init(void)
461 } 509 }
462 } 510 }
463 511
464 suspend_set_ops(&acpi_suspend_ops); 512 suspend_set_ops(old_suspend_ordering ?
513 &acpi_suspend_ops_old : &acpi_suspend_ops);
465#endif 514#endif
466 515
467#ifdef CONFIG_HIBERNATION 516#ifdef CONFIG_HIBERNATION
468 status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b); 517 status = acpi_get_sleep_type_data(ACPI_STATE_S4, &type_a, &type_b);
469 if (ACPI_SUCCESS(status)) { 518 if (ACPI_SUCCESS(status)) {
470 hibernation_set_ops(&acpi_hibernation_ops); 519 hibernation_set_ops(old_suspend_ordering ?
520 &acpi_hibernation_ops_old : &acpi_hibernation_ops);
471 sleep_states[ACPI_STATE_S4] = 1; 521 sleep_states[ACPI_STATE_S4] = 1;
472 printk(" S4"); 522 printk(" S4");
473 } 523 }