xref: /openbsd/sys/dev/acpi/acpibtn.c (revision 771fbea0)
1 /* $OpenBSD: acpibtn.c,v 1.47 2019/01/20 02:45:44 tedu 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 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
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
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
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
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
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
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\n", 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 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
207 	    sc, ACPIDEV_NOPOLL);
208 }
209 
210 int
211 acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
212 {
213 	struct acpibtn_softc	*sc = arg;
214 #ifndef SMALL_KERNEL
215 	extern int lid_action;
216 	extern int pwr_action;
217 	int64_t lid;
218 #endif
219 
220 	dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
221 	    sc->sc_devnode->name);
222 
223 	switch (sc->sc_btn_type) {
224 	case ACPIBTN_LID:
225 #ifndef SMALL_KERNEL
226 		/*
227 		 * Notification of 0x80 for lid opens or closes.  We
228 		 * need to check the current status by calling the
229 		 * _LID method.  0 means the lid is closed and we
230 		 * should go to sleep.
231 		 */
232 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
233 		    "_LID", 0, NULL, &lid))
234 			return (0);
235 		sc->sc_sens.value = lid;
236 
237 		if (lid != 0)
238 			break;
239 
240 		switch (lid_action) {
241 		case 1:
242 			goto sleep;
243 #ifdef HIBERNATE
244 		case 2:
245 			/* Request to go to sleep */
246 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
247 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
248 				    sc->sc_acpi, ACPI_SLEEP_HIBERNATE);
249 			break;
250 #endif
251 		default:
252 			break;
253 		}
254 #endif /* SMALL_KERNEL */
255 		break;
256 	case ACPIBTN_SLEEP:
257 #ifndef SMALL_KERNEL
258 		switch (notify_type) {
259 		case 0x02:
260 			/* "something" has been taken care of by the system */
261 			break;
262 		case 0x80:
263 sleep:
264 			/* Request to go to sleep */
265 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
266 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
267 				    sc->sc_acpi, ACPI_SLEEP_SUSPEND);
268 			break;
269 		}
270 #endif /* SMALL_KERNEL */
271 		break;
272 	case ACPIBTN_POWER:
273 		if (notify_type == 0x80) {
274 			switch (pwr_action) {
275 			case 0:
276 				break;
277 			case 1:
278 				acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
279 				    sc->sc_acpi, 0);
280 				break;
281 #ifndef SMALL_KERNEL
282 			case 2:
283 				goto sleep;
284 #endif
285 			}
286 		}
287 		break;
288 	default:
289 		printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
290 		    sc->sc_btn_type);
291 		break;
292 	}
293 
294 	return (0);
295 }
296 
297 int
298 acpibtn_activate(struct device *self, int act)
299 {
300 	struct acpibtn_softc	*sc = (struct acpibtn_softc *)self;
301 	int64_t			lid_open = 1;
302 
303 	switch (act) {
304 	case DVACT_WAKEUP:
305 		switch (sc->sc_btn_type) {
306 		case ACPIBTN_LID:
307 			aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
308 			    "_LID", 0, NULL, &lid_open);
309 			sc->sc_sens.value = lid_open;
310 			break;
311 		}
312 		break;
313 	}
314 	return (0);
315 }
316