ACPI suspend: Call _PTS before suspending devices

The ACPI 1.0 specification wants us to put devices into low power
states after executing the _PTS global control method, while ACPI
2.0 and later want us to do that in the reverse order. The current
suspend code follows ACPI 2.0 in that respect which causes some
ACPI 1.0x systems to hang during suspend (ref.
http://bugzilla.kernel.org/show_bug.cgi?id=9528).

Make the suspend code execute _PTS before putting devices into low
power states (ie. in accordance with ACPI 1.0x) and provide a command
line option to override the default if need be.

Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Len Brown <len.brown@intel.com>

authored by Rafael J. Wysocki and committed by Len Brown 60417f59 3c1d2b60

+43 -13
+5
Documentation/kernel-parameters.txt
··· 168 168 acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA 169 169 Format: <irq>,<irq>... 170 170 171 + acpi_new_pts_ordering [HW,ACPI] 172 + Enforce the ACPI 2.0 ordering of the _PTS control 173 + method wrt putting devices into low power states 174 + default: pre ACPI 2.0 ordering of _PTS 175 + 171 176 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT 172 177 173 178 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
+38 -13
drivers/acpi/sleep/main.c
··· 26 26 27 27 #ifdef CONFIG_PM_SLEEP 28 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); 29 44 #endif 30 45 31 46 int acpi_sleep_prepare(u32 acpi_state) ··· 89 74 90 75 if (sleep_states[acpi_state]) { 91 76 acpi_target_sleep_state = acpi_state; 77 + if (new_pts_ordering) 78 + return 0; 79 + 80 + error = acpi_sleep_prepare(acpi_state); 81 + if (error) 82 + acpi_target_sleep_state = ACPI_STATE_S0; 83 + else 84 + acpi_sleep_finish_wake_up = true; 92 85 } else { 93 86 printk(KERN_ERR "ACPI does not support this state: %d\n", 94 87 pm_state); ··· 114 91 115 92 static int acpi_pm_prepare(void) 116 93 { 117 - int error; 94 + if (new_pts_ordering) { 95 + int error = acpi_sleep_prepare(acpi_target_sleep_state); 118 96 119 - error = acpi_sleep_prepare(acpi_target_sleep_state); 120 - if (error) 121 - acpi_target_sleep_state = ACPI_STATE_S0; 122 - else if (!ACPI_SUCCESS(acpi_hw_disable_all_gpes())) 123 - error = -EFAULT; 97 + if (error) { 98 + acpi_target_sleep_state = ACPI_STATE_S0; 99 + return error; 100 + } 101 + acpi_sleep_finish_wake_up = true; 102 + } 124 103 125 - return error; 104 + return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT; 126 105 } 127 106 128 107 /** ··· 148 123 if (acpi_state == ACPI_STATE_S3) { 149 124 int error = acpi_save_state_mem(); 150 125 151 - if (error) { 152 - acpi_target_sleep_state = ACPI_STATE_S0; 126 + if (error) 153 127 return error; 154 - } 155 128 } 156 129 157 130 local_irq_save(flags); ··· 210 187 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 211 188 212 189 acpi_target_sleep_state = ACPI_STATE_S0; 190 + acpi_sleep_finish_wake_up = false; 213 191 214 192 #ifdef CONFIG_X86 215 193 if (init_8259A_after_S1) { ··· 227 203 static void acpi_pm_end(void) 228 204 { 229 205 /* 230 - * This is necessary in case acpi_pm_finish() is not called during a 231 - * failing transition to a sleep state. 206 + * This is necessary in case acpi_pm_finish() is not called directly 207 + * during a failing transition to a sleep state. 232 208 */ 233 - acpi_target_sleep_state = ACPI_STATE_S0; 209 + if (acpi_sleep_finish_wake_up) 210 + acpi_pm_finish(); 234 211 } 235 212 236 213 static int acpi_pm_state_valid(suspend_state_t pm_state)