1 /* $OpenBSD: acpi_x86.c,v 1.32 2024/09/21 19:06:06 deraadt Exp $ */ 2 /* 3 * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> 4 * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org> 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/device.h> 22 23 #include <dev/acpi/acpireg.h> 24 #include <dev/acpi/acpivar.h> 25 #include <dev/acpi/acpidev.h> 26 #include <dev/acpi/dsdt.h> 27 28 #include <machine/apmvar.h> 29 30 int 31 sleep_showstate(void *v, int sleepmode) 32 { 33 struct acpi_softc *sc = v; 34 int fallback_state = -1; 35 extern int lid_action; 36 37 switch (sleepmode) { 38 case SLEEP_SUSPEND: 39 sc->sc_state = ACPI_STATE_S3; 40 #ifdef __amd64__ 41 if (lid_action == -1) 42 sc->sc_state = ACPI_STATE_S0; 43 fallback_state = ACPI_STATE_S0; /* No S3, use S0 */ 44 #endif 45 break; 46 case SLEEP_HIBERNATE: 47 sc->sc_state = ACPI_STATE_S4; 48 fallback_state = ACPI_STATE_S5; /* No S4, use S5 */ 49 break; 50 default: 51 return (EOPNOTSUPP); 52 } 53 54 if (sc->sc_sleeptype[sc->sc_state].slp_typa == -1 || 55 sc->sc_sleeptype[sc->sc_state].slp_typb == -1) { 56 if (fallback_state != -1) { 57 printf("%s: S%d unavailable, using S%d\n", 58 sc->sc_dev.dv_xname, sc->sc_state, fallback_state); 59 sc->sc_state = fallback_state; 60 } else { 61 printf("%s: state S%d unavailable\n", 62 sc->sc_dev.dv_xname, sc->sc_state); 63 return (EOPNOTSUPP); 64 } 65 } 66 67 /* 1st suspend AML step: _TTS(tostate) */ 68 if (sc->sc_state != ACPI_STATE_S0) { 69 if (aml_node_setval(sc, sc->sc_tts, sc->sc_state) != 0) 70 return (EINVAL); 71 } 72 acpi_indicator(sc, ACPI_SST_WAKING); /* blink */ 73 return 0; 74 } 75 76 int 77 sleep_setstate(void *v) 78 { 79 struct acpi_softc *sc = v; 80 81 /* 2nd suspend AML step: _PTS(tostate) */ 82 if (sc->sc_state != ACPI_STATE_S0) { 83 if (aml_node_setval(sc, sc->sc_pts, sc->sc_state) != 0) 84 return (EINVAL); 85 } 86 return 0; 87 } 88 89 int 90 gosleep(void *v) 91 { 92 extern int cpu_wakeups; 93 struct acpi_softc *sc = v; 94 int ret; 95 96 acpibtn_enable_psw(); /* enable _LID for wakeup */ 97 acpi_indicator(sc, ACPI_SST_SLEEPING); 98 99 /* 3rd suspend AML step: _GTS(tostate) */ 100 if (sc->sc_state != ACPI_STATE_S0) 101 aml_node_setval(sc, sc->sc_gts, sc->sc_state); 102 103 /* Clear fixed event status */ 104 acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, ACPI_PM1_ALL_STS); 105 106 /* Enable wake GPEs */ 107 acpi_disable_allgpes(sc); 108 acpi_enable_wakegpes(sc, sc->sc_state); 109 110 if (sc->sc_pmc_suspend) 111 sc->sc_pmc_suspend(sc->sc_pmc_cookie); 112 113 cpu_wakeups = 0; 114 sc->sc_wakeup = 0; 115 sc->sc_wakeups = 0; 116 while (!sc->sc_wakeup) { 117 ret = acpi_sleep_cpu(sc, sc->sc_state); 118 acpi_resume_cpu(sc, sc->sc_state); 119 sc->sc_wakeups++; 120 121 if (sc->sc_ec && sc->sc_wakegpe == sc->sc_ec->sc_gpe) { 122 sc->sc_wakeup = 0; 123 acpiec_gpehandler(sc, sc->sc_wakegpe, sc->sc_ec); 124 } else 125 sc->sc_wakeup = 1; 126 } 127 128 if (sc->sc_pmc_resume) 129 sc->sc_pmc_resume(sc->sc_pmc_cookie); 130 131 acpi_indicator(sc, ACPI_SST_WAKING); /* blink */ 132 133 /* 1st resume AML step: _WAK(fromstate) */ 134 if (sc->sc_state != ACPI_STATE_S0) 135 aml_node_setval(sc, sc->sc_wak, sc->sc_state); 136 return ret; 137 } 138 139 int 140 sleep_resume(void *v) 141 { 142 struct acpi_softc *sc = v; 143 144 sc->sc_resume_time = getuptime(); 145 146 acpibtn_disable_psw(); /* disable _LID for wakeup */ 147 148 /* 3rd resume AML step: _TTS(runstate) */ 149 if (sc->sc_state != ACPI_STATE_S0) { 150 if (aml_node_setval(sc, sc->sc_tts, ACPI_STATE_S0) != 0) 151 return (EINVAL); 152 } 153 return 0; 154 } 155 156 157 static int 158 checklids(struct acpi_softc *sc) 159 { 160 extern int lid_action; 161 int lids; 162 163 lids = acpibtn_numopenlids(); 164 if (lids == 0 && lid_action != 0) 165 return EAGAIN; 166 return 0; 167 } 168 169 170 int 171 suspend_finish(void *v) 172 { 173 extern int cpu_wakeups; 174 struct acpi_softc *sc = v; 175 176 printf("wakeups: %d %d\n", cpu_wakeups, sc->sc_wakeups); 177 printf("wakeup event: "); 178 switch (sc->sc_wakegpe) { 179 case 0: 180 printf("unknown\n"); 181 break; 182 case -1: 183 printf("PWRBTN\n"); 184 break; 185 case -2: 186 printf("SLPTN\n"); 187 break; 188 default: 189 printf("GPE 0x%x\n", sc->sc_wakegpe); 190 break; 191 } 192 sc->sc_wakegpe = 0; 193 194 acpi_record_event(sc, APM_NORMAL_RESUME); 195 acpi_indicator(sc, ACPI_SST_WORKING); 196 197 sc->sc_state = ACPI_STATE_S0; 198 199 /* If we woke up but all the lids are closed, go back to sleep */ 200 return checklids(sc); 201 } 202