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