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