ACPI PM: Restore the 2.6.24 suspend ordering

Some time ago it turned out that our suspend code ordering broke some
NVidia-based systems that hung if _PTS was executed with one of the PCI
devices, specifically a USB controller, in a low power state.

Then, it was noticed that the suspend code ordering was not compliant
with ACPI 1.0, although it was compliant with ACPI 2.0 (and later), and
it was argued that the code had to be changed for that reason (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).

So we did, but evidently we did wrong, because it's now turning out that
some systems have been broken by this change. Refs:
http://bugzilla.kernel.org/show_bug.cgi?id=10340
https://bugzilla.novell.com/show_bug.cgi?id=374217#c16

[ I said at that time that something like this might happend, but the
majority of people involved thought that it was improbable due to the
necessity to preserve the compliance of hardware with ACPI 1.0. ]

This actually is a quite serious regression from 2.6.24.

Moreover, the ACPI 1.0 ordering of suspend code introduced another issue
that I have only noticed recently. Namely, if the suspend of one of
devices fails, the already suspended devices will be resumed without
executing _WAK before, which leads to problems on some systems (for
example, in such situations thermal management is broken on my HP
nx6325). Consequently, it also breaks suspend debugging on the affected
systems.

Note also, that the requirement to execute _PTS before suspending
devices does not really make sense, because the device in question may
be put into a low power state at run time for a reason unrelated to a
system-wide suspend.

For the reasons outlined above, the change of the suspend ordering
should be reverted, which is done by the patch below.

