xref: /titanic_44/usr/src/uts/intel/io/acpica/hardware/hwsleep.c (revision 5fd03bc0f2e00e7ba02316c2e08f45d52aab15db)
1 
2 /******************************************************************************
3  *
4  * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface
5  *
6  *****************************************************************************/
7 
8 /*
9  * Copyright (C) 2000 - 2011, 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 /*******************************************************************************
53  *
54  * FUNCTION:    AcpiSetFirmwareWakingVector
55  *
56  * PARAMETERS:  PhysicalAddress     - 32-bit physical address of ACPI real mode
57  *                                    entry point.
58  *
59  * RETURN:      Status
60  *
61  * DESCRIPTION: Sets the 32-bit FirmwareWakingVector field of the FACS
62  *
63  ******************************************************************************/
64 
65 ACPI_STATUS
66 AcpiSetFirmwareWakingVector (
67     UINT32                  PhysicalAddress)
68 {
69     ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector);
70 
71 
72     /* Set the 32-bit vector */
73 
74     AcpiGbl_FACS->FirmwareWakingVector = PhysicalAddress;
75 
76     /* Clear the 64-bit vector if it exists */
77 
78     if ((AcpiGbl_FACS->Length > 32) && (AcpiGbl_FACS->Version >= 1))
79     {
80         AcpiGbl_FACS->XFirmwareWakingVector = 0;
81     }
82 
83     return_ACPI_STATUS (AE_OK);
84 }
85 
86 ACPI_EXPORT_SYMBOL (AcpiSetFirmwareWakingVector)
87 
88 
89 #if ACPI_MACHINE_WIDTH == 64
90 /*******************************************************************************
91  *
92  * FUNCTION:    AcpiSetFirmwareWakingVector64
93  *
94  * PARAMETERS:  PhysicalAddress     - 64-bit physical address of ACPI protected
95  *                                    mode entry point.
96  *
97  * RETURN:      Status
98  *
99  * DESCRIPTION: Sets the 64-bit X_FirmwareWakingVector field of the FACS, if
100  *              it exists in the table. This function is intended for use with
101  *              64-bit host operating systems.
102  *
103  ******************************************************************************/
104 
105 ACPI_STATUS
106 AcpiSetFirmwareWakingVector64 (
107     UINT64                  PhysicalAddress)
108 {
109     ACPI_FUNCTION_TRACE (AcpiSetFirmwareWakingVector64);
110 
111 
112     /* Determine if the 64-bit vector actually exists */
113 
114     if ((AcpiGbl_FACS->Length <= 32) || (AcpiGbl_FACS->Version < 1))
115     {
116         return_ACPI_STATUS (AE_NOT_EXIST);
117     }
118 
119     /* Clear 32-bit vector, set the 64-bit X_ vector */
120 
121     AcpiGbl_FACS->FirmwareWakingVector = 0;
122     AcpiGbl_FACS->XFirmwareWakingVector = PhysicalAddress;
123     return_ACPI_STATUS (AE_OK);
124 }
125 
126 ACPI_EXPORT_SYMBOL (AcpiSetFirmwareWakingVector64)
127 #endif
128 
129 /*******************************************************************************
130  *
131  * FUNCTION:    AcpiEnterSleepStatePrep
132  *
133  * PARAMETERS:  SleepState          - Which sleep state to enter
134  *
135  * RETURN:      Status
136  *
137  * DESCRIPTION: Prepare to enter a system sleep state (see ACPI 2.0 spec p 231)
138  *              This function must execute with interrupts enabled.
139  *              We break sleeping into 2 stages so that OSPM can handle
140  *              various OS-specific tasks between the two steps.
141  *
142  ******************************************************************************/
143 
144 ACPI_STATUS
145 AcpiEnterSleepStatePrep (
146     UINT8                   SleepState)
147 {
148     ACPI_STATUS             Status;
149     ACPI_OBJECT_LIST        ArgList;
150     ACPI_OBJECT             Arg;
151 
152 
153     ACPI_FUNCTION_TRACE (AcpiEnterSleepStatePrep);
154 
155 
156     /* _PSW methods could be run here to enable wake-on keyboard, LAN, etc. */
157 
158     Status = AcpiGetSleepTypeData (SleepState,
159                     &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
160     if (ACPI_FAILURE (Status))
161     {
162         return_ACPI_STATUS (Status);
163     }
164 
165     /* Execute the _PTS method (Prepare To Sleep) */
166 
167     ArgList.Count = 1;
168     ArgList.Pointer = &Arg;
169     Arg.Type = ACPI_TYPE_INTEGER;
170     Arg.Integer.Value = SleepState;
171 
172     Status = AcpiEvaluateObject (NULL, METHOD_NAME__PTS, &ArgList, NULL);
173     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
174     {
175         return_ACPI_STATUS (Status);
176     }
177 
178     /* Setup the argument to the _SST method (System STatus) */
179 
180     switch (SleepState)
181     {
182     case ACPI_STATE_S0:
183         Arg.Integer.Value = ACPI_SST_WORKING;
184         break;
185 
186     case ACPI_STATE_S1:
187     case ACPI_STATE_S2:
188     case ACPI_STATE_S3:
189         Arg.Integer.Value = ACPI_SST_SLEEPING;
190         break;
191 
192     case ACPI_STATE_S4:
193         Arg.Integer.Value = ACPI_SST_SLEEP_CONTEXT;
194         break;
195 
196     default:
197         Arg.Integer.Value = ACPI_SST_INDICATOR_OFF; /* Default is off */
198         break;
199     }
200 
201     /*
202      * Set the system indicators to show the desired sleep state.
203      * _SST is an optional method (return no error if not found)
204      */
205     Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
206     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
207     {
208         ACPI_EXCEPTION ((AE_INFO, Status, "While executing method _SST"));
209     }
210 
211     return_ACPI_STATUS (AE_OK);
212 }
213 
214 ACPI_EXPORT_SYMBOL (AcpiEnterSleepStatePrep)
215 
216 
217 /*******************************************************************************
218  *
219  * FUNCTION:    AcpiEnterSleepState
220  *
221  * PARAMETERS:  SleepState          - Which sleep state to enter
222  *
223  * RETURN:      Status
224  *
225  * DESCRIPTION: Enter a system sleep state
226  *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
227  *
228  ******************************************************************************/
229 
230 ACPI_STATUS
231 AcpiEnterSleepState (
232     UINT8                   SleepState)
233 {
234     UINT32                  Pm1aControl;
235     UINT32                  Pm1bControl;
236     ACPI_BIT_REGISTER_INFO  *SleepTypeRegInfo;
237     ACPI_BIT_REGISTER_INFO  *SleepEnableRegInfo;
238     UINT32                  InValue;
239     ACPI_OBJECT_LIST        ArgList;
240     ACPI_OBJECT             Arg;
241     ACPI_STATUS             Status;
242 
243 
244     ACPI_FUNCTION_TRACE (AcpiEnterSleepState);
245 
246 
247     if ((AcpiGbl_SleepTypeA > ACPI_SLEEP_TYPE_MAX) ||
248         (AcpiGbl_SleepTypeB > ACPI_SLEEP_TYPE_MAX))
249     {
250         ACPI_ERROR ((AE_INFO, "Sleep values out of range: A=0x%X B=0x%X",
251             AcpiGbl_SleepTypeA, AcpiGbl_SleepTypeB));
252         return_ACPI_STATUS (AE_AML_OPERAND_VALUE);
253     }
254 
255     SleepTypeRegInfo   = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE);
256     SleepEnableRegInfo = AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE);
257 
258     /* Clear wake status */
259 
260     Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
261     if (ACPI_FAILURE (Status))
262     {
263         return_ACPI_STATUS (Status);
264     }
265 
266     /* Clear all fixed and general purpose status bits */
267 
268     Status = AcpiHwClearAcpiStatus ();
269     if (ACPI_FAILURE (Status))
270     {
271         return_ACPI_STATUS (Status);
272     }
273 
274     if (SleepState != ACPI_STATE_S5)
275     {
276         /*
277          * Disable BM arbitration. This feature is contained within an
278          * optional register (PM2 Control), so ignore a BAD_ADDRESS
279          * exception.
280          */
281         Status = AcpiWriteBitRegister (ACPI_BITREG_ARB_DISABLE, 1);
282         if (ACPI_FAILURE (Status) && (Status != AE_BAD_ADDRESS))
283         {
284             return_ACPI_STATUS (Status);
285         }
286     }
287 
288     /*
289      * 1) Disable/Clear all GPEs
290      * 2) Enable all wakeup GPEs
291      */
292     Status = AcpiHwDisableAllGpes ();
293     if (ACPI_FAILURE (Status))
294     {
295         return_ACPI_STATUS (Status);
296     }
297     AcpiGbl_SystemAwakeAndRunning = FALSE;
298 
299     Status = AcpiHwEnableAllWakeupGpes ();
300     if (ACPI_FAILURE (Status))
301     {
302         return_ACPI_STATUS (Status);
303     }
304 
305     /* Execute the _GTS method (Going To Sleep) */
306 
307     ArgList.Count = 1;
308     ArgList.Pointer = &Arg;
309     Arg.Type = ACPI_TYPE_INTEGER;
310     Arg.Integer.Value = SleepState;
311 
312     Status = AcpiEvaluateObject (NULL, METHOD_NAME__GTS, &ArgList, NULL);
313     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
314     {
315         return_ACPI_STATUS (Status);
316     }
317 
318     /* Get current value of PM1A control */
319 
320     Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
321                 &Pm1aControl);
322     if (ACPI_FAILURE (Status))
323     {
324         return_ACPI_STATUS (Status);
325     }
326     ACPI_DEBUG_PRINT ((ACPI_DB_INIT,
327         "Entering sleep state [S%u]\n", SleepState));
328 
329     /* Clear the SLP_EN and SLP_TYP fields */
330 
331     Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask |
332                      SleepEnableRegInfo->AccessBitMask);
333     Pm1bControl = Pm1aControl;
334 
335     /* Insert the SLP_TYP bits */
336 
337     Pm1aControl |= (AcpiGbl_SleepTypeA << SleepTypeRegInfo->BitPosition);
338     Pm1bControl |= (AcpiGbl_SleepTypeB << SleepTypeRegInfo->BitPosition);
339 
340     /*
341      * We split the writes of SLP_TYP and SLP_EN to workaround
342      * poorly implemented hardware.
343      */
344 
345     /* Write #1: write the SLP_TYP data to the PM1 Control registers */
346 
347     Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
348     if (ACPI_FAILURE (Status))
349     {
350         return_ACPI_STATUS (Status);
351     }
352 
353     /* Insert the sleep enable (SLP_EN) bit */
354 
355     Pm1aControl |= SleepEnableRegInfo->AccessBitMask;
356     Pm1bControl |= SleepEnableRegInfo->AccessBitMask;
357 
358     /* Flush caches, as per ACPI specification */
359 
360     ACPI_FLUSH_CPU_CACHE ();
361 
362     /* Write #2: Write both SLP_TYP + SLP_EN */
363 
364     Status = AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
365     if (ACPI_FAILURE (Status))
366     {
367         return_ACPI_STATUS (Status);
368     }
369 
370     if (SleepState > ACPI_STATE_S3)
371     {
372         /*
373          * We wanted to sleep > S3, but it didn't happen (by virtue of the
374          * fact that we are still executing!)
375          *
376          * Wait ten seconds, then try again. This is to get S4/S5 to work on
377          * all machines.
378          *
379          * We wait so long to allow chipsets that poll this reg very slowly
380          * to still read the right value. Ideally, this block would go
381          * away entirely.
382          */
383         AcpiOsStall (10000000);
384 
385         Status = AcpiHwRegisterWrite (ACPI_REGISTER_PM1_CONTROL,
386                     SleepEnableRegInfo->AccessBitMask);
387         if (ACPI_FAILURE (Status))
388         {
389             return_ACPI_STATUS (Status);
390         }
391     }
392 
393     /* Wait until we enter sleep state */
394 
395     do
396     {
397         Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue);
398         if (ACPI_FAILURE (Status))
399         {
400             return_ACPI_STATUS (Status);
401         }
402 
403         /* Spin until we wake */
404 
405     } while (!InValue);
406 
407     return_ACPI_STATUS (AE_OK);
408 }
409 
410 ACPI_EXPORT_SYMBOL (AcpiEnterSleepState)
411 
412 
413 /*******************************************************************************
414  *
415  * FUNCTION:    AcpiEnterSleepStateS4bios
416  *
417  * PARAMETERS:  None
418  *
419  * RETURN:      Status
420  *
421  * DESCRIPTION: Perform a S4 bios request.
422  *              THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED
423  *
424  ******************************************************************************/
425 
426 ACPI_STATUS
427 AcpiEnterSleepStateS4bios (
428     void)
429 {
430     UINT32                  InValue;
431     ACPI_STATUS             Status;
432 
433 
434     ACPI_FUNCTION_TRACE (AcpiEnterSleepStateS4bios);
435 
436 
437     /* Clear the wake status bit (PM1) */
438 
439     Status = AcpiWriteBitRegister (ACPI_BITREG_WAKE_STATUS, ACPI_CLEAR_STATUS);
440     if (ACPI_FAILURE (Status))
441     {
442         return_ACPI_STATUS (Status);
443     }
444 
445     Status = AcpiHwClearAcpiStatus ();
446     if (ACPI_FAILURE (Status))
447     {
448         return_ACPI_STATUS (Status);
449     }
450 
451     /*
452      * 1) Disable/Clear all GPEs
453      * 2) Enable all wakeup GPEs
454      */
455     Status = AcpiHwDisableAllGpes ();
456     if (ACPI_FAILURE (Status))
457     {
458         return_ACPI_STATUS (Status);
459     }
460     AcpiGbl_SystemAwakeAndRunning = FALSE;
461 
462     Status = AcpiHwEnableAllWakeupGpes ();
463     if (ACPI_FAILURE (Status))
464     {
465         return_ACPI_STATUS (Status);
466     }
467 
468     ACPI_FLUSH_CPU_CACHE ();
469 
470     Status = AcpiHwWritePort (AcpiGbl_FADT.SmiCommand,
471                 (UINT32) AcpiGbl_FADT.S4BiosRequest, 8);
472 
473     do {
474         AcpiOsStall(1000);
475         Status = AcpiReadBitRegister (ACPI_BITREG_WAKE_STATUS, &InValue);
476         if (ACPI_FAILURE (Status))
477         {
478             return_ACPI_STATUS (Status);
479         }
480     } while (!InValue);
481 
482     return_ACPI_STATUS (AE_OK);
483 }
484 
485 ACPI_EXPORT_SYMBOL (AcpiEnterSleepStateS4bios)
486 
487 
488 /*******************************************************************************
489  *
490  * FUNCTION:    AcpiLeaveSleepState
491  *
492  * PARAMETERS:  SleepState          - Which sleep state we just exited
493  *
494  * RETURN:      Status
495  *
496  * DESCRIPTION: Perform OS-independent ACPI cleanup after a sleep
497  *              Called with interrupts ENABLED.
498  *
499  ******************************************************************************/
500 
501 ACPI_STATUS
502 AcpiLeaveSleepState (
503     UINT8                   SleepState)
504 {
505     ACPI_OBJECT_LIST        ArgList;
506     ACPI_OBJECT             Arg;
507     ACPI_STATUS             Status;
508     ACPI_BIT_REGISTER_INFO  *SleepTypeRegInfo;
509     ACPI_BIT_REGISTER_INFO  *SleepEnableRegInfo;
510     UINT32                  Pm1aControl;
511     UINT32                  Pm1bControl;
512 
513 
514     ACPI_FUNCTION_TRACE (AcpiLeaveSleepState);
515 
516 
517     /*
518      * Set SLP_TYPE and SLP_EN to state S0.
519      * This is unclear from the ACPI Spec, but it is required
520      * by some machines.
521      */
522     Status = AcpiGetSleepTypeData (ACPI_STATE_S0,
523                     &AcpiGbl_SleepTypeA, &AcpiGbl_SleepTypeB);
524     if (ACPI_SUCCESS (Status))
525     {
526         SleepTypeRegInfo =
527             AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_TYPE);
528         SleepEnableRegInfo =
529             AcpiHwGetBitRegisterInfo (ACPI_BITREG_SLEEP_ENABLE);
530 
531         /* Get current value of PM1A control */
532 
533         Status = AcpiHwRegisterRead (ACPI_REGISTER_PM1_CONTROL,
534                     &Pm1aControl);
535         if (ACPI_SUCCESS (Status))
536         {
537             /* Clear the SLP_EN and SLP_TYP fields */
538 
539             Pm1aControl &= ~(SleepTypeRegInfo->AccessBitMask |
540                 SleepEnableRegInfo->AccessBitMask);
541             Pm1bControl = Pm1aControl;
542 
543             /* Insert the SLP_TYP bits */
544 
545             Pm1aControl |= (AcpiGbl_SleepTypeA <<
546                 SleepTypeRegInfo->BitPosition);
547             Pm1bControl |= (AcpiGbl_SleepTypeB <<
548                 SleepTypeRegInfo->BitPosition);
549 
550             /* Write the control registers and ignore any errors */
551 
552             (void) AcpiHwWritePm1Control (Pm1aControl, Pm1bControl);
553         }
554     }
555 
556     /* Ensure EnterSleepStatePrep -> EnterSleepState ordering */
557 
558     AcpiGbl_SleepTypeA = ACPI_SLEEP_TYPE_INVALID;
559 
560     /* Setup parameter object */
561 
562     ArgList.Count = 1;
563     ArgList.Pointer = &Arg;
564     Arg.Type = ACPI_TYPE_INTEGER;
565 
566     /* Ignore any errors from these methods */
567 
568     Arg.Integer.Value = ACPI_SST_WAKING;
569     Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
570     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
571     {
572         ACPI_EXCEPTION ((AE_INFO, Status, "During Method _SST"));
573     }
574 
575     Arg.Integer.Value = SleepState;
576     Status = AcpiEvaluateObject (NULL, METHOD_NAME__BFS, &ArgList, NULL);
577     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
578     {
579         ACPI_EXCEPTION ((AE_INFO, Status, "During Method _BFS"));
580     }
581 
582     Status = AcpiEvaluateObject (NULL, METHOD_NAME__WAK, &ArgList, NULL);
583     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
584     {
585         ACPI_EXCEPTION ((AE_INFO, Status, "During Method _WAK"));
586     }
587     /* TBD: _WAK "sometimes" returns stuff - do we want to look at it? */
588 
589     /*
590      * Restore the GPEs:
591      * 1) Disable/Clear all GPEs
592      * 2) Enable all runtime GPEs
593      */
594     Status = AcpiHwDisableAllGpes ();
595     if (ACPI_FAILURE (Status))
596     {
597         return_ACPI_STATUS (Status);
598     }
599     AcpiGbl_SystemAwakeAndRunning = TRUE;
600 
601     Status = AcpiHwEnableAllRuntimeGpes ();
602     if (ACPI_FAILURE (Status))
603     {
604         return_ACPI_STATUS (Status);
605     }
606 
607     /* Enable power button */
608 
609     (void) AcpiWriteBitRegister(
610             AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].EnableRegisterId,
611             ACPI_ENABLE_EVENT);
612 
613     (void) AcpiWriteBitRegister(
614             AcpiGbl_FixedEventInfo[ACPI_EVENT_POWER_BUTTON].StatusRegisterId,
615             ACPI_CLEAR_STATUS);
616 
617     /*
618      * Enable BM arbitration. This feature is contained within an
619      * optional register (PM2 Control), so ignore a BAD_ADDRESS
620      * exception.
621      */
622     Status = AcpiWriteBitRegister (ACPI_BITREG_ARB_DISABLE, 0);
623     if (ACPI_FAILURE (Status) && (Status != AE_BAD_ADDRESS))
624     {
625         return_ACPI_STATUS (Status);
626     }
627 
628     Arg.Integer.Value = ACPI_SST_WORKING;
629     Status = AcpiEvaluateObject (NULL, METHOD_NAME__SST, &ArgList, NULL);
630     if (ACPI_FAILURE (Status) && Status != AE_NOT_FOUND)
631     {
632         ACPI_EXCEPTION ((AE_INFO, Status, "During Method _SST"));
633     }
634 
635     return_ACPI_STATUS (Status);
636 }
637 
638 ACPI_EXPORT_SYMBOL (AcpiLeaveSleepState)
639 
640