1 /* $OpenBSD: acpibtn.c,v 1.54 2024/09/21 19:06:06 deraadt Exp $ */
2 /*
3 * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/signalvar.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23
24 #include <machine/bus.h>
25 #include <machine/apmvar.h>
26
27 #include <dev/acpi/acpireg.h>
28 #include <dev/acpi/acpivar.h>
29 #include <dev/acpi/acpidev.h>
30 #include <dev/acpi/amltypes.h>
31 #include <dev/acpi/dsdt.h>
32
33 #include <sys/sensors.h>
34
35 int acpibtn_match(struct device *, void *, void *);
36 void acpibtn_attach(struct device *, struct device *, void *);
37 int acpibtn_notify(struct aml_node *, int, void *);
38 int acpibtn_activate(struct device *, int);
39
40 struct acpibtn_softc {
41 struct device sc_dev;
42
43 bus_space_tag_t sc_iot;
44 bus_space_handle_t sc_ioh;
45
46 struct acpi_softc *sc_acpi;
47 struct aml_node *sc_devnode;
48
49 struct ksensor sc_sens;
50 struct ksensordev sc_sensdev;
51
52 int sc_btn_type;
53 #define ACPIBTN_UNKNOWN 0
54 #define ACPIBTN_LID 1
55 #define ACPIBTN_POWER 2
56 #define ACPIBTN_SLEEP 3
57 };
58
59 int acpibtn_setpsw(struct acpibtn_softc *, int);
60
61 struct acpi_lid {
62 struct acpibtn_softc *abl_softc;
63 SLIST_ENTRY(acpi_lid) abl_link;
64 };
65 SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids =
66 SLIST_HEAD_INITIALIZER(acpibtn_lids);
67
68 const struct cfattach acpibtn_ca = {
69 sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach, NULL,
70 acpibtn_activate
71 };
72
73 struct cfdriver acpibtn_cd = {
74 NULL, "acpibtn", DV_DULL
75 };
76
77 const char *acpibtn_hids[] = {
78 ACPI_DEV_LD,
79 ACPI_DEV_PBD,
80 ACPI_DEV_SBD,
81 NULL
82 };
83
84 /*
85 * acpibtn_numopenlids
86 *
87 * Return the number of _LID devices that are in the "open" state.
88 * Used to determine if we should go back to sleep/hibernate if we
89 * woke up with the all the lids still closed for some reason. If
90 * the machine has no lids, returns -1.
91 */
92 int
acpibtn_numopenlids(void)93 acpibtn_numopenlids(void)
94 {
95 struct acpi_lid *lid;
96 int64_t val;
97 int ct = 0;
98
99 /* If we have no lids ... */
100 if (SLIST_EMPTY(&acpibtn_lids))
101 return (-1);
102
103 /*
104 * Determine how many lids are open. Assumes _LID evals to
105 * non-0 or 0, for on / off (which is what the spec says).
106 */
107 SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
108 if (!aml_evalinteger(lid->abl_softc->sc_acpi,
109 lid->abl_softc->sc_devnode, "_LID", 0, NULL, &val) &&
110 val != 0)
111 ct++;
112 return (ct);
113 }
114
115 int
acpibtn_setpsw(struct acpibtn_softc * sc,int psw)116 acpibtn_setpsw(struct acpibtn_softc *sc, int psw)
117 {
118 struct aml_value val;
119
120 bzero(&val, sizeof val);
121 val.type = AML_OBJTYPE_INTEGER;
122 val.v_integer = psw;
123 val.length = 1;
124
125 return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val,
126 NULL));
127 }
128
129 void
acpibtn_disable_psw(void)130 acpibtn_disable_psw(void)
131 {
132 struct acpi_lid *lid;
133
134 /* disable _LID for wakeup */
135 SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
136 acpibtn_setpsw(lid->abl_softc, 0);
137 }
138
139 void
acpibtn_enable_psw(void)140 acpibtn_enable_psw(void)
141 {
142 struct acpi_lid *lid;
143
144 /* enable _LID for wakeup */
145 SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
146 acpibtn_setpsw(lid->abl_softc, 1);
147 }
148
149 int
acpibtn_match(struct device * parent,void * match,void * aux)150 acpibtn_match(struct device *parent, void *match, void *aux)
151 {
152 struct acpi_attach_args *aa = aux;
153 struct cfdata *cf = match;
154
155 /* sanity */
156 return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name));
157 }
158
159 void
acpibtn_attach(struct device * parent,struct device * self,void * aux)160 acpibtn_attach(struct device *parent, struct device *self, void *aux)
161 {
162 struct acpibtn_softc *sc = (struct acpibtn_softc *)self;
163 struct acpi_attach_args *aa = aux;
164 struct acpi_lid *lid;
165 int64_t lid_open = 1;
166 int64_t st;
167
168 sc->sc_acpi = (struct acpi_softc *)parent;
169 sc->sc_devnode = aa->aaa_node;
170
171 printf(": %s", sc->sc_devnode->name);
172
173 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
174 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
175 if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
176 (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
177 return;
178
179 if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) {
180 sc->sc_btn_type = ACPIBTN_LID;
181
182 /* Set PSW (if present) to disable wake on this LID */
183 (void)acpibtn_setpsw(sc, 0);
184 lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO);
185 lid->abl_softc = sc;
186 SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link);
187 } else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD))
188 sc->sc_btn_type = ACPIBTN_POWER;
189 else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD))
190 sc->sc_btn_type = ACPIBTN_SLEEP;
191
192 if (sc->sc_btn_type == ACPIBTN_LID) {
193 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
194 sizeof(sc->sc_sensdev.xname));
195 strlcpy(sc->sc_sens.desc, "lid open",
196 sizeof(sc->sc_sens.desc));
197 sc->sc_sens.type = SENSOR_INDICATOR;
198 sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
199 sensordev_install(&sc->sc_sensdev);
200
201 aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
202 "_LID", 0, NULL, &lid_open);
203 sc->sc_sens.value = lid_open;
204 }
205
206 /* Enable any related GPEs for wake. */
207 if (acpi_toggle_wakedev(sc->sc_acpi, sc->sc_devnode, 1) == 0) {
208 #ifdef SUSPEND
209 device_register_wakeup(self);
210 printf("(wakeup)");
211 #endif /* SUSPEND */
212 }
213
214 printf("\n");
215 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
216 sc, ACPIDEV_NOPOLL | ACPIDEV_WAKEUP);
217 }
218
219 int
acpibtn_notify(struct aml_node * node,int notify_type,void * arg)220 acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
221 {
222 struct acpibtn_softc *sc = arg;
223 #ifndef SMALL_KERNEL
224 extern int lid_action;
225 extern int pwr_action;
226 int64_t lid;
227 #endif
228
229 dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
230 sc->sc_devnode->name);
231
232 /* Ignore button events if we're resuming. */
233 if (acpi_resuming(sc->sc_acpi))
234 return (0);
235
236 switch (sc->sc_btn_type) {
237 case ACPIBTN_LID:
238 #ifndef SMALL_KERNEL
239 /*
240 * Notification of 0x80 for lid opens or closes. We
241 * need to check the current status by calling the
242 * _LID method. 0 means the lid is closed and we
243 * should go to sleep.
244 */
245 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
246 "_LID", 0, NULL, &lid))
247 return (0);
248 if (sc->sc_sens.value == lid)
249 break;
250 sc->sc_sens.value = lid;
251
252 if (lid != 0)
253 break;
254
255 switch (lid_action) {
256 case 1:
257 case -1:
258 goto sleep;
259 #ifdef HIBERNATE
260 case 2:
261 /* Request to go to sleep */
262 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
263 acpi_addtask(sc->sc_acpi, acpi_sleep_task,
264 sc->sc_acpi, SLEEP_HIBERNATE);
265 break;
266 #endif
267 default:
268 break;
269 }
270 #endif /* SMALL_KERNEL */
271 break;
272 case ACPIBTN_SLEEP:
273 #ifndef SMALL_KERNEL
274 switch (notify_type) {
275 case 0x02:
276 /* "something" has been taken care of by the system */
277 break;
278 case 0x80:
279 sleep:
280 /* Request to go to sleep */
281 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
282 acpi_addtask(sc->sc_acpi, acpi_sleep_task,
283 sc->sc_acpi, SLEEP_SUSPEND);
284 break;
285 }
286 #endif /* SMALL_KERNEL */
287 break;
288 case ACPIBTN_POWER:
289 if (notify_type == 0x80) {
290 switch (pwr_action) {
291 case 0:
292 break;
293 case 1:
294 acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
295 sc->sc_acpi, 0);
296 break;
297 #ifndef SMALL_KERNEL
298 case 2:
299 goto sleep;
300 #endif
301 }
302 }
303 break;
304 default:
305 printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
306 sc->sc_btn_type);
307 break;
308 }
309
310 return (0);
311 }
312
313 int
acpibtn_activate(struct device * self,int act)314 acpibtn_activate(struct device *self, int act)
315 {
316 struct acpibtn_softc *sc = (struct acpibtn_softc *)self;
317 int64_t lid_open = 1;
318
319 switch (act) {
320 case DVACT_WAKEUP:
321 switch (sc->sc_btn_type) {
322 case ACPIBTN_LID:
323 aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
324 "_LID", 0, NULL, &lid_open);
325 sc->sc_sens.value = lid_open;
326 break;
327 }
328 break;
329 }
330 return (0);
331 }
332