[ Felix Möller: "I am the reporter from the original Novell Bug:

https://bugzilla.novell.com/show_bug.cgi?id=374217

I just tried current git head (two hours ago) with the patch (the one
from the beginning of this thread) from Rafael and without it. With
the patch my MacBook does suspend without it does not." ]

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Tested-by: Felix Möller <felix@derklecks.de>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by Rafael J. Wysocki and committed by Linus Torvalds 7731ce63 cabce28e

+14 -62
-5
Documentation/kernel-parameters.txt
··· 170 acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA 171 Format: <irq>,<irq>... 172 173 - acpi_new_pts_ordering [HW,ACPI] 174 - Enforce the ACPI 2.0 ordering of the _PTS control 175 - method wrt putting devices into low power states 176 - default: pre ACPI 2.0 ordering of _PTS 177 - 178 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT 179 180 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
··· 170 acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA 171 Format: <irq>,<irq>... 172 173 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT 174 175 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
+14 -57
drivers/acpi/sleep/main.c
··· 26 27 #ifdef CONFIG_PM_SLEEP 28 static u32 acpi_target_sleep_state = ACPI_STATE_S0; 29 - static bool acpi_sleep_finish_wake_up; 30 - 31 - /* 32 - * ACPI 2.0 and later want us to execute _PTS after suspending devices, so we 33 - * allow the user to request that behavior by using the 'acpi_new_pts_ordering' 34 - * kernel command line option that causes the following variable to be set. 35 - */ 36 - static bool new_pts_ordering; 37 - 38 - static int __init acpi_new_pts_ordering(char *str) 39 - { 40 - new_pts_ordering = true; 41 - return 1; 42 - } 43 - __setup("acpi_new_pts_ordering", acpi_new_pts_ordering); 44 #endif 45 46 static int acpi_sleep_prepare(u32 acpi_state) ··· 76 77 if (sleep_states[acpi_state]) { 78 acpi_target_sleep_state = acpi_state; 79 - if (new_pts_ordering) 80 - return 0; 81 - 82 - error = acpi_sleep_prepare(acpi_state); 83 - if (error) 84 - acpi_target_sleep_state = ACPI_STATE_S0; 85 - else 86 - acpi_sleep_finish_wake_up = true; 87 } else { 88 printk(KERN_ERR "ACPI does not support this state: %d\n", 89 pm_state); ··· 93 94 static int acpi_pm_prepare(void) 95 { 96 - if (new_pts_ordering) { 97 - int error = acpi_sleep_prepare(acpi_target_sleep_state); 98 99 - if (error) { 100 - acpi_target_sleep_state = ACPI_STATE_S0; 101 - return error; 102 - } 103 - acpi_sleep_finish_wake_up = true; 104 } 105 106 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; ··· 186 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 187 188 acpi_target_sleep_state = ACPI_STATE_S0; 189 - acpi_sleep_finish_wake_up = false; 190 191 #ifdef CONFIG_X86 192 if (init_8259A_after_S1) { ··· 202 static void acpi_pm_end(void) 203 { 204 /* 205 - * This is necessary in case acpi_pm_finish() is not called directly 206 - * during a failing transition to a sleep state. 207 */ 208 - if (acpi_sleep_finish_wake_up) 209 - acpi_pm_finish(); 210 } 211 212 static int acpi_pm_state_valid(suspend_state_t pm_state) ··· 257 #ifdef CONFIG_HIBERNATION 258 static int acpi_hibernation_begin(void) 259 { 260 - int error; 261 - 262 acpi_target_sleep_state = ACPI_STATE_S4; 263 - if (new_pts_ordering) 264 - return 0; 265 266 - error = acpi_sleep_prepare(ACPI_STATE_S4); 267 - if (error) 268 - acpi_target_sleep_state = ACPI_STATE_S0; 269 - else 270 - acpi_sleep_finish_wake_up = true; 271 - 272 - return error; 273 } 274 275 static int acpi_hibernation_prepare(void) 276 { 277 - if (new_pts_ordering) { 278 - int error = acpi_sleep_prepare(ACPI_STATE_S4); 279 280 - if (error) { 281 - acpi_target_sleep_state = ACPI_STATE_S0; 282 - return error; 283 - } 284 - acpi_sleep_finish_wake_up = true; 285 } 286 287 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; ··· 312 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 313 314 acpi_target_sleep_state = ACPI_STATE_S0; 315 - acpi_sleep_finish_wake_up = false; 316 } 317 318 static void acpi_hibernation_end(void) 319 { 320 /* 321 * This is necessary in case acpi_hibernation_finish() is not called 322 - * directly during a failing transition to the sleep state. 323 */ 324 - if (acpi_sleep_finish_wake_up) 325 - acpi_hibernation_finish(); 326 } 327 328 static int acpi_hibernation_pre_restore(void)
··· 26 27 #ifdef CONFIG_PM_SLEEP 28 static u32 acpi_target_sleep_state = ACPI_STATE_S0; 29 #endif 30 31 static int acpi_sleep_prepare(u32 acpi_state) ··· 91 92 if (sleep_states[acpi_state]) { 93 acpi_target_sleep_state = acpi_state; 94 } else { 95 printk(KERN_ERR "ACPI does not support this state: %d\n", 96 pm_state); ··· 116 117 static int acpi_pm_prepare(void) 118 { 119 + int error = acpi_sleep_prepare(acpi_target_sleep_state); 120 121 + if (error) { 122 + acpi_target_sleep_state = ACPI_STATE_S0; 123 + return error; 124 } 125 126 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; ··· 212 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 213 214 acpi_target_sleep_state = ACPI_STATE_S0; 215 216 #ifdef CONFIG_X86 217 if (init_8259A_after_S1) { ··· 229 static void acpi_pm_end(void) 230 { 231 /* 232 + * This is necessary in case acpi_pm_finish() is not called during a 233 + * failing transition to a sleep state. 234 */ 235 + acpi_target_sleep_state = ACPI_STATE_S0; 236 } 237 238 static int acpi_pm_state_valid(suspend_state_t pm_state) ··· 285 #ifdef CONFIG_HIBERNATION 286 static int acpi_hibernation_begin(void) 287 { 288 acpi_target_sleep_state = ACPI_STATE_S4; 289 290 + return 0; 291 } 292 293 static int acpi_hibernation_prepare(void) 294 { 295 + int error = acpi_sleep_prepare(ACPI_STATE_S4); 296 297 + if (error) { 298 + acpi_target_sleep_state = ACPI_STATE_S0; 299 + return error; 300 } 301 302 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; ··· 353 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 354 355 acpi_target_sleep_state = ACPI_STATE_S0; 356 } 357 358 static void acpi_hibernation_end(void) 359 { 360 /* 361 * This is necessary in case acpi_hibernation_finish() is not called 362 + * during a failing transition to the sleep state. 363 */ 364 + acpi_target_sleep_state = ACPI_STATE_S0; 365 } 366 367 static int acpi_hibernation_pre_restore(void)