xref: /titanic_44/usr/src/uts/intel/io/acpica/hardware/hwsleep.c (revision 12ae924a5d4f6af89ee47f2908555a487618d82b)
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 - 2016, 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     /* Clear all fixed and general purpose status bits */
94 
95     Status = AcpiHwClearAcpiStatus ();
96     if (ACPI_FAILURE (Status))
97     {
98         return_ACPI_STATUS (Status);
99     }
100 
101     /*
102      * 1) Disable/Clear all GPEs
103      * 2) Enable all wakeup GPEs
104      */
105     Status = AcpiHwDisableAllGpes ();
106     if (ACPI_FAILURE (Status))
107     {
108         return_ACPI_STATUS (Status);
109     }
110     AcpiGbl_SystemAwakeAndRunning = FALSE;
111 
112     Status = AcpiHwEnableAllWakeupGpes ();
113     if (ACPI_FAILURE (Status))
114     {
115         return_ACPI_STATUS (Status);
116     }
117 
118     /* Get current value of PM1A control */
119 
120     Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
121         &Pm1aControl);
122     if (ACPI_FAILURE (Status))
123     {
124         return_ACPI_STATUS (Status);
125     }
126     ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
127         "Entering sleep state [S%u]\n", SleepState));
128 
129     /* Clear the SLP_EN and SLP_TYP fields */
130 
131     Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask |
132          SleepEnableRegInfo->AccessBitMask);
133     Pm1bControl = Pm1aControl;
134 
135     /* Insert the SLP_TYP bits */
136 
137     Pm1aControl |= (AcpiGbl_SleepTypeA << SleepTypeRegInfo->BitPosition);
138     Pm1bControl |= (AcpiGbl_SleepTypeB << SleepTypeRegInfo->BitPosition);
139 
140     /*
141      * We split the writes of SLP_TYP and SLP_EN to workaround
142      * poorly implemented hardware.
143      */
144 
145     /* Write #1: write the SLP_TYP data to the PM1 Control registers */
146 
147     Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
148     if (ACPI_FAILURE (Status))
149     {
150         return_ACPI_STATUS (Status);
151     }
152 
153     /* Insert the sleep enable (SLP_EN) bit */
154 
155     Pm1aControl |= SleepEnableRegInfo->AccessBitMask;
156     Pm1bControl |= SleepEnableRegInfo->AccessBitMask;
157 
158     /* Flush caches, as per ACPI specification */
159 
160     ACPI_FLUSH_CPU_CACHE ();
161 
162     /* Write #2: Write both SLP_TYP + SLP_EN */
163 
164     Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
165     if (ACPI_FAILURE (Status))
166     {
167         return_ACPI_STATUS (Status);
168     }
169 
170     if (SleepState > ACPI_STATE_S3)
171     {
172         /*
173          * We wanted to sleep > S3, but it didn't happen (by virtue of the
174          * fact that we are still executing!)
175          *
176          * Wait ten seconds, then try again. This is to get S4/S5 to work on
177          * all machines.
178          *
179          * We wait so long to allow chipsets that poll this reg very slowly
180          * to still read the right value. Ideally, this block would go
181          * away entirely.
182          */
183         AcpiOsStall (10 * ACPI_USEC_PER_SEC);
184 
185         Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_CONTROL,
186             SleepEnableRegInfo->AccessBitMask);
187         if (ACPI_FAILURE (Status))
188         {
189             return_ACPI_STATUS (Status);
190         }
191     }
192 
193     /* Wait for transition back to Working State */
194 
195     do
196     {
197         Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue);
198         if (ACPI_FAILURE (Status))
199         {
200             return_ACPI_STATUS (Status);
201         }
202 
203     } while (!InValue);
204 
205     return_ACPI_STATUS (AE_OK);
206 }
207 
208 
209 /*******************************************************************************
210  *
211  * FUNCTION:    AcpiHwLegacyWakePrep
212  *
213  * PARAMETERS:  SleepState          - Which sleep state we just exited
214  *
215  * RETURN:      Status
216  *
217  * DESCRIPTION: Perform the first state of OS-independent ACPI cleanup after a
218  *              sleep.
219  *              Called with interrupts ENABLED.
220  *
221  ******************************************************************************/
222 
223 ACPI_STATUS
224 AcpiHwLegacyWakePrep (
225     UINT8                   SleepState)
226 {
227     ACPI_STATUS             Status;
228     ACPI_BIT_REGISTER_INFO  *SleepTypeRegInfo;
229     ACPI_BIT_REGISTER_INFO  *SleepEnableRegInfo;
230     UINT32                  Pm1aControl;
231     UINT32                  Pm1bControl;
232 
233 
234     ACPI_FUNCTION_TRACE (HwLegacyWakePrep);
235 
236     /*
237      * Set SLP_TYPE and SLP_EN to state S0.
238      * This is unclear from the ACPI Spec, but it is required
239      * by some machines.
240      */
241     Status = AcpiGetSleepTypeData (ACPI_STATE_S0,
242         &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
243     if (ACPI_SUCCESS (Status))
244     {
245         SleepTypeRegInfo =
246             AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE);
247         SleepEnableRegInfo =
248             AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE);
249 
250         /* Get current value of PM1A control */
251 
252         Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
253             &Pm1aControl);
254         if (ACPI_SUCCESS (Status))
255         {
256             /* Clear the SLP_EN and SLP_TYP fields */
257 
258             Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask |
259                 SleepEnableRegInfo->AccessBitMask);
260             Pm1bControl = Pm1aControl;
261 
262             /* Insert the SLP_TYP bits */
263 
264             Pm1aControl |= (AcpiGbl_SleepTypeA <<
265                 SleepTypeRegInfo->BitPosition);
266             Pm1bControl |= (AcpiGbl_SleepTypeB <<
267                 SleepTypeRegInfo->BitPosition);
268 
269             /* Write the control registers and ignore any errors */
270 
271             (void) AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
272         }
273     }
274 
275     return_ACPI_STATUS (Status);
276 }
277 
278 
279 /*******************************************************************************
280  *
281  * FUNCTION:    AcpiHwLegacyWake
282  *
283  * PARAMETERS:  SleepState          - Which sleep state we just exited
284  *
285  * RETURN:      Status
286  *
287  * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
288  *              Called with interrupts ENABLED.
289  *
290  ******************************************************************************/
291 
292 ACPI_STATUS
293 AcpiHwLegacyWake (
294     UINT8                   SleepState)
295 {
296     ACPI_STATUS             Status;
297 
298 
299     ACPI_FUNCTION_TRACE (HwLegacyWake);
300 
301 
302     /* Ensure EnterSleepStatePrep -> EnterSleepState ordering */
303 
304     AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID;
305     AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WAKING);
306 
307     /*
308      * GPEs must be enabled before _WAK is called as GPEs
309      * might get fired there
310      *
311      * Restore the GPEs:
312      * 1) Disable/Clear all GPEs
313      * 2) Enable all runtime GPEs
314      */
315     Status = AcpiHwDisableAllGpes ();
316     if (ACPI_FAILURE (Status))
317     {
318         return_ACPI_STATUS (Status);
319     }
320 
321     Status = AcpiHwEnableAllRuntimeGpes ();
322     if (ACPI_FAILURE (Status))
323     {
324         return_ACPI_STATUS (Status);
325     }
326 
327     /*
328      * Now we can execute _WAK, etc. Some machines require that the GPEs
329      * are enabled before the wake methods are executed.
330      */
331     AcpiHwExecuteSleepMethod (METHOD_PATHNAME__WAK, SleepState);
332 
333     /*
334      * Some BIOS code assumes that WAK_STS will be cleared on resume
335      * and use it to determine whether the system is rebooting or
336      * resuming. Clear WAK_STS for compatibility.
337      */
338     (void) AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS,
339         ACPI_CLEAR_STATUS);
340     AcpiGbl_SystemAwakeAndRunning = TRUE;
341 
342     /* Enable power button */
343 
344     (void) AcpiWriteBitRegister(
345             AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].EnableRegisterId,
346             ACPI_ENABLE_EVENT);
347 
348     (void) AcpiWriteBitRegister(
349             AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].StatusRegisterId,
350             ACPI_CLEAR_STATUS);
351 
352     AcpiHwExecuteSleepMethod (METHOD_PATHNAME__SST, ACPI_SST_WORKING);
353     return_ACPI_STATUS (Status);
354 }
355 
356 #endif /* !ACPI_REDUCED_HARDWARE */
357