1 |
/* $MidnightBSD$ */ |
2 |
/****************************************************************************** |
3 |
* |
4 |
* Name: hwsleep.c - ACPI Hardware Sleep/Wake Support functions for the |
5 |
* original/legacy sleep/PM registers. |
6 |
* |
7 |
*****************************************************************************/ |
8 |
|
9 |
/* |
10 |
* Copyright (C) 2000 - 2016, Intel Corp. |
11 |
* All rights reserved. |
12 |
* |
13 |
* Redistribution and use in source and binary forms, with or without |
14 |
* modification, are permitted provided that the following conditions |
15 |
* are met: |
16 |
* 1. Redistributions of source code must retain the above copyright |
17 |
* notice, this list of conditions, and the following disclaimer, |
18 |
* without modification. |
19 |
* 2. Redistributions in binary form must reproduce at minimum a disclaimer |
20 |
* substantially similar to the "NO WARRANTY" disclaimer below |
21 |
* ("Disclaimer") and any redistribution must be conditioned upon |
22 |
* including a substantially similar Disclaimer requirement for further |
23 |
* binary redistribution. |
24 |
* 3. Neither the names of the above-listed copyright holders nor the names |
25 |
* of any contributors may be used to endorse or promote products derived |
26 |
* from this software without specific prior written permission. |
27 |
* |
28 |
* Alternatively, this software may be distributed under the terms of the |
29 |
* GNU General Public License ("GPL") version 2 as published by the Free |
30 |
* Software Foundation. |
31 |
* |
32 |
* NO WARRANTY |
33 |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
34 |
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
35 |
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
36 |
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
37 |
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
38 |
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
39 |
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
40 |
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
41 |
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
42 |
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
43 |
* POSSIBILITY OF SUCH DAMAGES. |
44 |
*/ |
45 |
|
46 |
#include <contrib/dev/acpica/include/acpi.h> |
47 |
#include <contrib/dev/acpica/include/accommon.h> |
48 |
|
49 |
#define _COMPONENT ACPI_HARDWARE |
50 |
ACPI_MODULE_NAME ("hwsleep") |
51 |
|
52 |
|
53 |
#if (!ACPI_REDUCED_HARDWARE) /* Entire module */ |
54 |
/******************************************************************************* |
55 |
* |
56 |
* FUNCTION: AcpiHwLegacySleep |
57 |
* |
58 |
* PARAMETERS: SleepState - Which sleep state to enter |
59 |
* |
60 |
* RETURN: Status |
61 |
* |
62 |
* DESCRIPTION: Enter a system sleep state via the legacy FADT PM registers |
63 |
* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED |
64 |
* |
65 |
******************************************************************************/ |
66 |
|
67 |
ACPI_STATUS |
68 |
AcpiHwLegacySleep ( |
69 |
UINT8 SleepState) |
70 |
{ |
71 |
ACPI_BIT_REGISTER_INFO *SleepTypeRegInfo; |
72 |
ACPI_BIT_REGISTER_INFO *SleepEnableRegInfo; |
73 |
UINT32 Pm1aControl; |
74 |
UINT32 Pm1bControl; |
75 |
UINT32 InValue; |
76 |
ACPI_STATUS Status; |
77 |
|
78 |
|
79 |
ACPI_FUNCTION_TRACE (HwLegacySleep); |
80 |
|
81 |
|
82 |
SleepTypeRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE); |
83 |
SleepEnableRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE); |
84 |
|
85 |
/* Clear wake status */ |
86 |
|
87 |
Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, |
88 |
ACPI_CLEAR_STATUS); |
89 |
if (ACPI_FAILURE (Status)) |
90 |
{ |
91 |
return_ACPI_STATUS (Status); |
92 |
} |
93 |
|
94 |
/* Clear all fixed and general purpose status bits */ |
95 |
|
96 |
Status = AcpiHwClearAcpiStatus (); |
97 |
if (ACPI_FAILURE (Status)) |
98 |
{ |
99 |
return_ACPI_STATUS (Status); |
100 |
} |
101 |
|
102 |
/* |
103 |
* 1) Disable/Clear all GPEs |
104 |
* 2) Enable all wakeup GPEs |
105 |
*/ |
106 |
Status = AcpiHwDisableAllGpes (); |
107 |
if (ACPI_FAILURE (Status)) |
108 |
{ |
109 |
return_ACPI_STATUS (Status); |
110 |
} |
111 |
AcpiGbl_SystemAwakeAndRunning = FALSE; |
112 |
|
113 |
Status = AcpiHwEnableAllWakeupGpes (); |
114 |
if (ACPI_FAILURE (Status)) |
115 |
{ |
116 |
return_ACPI_STATUS (Status); |
117 |
} |
118 |
|
119 |
/* Get current value of PM1A control */ |
120 |
|
121 |
Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL, |
122 |
&Pm1aControl); |
123 |
if (ACPI_FAILURE (Status)) |
124 |
{ |
125 |
return_ACPI_STATUS (Status); |
126 |
} |
127 |
ACPI_DEBUG_PRINT ((ACPI_DB_INIT, |
128 |
"Entering sleep state [S%u]\n", SleepState)); |
129 |
|
130 |
/* Clear the SLP_EN and SLP_TYP fields */ |
131 |
|
132 |
Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask | |
133 |
SleepEnableRegInfo->AccessBitMask); |
134 |
Pm1bControl = Pm1aControl; |
135 |
|
136 |
/* Insert the SLP_TYP bits */ |
137 |
|
138 |
Pm1aControl |= (AcpiGbl_SleepTypeA << SleepTypeRegInfo->BitPosition); |
139 |
Pm1bControl |= (AcpiGbl_SleepTypeB << SleepTypeRegInfo->BitPosition); |
140 |
|
141 |
/* |
142 |
* We split the writes of SLP_TYP and SLP_EN to workaround |
143 |
* poorly implemented hardware. |
144 |
*/ |
145 |
|
146 |
/* Write #1: write the SLP_TYP data to the PM1 Control registers */ |
147 |
|
148 |
Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl); |
149 |
if (ACPI_FAILURE (Status)) |
150 |
{ |
151 |
return_ACPI_STATUS (Status); |
152 |
} |
153 |
|
154 |
/* Insert the sleep enable (SLP_EN) bit */ |
155 |
|
156 |
Pm1aControl |= SleepEnableRegInfo->AccessBitMask; |
157 |
Pm1bControl |= SleepEnableRegInfo->AccessBitMask; |
158 |
|
159 |
/* Flush caches, as per ACPI specification */ |
160 |
|
161 |
ACPI_FLUSH_CPU_CACHE (); |
162 |
|
163 |
Status = AcpiOsEnterSleep (SleepState, Pm1aControl, Pm1bControl); |
164 |
if (Status == AE_CTRL_TERMINATE) |
165 |
{ |
166 |
return_ACPI_STATUS (AE_OK); |
167 |
} |
168 |
if (ACPI_FAILURE (Status)) |
169 |
{ |
170 |
return_ACPI_STATUS (Status); |
171 |
} |
172 |
|
173 |
/* Write #2: Write both SLP_TYP + SLP_EN */ |
174 |
|
175 |
Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl); |
176 |
if (ACPI_FAILURE (Status)) |
177 |
{ |
178 |
return_ACPI_STATUS (Status); |
179 |
} |
180 |
|
181 |
if (SleepState > ACPI_STATE_S3) |
182 |
{ |
183 |
/* |
184 |
* We wanted to sleep > S3, but it didn't happen (by virtue of the |
185 |
* fact that we are still executing!) |
186 |
* |
187 |
* Wait ten seconds, then try again. This is to get S4/S5 to work on |
188 |
* all machines. |
189 |
* |
190 |
* We wait so long to allow chipsets that poll this reg very slowly |
191 |
* to still read the right value. Ideally, this block would go |
192 |
* away entirely. |
193 |
*/ |
194 |
AcpiOsStall (10 * ACPI_USEC_PER_SEC); |
195 |
|
196 |
Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_CONTROL, |
197 |
SleepEnableRegInfo->AccessBitMask); |
198 |
if (ACPI_FAILURE (Status)) |
199 |
{ |
200 |
return_ACPI_STATUS (Status); |
201 |
} |
202 |
} |
203 |
|
204 |
/* Wait for transition back to Working State */ |
205 |
|
206 |
do |
207 |
{ |
208 |
Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue); |
209 |
if (ACPI_FAILURE (Status)) |
210 |
{ |
211 |
return_ACPI_STATUS (Status); |
212 |
} |
213 |
|
214 |
} while (!InValue); |
215 |
|
216 |
return_ACPI_STATUS (AE_OK); |
217 |
} |
218 |
|
219 |
|
220 |
/******************************************************************************* |
221 |
* |
222 |
* FUNCTION: AcpiHwLegacyWakePrep |
223 |
* |
224 |
* PARAMETERS: SleepState - Which sleep state we just exited |
225 |
* |
226 |
* RETURN: Status |
227 |
* |
228 |
* DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a |
229 |
* sleep. |
230 |
* Called with interrupts ENABLED. |
231 |
* |
232 |
******************************************************************************/ |
233 |
|
234 |
ACPI_STATUS |
235 |
AcpiHwLegacyWakePrep ( |
236 |
UINT8 SleepState) |
237 |
{ |
238 |
ACPI_STATUS Status; |
239 |
ACPI_BIT_REGISTER_INFO *SleepTypeRegInfo; |
240 |
ACPI_BIT_REGISTER_INFO *SleepEnableRegInfo; |
241 |
UINT32 Pm1aControl; |
242 |
UINT32 Pm1bControl; |
243 |
|
244 |
|
245 |
ACPI_FUNCTION_TRACE (HwLegacyWakePrep); |
246 |
|
247 |
/* |
248 |
* Set SLP_TYPE and SLP_EN to state S0. |
249 |
* This is unclear from the ACPI Spec, but it is required |
250 |
* by some machines. |
251 |
*/ |
252 |
Status = AcpiGetSleepTypeData (ACPI_STATE_S0, |
253 |
&AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB); |
254 |
if (ACPI_SUCCESS (Status)) |
255 |
{ |
256 |
SleepTypeRegInfo = |
257 |
AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE); |
258 |
SleepEnableRegInfo = |
259 |
AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE); |
260 |
|
261 |
/* Get current value of PM1A control */ |
262 |
|
263 |
Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL, |
264 |
&Pm1aControl); |
265 |
if (ACPI_SUCCESS (Status)) |
266 |
{ |
267 |
/* Clear the SLP_EN and SLP_TYP fields */ |
268 |
|
269 |
Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask | |
270 |
SleepEnableRegInfo->AccessBitMask); |
271 |
Pm1bControl = Pm1aControl; |
272 |
|
273 |
/* Insert the SLP_TYP bits */ |
274 |
|
275 |
Pm1aControl |= (AcpiGbl_SleepTypeA << |
276 |
SleepTypeRegInfo->BitPosition); |
277 |
Pm1bControl |= (AcpiGbl_SleepTypeB << |
278 |
SleepTypeRegInfo->BitPosition); |
279 |
|
280 |
/* Write the control registers and ignore any errors */ |
281 |
|
282 |
(void) AcpiHwWritePm1Control (Pm1aControl, Pm1bControl); |
283 |
} |
284 |
} |
285 |
|
286 |
return_ACPI_STATUS (Status); |
287 |
} |
288 |
|
289 |
|
290 |
/******************************************************************************* |
291 |
* |
292 |
* FUNCTION: AcpiHwLegacyWake |
293 |
* |
294 |
* PARAMETERS: SleepState - Which sleep state we just exited |
295 |
* |
296 |
* RETURN: Status |
297 |
* |
298 |
* DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep |
299 |
* Called with interrupts ENABLED. |
300 |
* |
301 |
******************************************************************************/ |
302 |
|
303 |
ACPI_STATUS |
304 |
AcpiHwLegacyWake ( |
305 |
UINT8 SleepState) |
306 |
{ |
307 |
ACPI_STATUS Status; |
308 |
|
309 |
|
310 |
ACPI_FUNCTION_TRACE (HwLegacyWake); |
311 |
|
312 |
|
313 |
/* Ensure EnterSleepStatePrep -> EnterSleepState ordering */ |
314 |
|
315 |
AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID; |
316 |
AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WAKING); |
317 |
|
318 |
/* |
319 |
* GPEs must be enabled before _WAK is called as GPEs |
320 |
* might get fired there |
321 |
* |
322 |
* Restore the GPEs: |
323 |
* 1) Disable/Clear all GPEs |
324 |
* 2) Enable all runtime GPEs |
325 |
*/ |
326 |
Status = AcpiHwDisableAllGpes (); |
327 |
if (ACPI_FAILURE (Status)) |
328 |
{ |
329 |
return_ACPI_STATUS (Status); |
330 |
} |
331 |
|
332 |
Status = AcpiHwEnableAllRuntimeGpes (); |
333 |
if (ACPI_FAILURE (Status)) |
334 |
{ |
335 |
return_ACPI_STATUS (Status); |
336 |
} |
337 |
|
338 |
/* |
339 |
* Now we can execute _WAK, etc. Some machines require that the GPEs |
340 |
* are enabled before the wake methods are executed. |
341 |
*/ |
342 |
AcpiHwExecuteSleepMethod (METHOD_PATHNAME__WAK, SleepState); |
343 |
|
344 |
/* |
345 |
* Some BIOS code assumes that WAK_STS will be cleared on resume |
346 |
* and use it to determine whether the system is rebooting or |
347 |
* resuming. Clear WAK_STS for compatibility. |
348 |
*/ |
349 |
(void) AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, |
350 |
ACPI_CLEAR_STATUS); |
351 |
AcpiGbl_SystemAwakeAndRunning = TRUE; |
352 |
|
353 |
/* Enable power button */ |
354 |
|
355 |
(void) AcpiWriteBitRegister( |
356 |
AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].EnableRegisterId, |
357 |
ACPI_ENABLE_EVENT); |
358 |
|
359 |
(void) AcpiWriteBitRegister( |
360 |
AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].StatusRegisterId, |
361 |
ACPI_CLEAR_STATUS); |
362 |
|
363 |
AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WORKING); |
364 |
return_ACPI_STATUS (Status); |
365 |
} |
366 |
|
367 |
#endif /* !ACPI_REDUCED_HARDWARE */ |