]>
Commit | Line | Data |
---|---|---|
95857638 | 1 | // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0 |
1da177e4 LT |
2 | /****************************************************************************** |
3 | * | |
70958576 BM |
4 | * Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the |
5 | * original/legacy sleep/PM registers. | |
1da177e4 | 6 | * |
da6f8320 | 7 | * Copyright (C) 2000 - 2018, Intel Corp. |
1da177e4 | 8 | * |
95857638 | 9 | *****************************************************************************/ |
1da177e4 | 10 | |
1da177e4 | 11 | #include <acpi/acpi.h> |
e2f7a777 | 12 | #include "accommon.h" |
1da177e4 LT |
13 | |
14 | #define _COMPONENT ACPI_HARDWARE | |
4be44fcd | 15 | ACPI_MODULE_NAME("hwsleep") |
1da177e4 | 16 | |
70958576 | 17 | #if (!ACPI_REDUCED_HARDWARE) /* Entire module */ |
44f6c012 | 18 | /******************************************************************************* |
1da177e4 | 19 | * |
2feec47d | 20 | * FUNCTION: acpi_hw_legacy_sleep |
1da177e4 LT |
21 | * |
22 | * PARAMETERS: sleep_state - Which sleep state to enter | |
23 | * | |
24 | * RETURN: Status | |
25 | * | |
2feec47d | 26 | * DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers |
1da177e4 LT |
27 | * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED |
28 | * | |
29 | ******************************************************************************/ | |
3f6f49c7 | 30 | acpi_status acpi_hw_legacy_sleep(u8 sleep_state) |
1da177e4 | 31 | { |
4be44fcd LB |
32 | struct acpi_bit_register_info *sleep_type_reg_info; |
33 | struct acpi_bit_register_info *sleep_enable_reg_info; | |
2feec47d BM |
34 | u32 pm1a_control; |
35 | u32 pm1b_control; | |
4be44fcd LB |
36 | u32 in_value; |
37 | acpi_status status; | |
1da177e4 | 38 | |
2feec47d | 39 | ACPI_FUNCTION_TRACE(hw_legacy_sleep); |
1da177e4 | 40 | |
4be44fcd | 41 | sleep_type_reg_info = |
82d79b86 | 42 | acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE); |
4be44fcd LB |
43 | sleep_enable_reg_info = |
44 | acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE); | |
1da177e4 LT |
45 | |
46 | /* Clear wake status */ | |
47 | ||
1fad8738 BM |
48 | status = acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, |
49 | ACPI_CLEAR_STATUS); | |
4be44fcd LB |
50 | if (ACPI_FAILURE(status)) { |
51 | return_ACPI_STATUS(status); | |
1da177e4 LT |
52 | } |
53 | ||
1da177e4 | 54 | /* |
18996f2d | 55 | * 1) Disable all GPEs |
1da177e4 LT |
56 | * 2) Enable all wakeup GPEs |
57 | */ | |
1d99967b AS |
58 | status = acpi_hw_disable_all_gpes(); |
59 | if (ACPI_FAILURE(status)) { | |
60 | return_ACPI_STATUS(status); | |
61 | } | |
1da177e4 LT |
62 | acpi_gbl_system_awake_and_running = FALSE; |
63 | ||
4be44fcd LB |
64 | status = acpi_hw_enable_all_wakeup_gpes(); |
65 | if (ACPI_FAILURE(status)) { | |
66 | return_ACPI_STATUS(status); | |
1da177e4 LT |
67 | } |
68 | ||
69 | /* Get current value of PM1A control */ | |
70 | ||
32c9ef99 BM |
71 | status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, |
72 | &pm1a_control); | |
4be44fcd LB |
73 | if (ACPI_FAILURE(status)) { |
74 | return_ACPI_STATUS(status); | |
1da177e4 | 75 | } |
4be44fcd | 76 | ACPI_DEBUG_PRINT((ACPI_DB_INIT, |
b27d6597 | 77 | "Entering sleep state [S%u]\n", sleep_state)); |
1da177e4 | 78 | |
32c9ef99 | 79 | /* Clear the SLP_EN and SLP_TYP fields */ |
1da177e4 | 80 | |
32c9ef99 BM |
81 | pm1a_control &= ~(sleep_type_reg_info->access_bit_mask | |
82 | sleep_enable_reg_info->access_bit_mask); | |
83 | pm1b_control = pm1a_control; | |
1da177e4 | 84 | |
32c9ef99 | 85 | /* Insert the SLP_TYP bits */ |
1da177e4 | 86 | |
32c9ef99 | 87 | pm1a_control |= |
4be44fcd | 88 | (acpi_gbl_sleep_type_a << sleep_type_reg_info->bit_position); |
32c9ef99 | 89 | pm1b_control |= |
4be44fcd | 90 | (acpi_gbl_sleep_type_b << sleep_type_reg_info->bit_position); |
1da177e4 LT |
91 | |
92 | /* | |
93 | * We split the writes of SLP_TYP and SLP_EN to workaround | |
94 | * poorly implemented hardware. | |
95 | */ | |
96 | ||
32c9ef99 | 97 | /* Write #1: write the SLP_TYP data to the PM1 Control registers */ |
1da177e4 | 98 | |
32c9ef99 | 99 | status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control); |
4be44fcd LB |
100 | if (ACPI_FAILURE(status)) { |
101 | return_ACPI_STATUS(status); | |
1da177e4 LT |
102 | } |
103 | ||
32c9ef99 | 104 | /* Insert the sleep enable (SLP_EN) bit */ |
1da177e4 | 105 | |
32c9ef99 BM |
106 | pm1a_control |= sleep_enable_reg_info->access_bit_mask; |
107 | pm1b_control |= sleep_enable_reg_info->access_bit_mask; | |
1da177e4 | 108 | |
32c9ef99 | 109 | /* Flush caches, as per ACPI specification */ |
1da177e4 | 110 | |
4be44fcd | 111 | ACPI_FLUSH_CPU_CACHE(); |
1da177e4 | 112 | |
0fc5e8f4 LZ |
113 | status = acpi_os_enter_sleep(sleep_state, pm1a_control, pm1b_control); |
114 | if (status == AE_CTRL_TERMINATE) { | |
09f98a82 | 115 | return_ACPI_STATUS(AE_OK); |
0fc5e8f4 LZ |
116 | } |
117 | if (ACPI_FAILURE(status)) { | |
09f98a82 | 118 | return_ACPI_STATUS(status); |
0fc5e8f4 LZ |
119 | } |
120 | ||
32c9ef99 | 121 | /* Write #2: Write both SLP_TYP + SLP_EN */ |
1da177e4 | 122 | |
32c9ef99 | 123 | status = acpi_hw_write_pm1_control(pm1a_control, pm1b_control); |
4be44fcd LB |
124 | if (ACPI_FAILURE(status)) { |
125 | return_ACPI_STATUS(status); | |
1da177e4 LT |
126 | } |
127 | ||
128 | if (sleep_state > ACPI_STATE_S3) { | |
129 | /* | |
44f6c012 RM |
130 | * We wanted to sleep > S3, but it didn't happen (by virtue of the |
131 | * fact that we are still executing!) | |
1da177e4 | 132 | * |
44f6c012 RM |
133 | * Wait ten seconds, then try again. This is to get S4/S5 to work on |
134 | * all machines. | |
1da177e4 | 135 | * |
d4913dc6 BM |
136 | * We wait so long to allow chipsets that poll this reg very slowly |
137 | * to still read the right value. Ideally, this block would go | |
1da177e4 LT |
138 | * away entirely. |
139 | */ | |
c41679a4 | 140 | acpi_os_stall(10 * ACPI_USEC_PER_SEC); |
4be44fcd | 141 | |
d30dc9ab | 142 | status = acpi_hw_register_write(ACPI_REGISTER_PM1_CONTROL, |
4be44fcd LB |
143 | sleep_enable_reg_info-> |
144 | access_bit_mask); | |
145 | if (ACPI_FAILURE(status)) { | |
146 | return_ACPI_STATUS(status); | |
1da177e4 LT |
147 | } |
148 | } | |
149 | ||
2feec47d | 150 | /* Wait for transition back to Working State */ |
1da177e4 LT |
151 | |
152 | do { | |
50ffba1b BM |
153 | status = |
154 | acpi_read_bit_register(ACPI_BITREG_WAKE_STATUS, &in_value); | |
4be44fcd LB |
155 | if (ACPI_FAILURE(status)) { |
156 | return_ACPI_STATUS(status); | |
1da177e4 | 157 | } |
2feec47d | 158 | |
1da177e4 LT |
159 | } while (!in_value); |
160 | ||
4be44fcd | 161 | return_ACPI_STATUS(AE_OK); |
1da177e4 | 162 | } |
1da177e4 | 163 | |
44f6c012 | 164 | /******************************************************************************* |
1da177e4 | 165 | * |
2feec47d | 166 | * FUNCTION: acpi_hw_legacy_wake_prep |
1da177e4 | 167 | * |
2feec47d | 168 | * PARAMETERS: sleep_state - Which sleep state we just exited |
1da177e4 LT |
169 | * |
170 | * RETURN: Status | |
171 | * | |
c95d47a8 RW |
172 | * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a |
173 | * sleep. | |
2feec47d | 174 | * Called with interrupts ENABLED. |
1da177e4 LT |
175 | * |
176 | ******************************************************************************/ | |
2feec47d | 177 | |
3f6f49c7 | 178 | acpi_status acpi_hw_legacy_wake_prep(u8 sleep_state) |
1da177e4 | 179 | { |
4be44fcd LB |
180 | acpi_status status; |
181 | struct acpi_bit_register_info *sleep_type_reg_info; | |
182 | struct acpi_bit_register_info *sleep_enable_reg_info; | |
32c9ef99 BM |
183 | u32 pm1a_control; |
184 | u32 pm1b_control; | |
1da177e4 | 185 | |
2feec47d | 186 | ACPI_FUNCTION_TRACE(hw_legacy_wake_prep); |
1da177e4 LT |
187 | |
188 | /* | |
189 | * Set SLP_TYPE and SLP_EN to state S0. | |
190 | * This is unclear from the ACPI Spec, but it is required | |
191 | * by some machines. | |
192 | */ | |
4be44fcd LB |
193 | status = acpi_get_sleep_type_data(ACPI_STATE_S0, |
194 | &acpi_gbl_sleep_type_a, | |
195 | &acpi_gbl_sleep_type_b); | |
196 | if (ACPI_SUCCESS(status)) { | |
197 | sleep_type_reg_info = | |
82d79b86 | 198 | acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_TYPE); |
4be44fcd LB |
199 | sleep_enable_reg_info = |
200 | acpi_hw_get_bit_register_info(ACPI_BITREG_SLEEP_ENABLE); | |
1da177e4 LT |
201 | |
202 | /* Get current value of PM1A control */ | |
203 | ||
d30dc9ab | 204 | status = acpi_hw_register_read(ACPI_REGISTER_PM1_CONTROL, |
32c9ef99 | 205 | &pm1a_control); |
4be44fcd | 206 | if (ACPI_SUCCESS(status)) { |
52fc0b02 | 207 | |
32c9ef99 | 208 | /* Clear the SLP_EN and SLP_TYP fields */ |
1da177e4 | 209 | |
32c9ef99 BM |
210 | pm1a_control &= ~(sleep_type_reg_info->access_bit_mask | |
211 | sleep_enable_reg_info-> | |
212 | access_bit_mask); | |
213 | pm1b_control = pm1a_control; | |
1da177e4 | 214 | |
32c9ef99 | 215 | /* Insert the SLP_TYP bits */ |
1da177e4 | 216 | |
d4913dc6 BM |
217 | pm1a_control |= (acpi_gbl_sleep_type_a << |
218 | sleep_type_reg_info->bit_position); | |
219 | pm1b_control |= (acpi_gbl_sleep_type_b << | |
220 | sleep_type_reg_info->bit_position); | |
1da177e4 | 221 | |
32c9ef99 | 222 | /* Write the control registers and ignore any errors */ |
1da177e4 | 223 | |
32c9ef99 BM |
224 | (void)acpi_hw_write_pm1_control(pm1a_control, |
225 | pm1b_control); | |
1da177e4 LT |
226 | } |
227 | } | |
228 | ||
c95d47a8 RW |
229 | return_ACPI_STATUS(status); |
230 | } | |
231 | ||
232 | /******************************************************************************* | |
233 | * | |
2feec47d | 234 | * FUNCTION: acpi_hw_legacy_wake |
c95d47a8 RW |
235 | * |
236 | * PARAMETERS: sleep_state - Which sleep state we just exited | |
237 | * | |
238 | * RETURN: Status | |
239 | * | |
240 | * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep | |
241 | * Called with interrupts ENABLED. | |
242 | * | |
243 | ******************************************************************************/ | |
2feec47d | 244 | |
3f6f49c7 | 245 | acpi_status acpi_hw_legacy_wake(u8 sleep_state) |
c95d47a8 | 246 | { |
c95d47a8 RW |
247 | acpi_status status; |
248 | ||
2feec47d | 249 | ACPI_FUNCTION_TRACE(hw_legacy_wake); |
c95d47a8 | 250 | |
1da177e4 LT |
251 | /* Ensure enter_sleep_state_prep -> enter_sleep_state ordering */ |
252 | ||
253 | acpi_gbl_sleep_type_a = ACPI_SLEEP_TYPE_INVALID; | |
4efeeecd | 254 | acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WAKING); |
1da177e4 | 255 | |
1da177e4 | 256 | /* |
79d2dfaa TR |
257 | * GPEs must be enabled before _WAK is called as GPEs |
258 | * might get fired there | |
259 | * | |
1da177e4 | 260 | * Restore the GPEs: |
18996f2d | 261 | * 1) Disable all GPEs |
1da177e4 LT |
262 | * 2) Enable all runtime GPEs |
263 | */ | |
4be44fcd LB |
264 | status = acpi_hw_disable_all_gpes(); |
265 | if (ACPI_FAILURE(status)) { | |
266 | return_ACPI_STATUS(status); | |
1da177e4 | 267 | } |
2feec47d | 268 | |
4be44fcd LB |
269 | status = acpi_hw_enable_all_runtime_gpes(); |
270 | if (ACPI_FAILURE(status)) { | |
271 | return_ACPI_STATUS(status); | |
1da177e4 LT |
272 | } |
273 | ||
2feec47d BM |
274 | /* |
275 | * Now we can execute _WAK, etc. Some machines require that the GPEs | |
276 | * are enabled before the wake methods are executed. | |
277 | */ | |
4efeeecd | 278 | acpi_hw_execute_sleep_method(METHOD_PATHNAME__WAK, sleep_state); |
79d2dfaa | 279 | |
a68823ee | 280 | /* |
2feec47d BM |
281 | * Some BIOS code assumes that WAK_STS will be cleared on resume |
282 | * and use it to determine whether the system is rebooting or | |
283 | * resuming. Clear WAK_STS for compatibility. | |
a68823ee | 284 | */ |
739dcbb9 LZ |
285 | (void)acpi_write_bit_register(ACPI_BITREG_WAKE_STATUS, |
286 | ACPI_CLEAR_STATUS); | |
79d2dfaa TR |
287 | acpi_gbl_system_awake_and_running = TRUE; |
288 | ||
1da177e4 LT |
289 | /* Enable power button */ |
290 | ||
4be44fcd | 291 | (void) |
50ffba1b | 292 | acpi_write_bit_register(acpi_gbl_fixed_event_info |
2feec47d BM |
293 | [ACPI_EVENT_POWER_BUTTON]. |
294 | enable_register_id, ACPI_ENABLE_EVENT); | |
44f6c012 | 295 | |
4be44fcd | 296 | (void) |
50ffba1b | 297 | acpi_write_bit_register(acpi_gbl_fixed_event_info |
2feec47d BM |
298 | [ACPI_EVENT_POWER_BUTTON]. |
299 | status_register_id, ACPI_CLEAR_STATUS); | |
1da177e4 | 300 | |
4efeeecd | 301 | acpi_hw_execute_sleep_method(METHOD_PATHNAME__SST, ACPI_SST_WORKING); |
4be44fcd | 302 | return_ACPI_STATUS(status); |
1da177e4 | 303 | } |
8313524a | 304 | |
33620c54 | 305 | #endif /* !ACPI_REDUCED_HARDWARE */ |