xref: /openbsd-src/sys/dev/acpi/acpi_x86.c (revision 20bdc91c785fb949f0d36715e4f9c8ac93a86082)
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