aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafael J. Wysocki <rjw@sisk.pl>2008-01-07 18:07:39 -0500
committerLen Brown <len.brown@intel.com>2008-02-01 18:30:58 -0500
commit60417f5976df029227450b46d7fa6f0e9b1e654c (patch)
treeb0faa81f4517aa41c6113824b59a47b87b4aea2f
parent3c1d2b6085d75df0691cec6a4a053c0aa55fe4c9 (diff)
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>
-rw-r--r--Documentation/kernel-parameters.txt5
-rw-r--r--drivers/acpi/sleep/main.c51
2 files changed, 43 insertions, 13 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 92c40d174355..cf3868956f1e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -168,6 +168,11 @@ and is between 256 and 4096 characters. It is defined in the file
168 acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA 168 acpi_irq_isa= [HW,ACPI] If irq_balance, mark listed IRQs used by ISA
169 Format: <irq>,<irq>... 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 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT 176 acpi_no_auto_ssdt [HW,ACPI] Disable automatic loading of SSDT
172 177
173 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS 178 acpi_os_name= [HW,ACPI] Tell ACPI BIOS the name of the OS
diff --git a/drivers/acpi/sleep/main.c b/drivers/acpi/sleep/main.c
index 198ff8a1529a..c37c4ead95c9 100644
--- a/drivers/acpi/sleep/main.c
+++ b/drivers/acpi/sleep/main.c
@@ -26,6 +26,21 @@ u8 sleep_states[ACPI_S_STATE_COUNT];
26 26
27#ifdef CONFIG_PM_SLEEP 27#ifdef CONFIG_PM_SLEEP
28static u32 acpi_target_sleep_state = ACPI_STATE_S0; 28static u32 acpi_target_sleep_state = ACPI_STATE_S0;
29static 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 */
36static bool new_pts_ordering;
37
38static 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#endif 44#endif
30 45
31int acpi_sleep_prepare(u32 acpi_state) 46int acpi_sleep_prepare(u32 acpi_state)
@@ -74,6 +89,14 @@ static int acpi_pm_begin(suspend_state_t pm_state)
74 89
75 if (sleep_states[acpi_state]) { 90 if (sleep_states[acpi_state]) {
76 acpi_target_sleep_state = acpi_state; 91 acpi_target_sleep_state = acpi_state;
92 if (new_pts_ordering)
93 return 0;
94
95 error = acpi_sleep_prepare(acpi_state);
96 if (error)
97 acpi_target_sleep_state = ACPI_STATE_S0;
98 else
99 acpi_sleep_finish_wake_up = true;
77 } else { 100 } else {
78 printk(KERN_ERR "ACPI does not support this state: %d\n", 101 printk(KERN_ERR "ACPI does not support this state: %d\n",
79 pm_state); 102 pm_state);
@@ -91,15 +114,17 @@ static int acpi_pm_begin(suspend_state_t pm_state)
91 114
92static int acpi_pm_prepare(void) 115static int acpi_pm_prepare(void)
93{ 116{
94 int error; 117 if (new_pts_ordering) {
118 int error = acpi_sleep_prepare(acpi_target_sleep_state);
95 119
96 error = acpi_sleep_prepare(acpi_target_sleep_state); 120 if (error) {
97 if (error) 121 acpi_target_sleep_state = ACPI_STATE_S0;
98 acpi_target_sleep_state = ACPI_STATE_S0; 122 return error;
99 else if (!ACPI_SUCCESS(acpi_hw_disable_all_gpes())) 123 }
100 error = -EFAULT; 124 acpi_sleep_finish_wake_up = true;
125 }
101 126
102 return error; 127 return ACPI_SUCCESS(acpi_hw_disable_all_gpes()) ? 0 : -EFAULT;
103} 128}
104 129
105/** 130/**
@@ -123,10 +148,8 @@ static int acpi_pm_enter(suspend_state_t pm_state)
123 if (acpi_state == ACPI_STATE_S3) { 148 if (acpi_state == ACPI_STATE_S3) {
124 int error = acpi_save_state_mem(); 149 int error = acpi_save_state_mem();
125 150
126 if (error) { 151 if (error)
127 acpi_target_sleep_state = ACPI_STATE_S0;
128 return error; 152 return error;
129 }
130 } 153 }
131 154
132 local_irq_save(flags); 155 local_irq_save(flags);
@@ -187,6 +210,7 @@ static void acpi_pm_finish(void)
187 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 210 acpi_set_firmware_waking_vector((acpi_physical_address) 0);
188 211
189 acpi_target_sleep_state = ACPI_STATE_S0; 212 acpi_target_sleep_state = ACPI_STATE_S0;
213 acpi_sleep_finish_wake_up = false;
190 214
191#ifdef CONFIG_X86 215#ifdef CONFIG_X86
192 if (init_8259A_after_S1) { 216 if (init_8259A_after_S1) {
@@ -203,10 +227,11 @@ static void acpi_pm_finish(void)
203static void acpi_pm_end(void) 227static void acpi_pm_end(void)
204{ 228{
205 /* 229 /*
206 * This is necessary in case acpi_pm_finish() is not called during a 230 * This is necessary in case acpi_pm_finish() is not called directly
207 * failing transition to a sleep state. 231 * during a failing transition to a sleep state.
208 */ 232 */
209 acpi_target_sleep_state = ACPI_STATE_S0; 233 if (acpi_sleep_finish_wake_up)
234 acpi_pm_finish();
210} 235}
211 236
212static int acpi_pm_state_valid(suspend_state_t pm_state) 237static int acpi_pm_state_valid(suspend_state_t pm_state)