xref: /openbsd-src/sys/dev/acpi/acpibtn.c (revision 20bdc91c785fb949f0d36715e4f9c8ac93a86082)
1*20bdc91cSderaadt /* $OpenBSD: acpibtn.c,v 1.54 2024/09/21 19:06:06 deraadt Exp $ */
224469788Smarco /*
324469788Smarco  * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org>
424469788Smarco  *
524469788Smarco  * Permission to use, copy, modify, and distribute this software for any
624469788Smarco  * purpose with or without fee is hereby granted, provided that the above
724469788Smarco  * copyright notice and this permission notice appear in all copies.
824469788Smarco  *
924469788Smarco  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1024469788Smarco  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1124469788Smarco  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1224469788Smarco  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1324469788Smarco  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1424469788Smarco  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1524469788Smarco  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1624469788Smarco  */
1724469788Smarco 
1824469788Smarco #include <sys/param.h>
19155ddd61Smarco #include <sys/signalvar.h>
2024469788Smarco #include <sys/systm.h>
2124469788Smarco #include <sys/device.h>
2224469788Smarco #include <sys/malloc.h>
2324469788Smarco 
2424469788Smarco #include <machine/bus.h>
2532512613Skettenis #include <machine/apmvar.h>
2624469788Smarco 
2724469788Smarco #include <dev/acpi/acpireg.h>
2824469788Smarco #include <dev/acpi/acpivar.h>
2924469788Smarco #include <dev/acpi/acpidev.h>
3024469788Smarco #include <dev/acpi/amltypes.h>
3124469788Smarco #include <dev/acpi/dsdt.h>
3224469788Smarco 
3324469788Smarco #include <sys/sensors.h>
3424469788Smarco 
3524469788Smarco int	acpibtn_match(struct device *, void *, void *);
3624469788Smarco void	acpibtn_attach(struct device *, struct device *, void *);
3724469788Smarco int	acpibtn_notify(struct aml_node *, int, void *);
382204e1dfSmpi int	acpibtn_activate(struct device *, int);
3924469788Smarco 
4024469788Smarco struct acpibtn_softc {
4124469788Smarco 	struct device		sc_dev;
4224469788Smarco 
4324469788Smarco 	bus_space_tag_t		sc_iot;
4424469788Smarco 	bus_space_handle_t	sc_ioh;
4524469788Smarco 
4624469788Smarco 	struct acpi_softc	*sc_acpi;
4724469788Smarco 	struct aml_node		*sc_devnode;
4824469788Smarco 
49e844602dSlandry 	struct ksensor		sc_sens;
50e844602dSlandry 	struct ksensordev	sc_sensdev;
51e844602dSlandry 
52155ddd61Smarco 	int			sc_btn_type;
537b7683c7Smarco #define	ACPIBTN_UNKNOWN	0
547b7683c7Smarco #define ACPIBTN_LID	1
557b7683c7Smarco #define ACPIBTN_POWER	2
567b7683c7Smarco #define ACPIBTN_SLEEP	3
5724469788Smarco };
5824469788Smarco 
597b7683c7Smarco int	acpibtn_setpsw(struct acpibtn_softc *, int);
607b7683c7Smarco 
617b7683c7Smarco struct acpi_lid {
627b7683c7Smarco 	struct acpibtn_softc	*abl_softc;
637b7683c7Smarco 	SLIST_ENTRY(acpi_lid)	abl_link;
647b7683c7Smarco };
657b7683c7Smarco SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids =
667b7683c7Smarco     SLIST_HEAD_INITIALIZER(acpibtn_lids);
6724469788Smarco 
68471aeecfSnaddy const struct cfattach acpibtn_ca = {
692204e1dfSmpi 	sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach, NULL,
702204e1dfSmpi 	acpibtn_activate
7124469788Smarco };
7224469788Smarco 
7324469788Smarco struct cfdriver acpibtn_cd = {
7424469788Smarco 	NULL, "acpibtn", DV_DULL
7524469788Smarco };
7624469788Smarco 
77128e94b2Smlarkin const char *acpibtn_hids[] = {
78128e94b2Smlarkin 	ACPI_DEV_LD,
79128e94b2Smlarkin 	ACPI_DEV_PBD,
80128e94b2Smlarkin 	ACPI_DEV_SBD,
81128e94b2Smlarkin 	NULL
82128e94b2Smlarkin };
837df90d51Smarco 
8410b29720Smlarkin /*
8510b29720Smlarkin  * acpibtn_numopenlids
8610b29720Smlarkin  *
875a2afd51Sderaadt  * Return the number of _LID devices that are in the "open" state.
885a2afd51Sderaadt  * Used to determine if we should go back to sleep/hibernate if we
895a2afd51Sderaadt  * woke up with the all the lids still closed for some reason. If
905a2afd51Sderaadt  * the machine has no lids, returns -1.
9110b29720Smlarkin  */
9224469788Smarco int
9310b29720Smlarkin acpibtn_numopenlids(void)
9413544c12Smlarkin {
9513544c12Smlarkin 	struct acpi_lid *lid;
965a2afd51Sderaadt 	int64_t val;
975a2afd51Sderaadt 	int ct = 0;
9813544c12Smlarkin 
9910b29720Smlarkin 	/* If we have no lids ... */
10010b29720Smlarkin 	if (SLIST_EMPTY(&acpibtn_lids))
10110b29720Smlarkin 		return (-1);
10213544c12Smlarkin 
10310b29720Smlarkin 	/*
1045a2afd51Sderaadt 	 * Determine how many lids are open. Assumes _LID evals to
1055a2afd51Sderaadt 	 * non-0 or 0, for on / off (which is what the spec says).
10610b29720Smlarkin 	 */
10713544c12Smlarkin 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
10813544c12Smlarkin 		if (!aml_evalinteger(lid->abl_softc->sc_acpi,
1095a2afd51Sderaadt 		    lid->abl_softc->sc_devnode, "_LID", 0, NULL, &val) &&
1105a2afd51Sderaadt 		    val != 0)
1115a2afd51Sderaadt 			ct++;
11210b29720Smlarkin 	return (ct);
11313544c12Smlarkin }
11413544c12Smlarkin 
11513544c12Smlarkin int
1167b7683c7Smarco acpibtn_setpsw(struct acpibtn_softc *sc, int psw)
1177b7683c7Smarco {
1187b7683c7Smarco 	struct aml_value	val;
1197b7683c7Smarco 
1207b7683c7Smarco 	bzero(&val, sizeof val);
1217b7683c7Smarco 	val.type = AML_OBJTYPE_INTEGER;
1227b7683c7Smarco 	val.v_integer = psw;
1237b7683c7Smarco 	val.length = 1;
1247b7683c7Smarco 
1257b7683c7Smarco 	return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val,
1267b7683c7Smarco 	    NULL));
1277b7683c7Smarco }
1287b7683c7Smarco 
1297b7683c7Smarco void
1307b7683c7Smarco acpibtn_disable_psw(void)
1317b7683c7Smarco {
1327b7683c7Smarco 	struct acpi_lid *lid;
1337b7683c7Smarco 
1347b7683c7Smarco 	/* disable _LID for wakeup */
1357b7683c7Smarco 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
1367b7683c7Smarco 		acpibtn_setpsw(lid->abl_softc, 0);
1377b7683c7Smarco }
1387b7683c7Smarco 
1397b7683c7Smarco void
1407b7683c7Smarco acpibtn_enable_psw(void)
1417b7683c7Smarco {
1427b7683c7Smarco 	struct acpi_lid		*lid;
1437b7683c7Smarco 
1447b7683c7Smarco 	/* enable _LID for wakeup */
1457b7683c7Smarco 	SLIST_FOREACH(lid, &acpibtn_lids, abl_link)
1467b7683c7Smarco 		acpibtn_setpsw(lid->abl_softc, 1);
1477b7683c7Smarco }
1487b7683c7Smarco 
1497b7683c7Smarco int
15024469788Smarco acpibtn_match(struct device *parent, void *match, void *aux)
15124469788Smarco {
15224469788Smarco 	struct acpi_attach_args	*aa = aux;
15324469788Smarco 	struct cfdata		*cf = match;
15424469788Smarco 
15524469788Smarco 	/* sanity */
1567df90d51Smarco 	return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name));
15724469788Smarco }
15824469788Smarco 
15924469788Smarco void
16024469788Smarco acpibtn_attach(struct device *parent, struct device *self, void *aux)
16124469788Smarco {
16224469788Smarco 	struct acpibtn_softc	*sc = (struct acpibtn_softc *)self;
16324469788Smarco 	struct acpi_attach_args *aa = aux;
1647b7683c7Smarco 	struct acpi_lid		*lid;
1652204e1dfSmpi 	int64_t			lid_open = 1;
16608f3bbe2Skettenis 	int64_t			st;
16724469788Smarco 
16824469788Smarco 	sc->sc_acpi = (struct acpi_softc *)parent;
1693b455a03Smarco 	sc->sc_devnode = aa->aaa_node;
17024469788Smarco 
171d8f86d59Sdv 	printf(": %s", sc->sc_devnode->name);
17208f3bbe2Skettenis 
17308f3bbe2Skettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st))
17408f3bbe2Skettenis 		st = STA_PRESENT | STA_ENABLED | STA_DEV_OK;
17508f3bbe2Skettenis 	if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) !=
17608f3bbe2Skettenis 	    (STA_PRESENT | STA_ENABLED | STA_DEV_OK))
17708f3bbe2Skettenis 		return;
17808f3bbe2Skettenis 
1791d0deb3bSmarco 	if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) {
180155ddd61Smarco 		sc->sc_btn_type = ACPIBTN_LID;
18132aa4382Smlarkin 
18232aa4382Smlarkin 		/* Set PSW (if present) to disable wake on this LID */
18332aa4382Smlarkin 		(void)acpibtn_setpsw(sc, 0);
1847b7683c7Smarco 		lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO);
1857b7683c7Smarco 		lid->abl_softc = sc;
1867b7683c7Smarco 		SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link);
1871d0deb3bSmarco 	} else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD))
188155ddd61Smarco 		sc->sc_btn_type = ACPIBTN_POWER;
189ebd5838bSmiod 	else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD))
190155ddd61Smarco 		sc->sc_btn_type = ACPIBTN_SLEEP;
191155ddd61Smarco 
192e844602dSlandry 	if (sc->sc_btn_type == ACPIBTN_LID) {
193e844602dSlandry 		strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
194e844602dSlandry 		    sizeof(sc->sc_sensdev.xname));
195e844602dSlandry 		strlcpy(sc->sc_sens.desc, "lid open",
196e844602dSlandry 		    sizeof(sc->sc_sens.desc));
197e844602dSlandry 		sc->sc_sens.type = SENSOR_INDICATOR;
198e844602dSlandry 		sensor_attach(&sc->sc_sensdev, &sc->sc_sens);
199e844602dSlandry 		sensordev_install(&sc->sc_sensdev);
200e844602dSlandry 
201e844602dSlandry 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
202e844602dSlandry 		    "_LID", 0, NULL, &lid_open);
203e844602dSlandry 		sc->sc_sens.value = lid_open;
204e844602dSlandry 	}
205e844602dSlandry 
206d8f86d59Sdv 	/* Enable any related GPEs for wake. */
207d8f86d59Sdv 	if (acpi_toggle_wakedev(sc->sc_acpi, sc->sc_devnode, 1) == 0) {
208d8f86d59Sdv #ifdef SUSPEND
209d8f86d59Sdv 		device_register_wakeup(self);
210d8f86d59Sdv 		printf("(wakeup)");
211d8f86d59Sdv #endif /* SUSPEND */
212d8f86d59Sdv 	}
213d8f86d59Sdv 
214d8f86d59Sdv 	printf("\n");
2153b455a03Smarco 	aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify,
21699446a6dSkettenis 	    sc, ACPIDEV_NOPOLL | ACPIDEV_WAKEUP);
21724469788Smarco }
21824469788Smarco 
21924469788Smarco int
22024469788Smarco acpibtn_notify(struct aml_node *node, int notify_type, void *arg)
22124469788Smarco {
22224469788Smarco 	struct acpibtn_softc	*sc = arg;
223dcc57f84Skettenis #ifndef SMALL_KERNEL
2242d357aedSnatano 	extern int lid_action;
225e9721e48Stedu 	extern int pwr_action;
226dcc57f84Skettenis 	int64_t lid;
227dcc57f84Skettenis #endif
22824469788Smarco 
229155ddd61Smarco 	dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type,
2303b455a03Smarco 	    sc->sc_devnode->name);
231fbb4b0efSmarco 
2328a0aee6cSkettenis 	/* Ignore button events if we're resuming. */
2338a0aee6cSkettenis 	if (acpi_resuming(sc->sc_acpi))
2348a0aee6cSkettenis 		return (0);
2358a0aee6cSkettenis 
236155ddd61Smarco 	switch (sc->sc_btn_type) {
237155ddd61Smarco 	case ACPIBTN_LID:
23830cdf572Sderaadt #ifndef SMALL_KERNEL
239b8510741Sderaadt 		/*
240b8510741Sderaadt 		 * Notification of 0x80 for lid opens or closes.  We
241dcc57f84Skettenis 		 * need to check the current status by calling the
24230cdf572Sderaadt 		 * _LID method.  0 means the lid is closed and we
243dcc57f84Skettenis 		 * should go to sleep.
244b8510741Sderaadt 		 */
245dcc57f84Skettenis 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
246dcc57f84Skettenis 		    "_LID", 0, NULL, &lid))
247dcc57f84Skettenis 			return (0);
2480ebc3444Smillert 		if (sc->sc_sens.value == lid)
2490ebc3444Smillert 			break;
250e844602dSlandry 		sc->sc_sens.value = lid;
2512d357aedSnatano 
2522d357aedSnatano 		if (lid != 0)
253e844602dSlandry 			break;
2542d357aedSnatano 
2552d357aedSnatano 		switch (lid_action) {
2562d357aedSnatano 		case 1:
257*20bdc91cSderaadt 		case -1:
2581d0deb3bSmarco 			goto sleep;
2592d357aedSnatano #ifdef HIBERNATE
2602d357aedSnatano 		case 2:
2612d357aedSnatano 			/* Request to go to sleep */
2622d357aedSnatano 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
2632d357aedSnatano 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
264ad814436Sderaadt 				    sc->sc_acpi, SLEEP_HIBERNATE);
2652d357aedSnatano 			break;
2662d357aedSnatano #endif
2672d357aedSnatano 		default:
2682d357aedSnatano 			break;
2692d357aedSnatano 		}
270dcc57f84Skettenis #endif /* SMALL_KERNEL */
27130cdf572Sderaadt 		break;
2723b3e1f8dSjordan 	case ACPIBTN_SLEEP:
273de7aea19Spirofti #ifndef SMALL_KERNEL
274b8510741Sderaadt 		switch (notify_type) {
275b8510741Sderaadt 		case 0x02:
276b8510741Sderaadt 			/* "something" has been taken care of by the system */
277b8510741Sderaadt 			break;
278b8510741Sderaadt 		case 0x80:
2791d0deb3bSmarco sleep:
280b8510741Sderaadt 			/* Request to go to sleep */
2817a94bfdfSderaadt 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
28290680e5dSjordan 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
283ad814436Sderaadt 				    sc->sc_acpi, SLEEP_SUSPEND);
284b8510741Sderaadt 			break;
285b8510741Sderaadt 		}
286de7aea19Spirofti #endif /* SMALL_KERNEL */
287155ddd61Smarco 		break;
288155ddd61Smarco 	case ACPIBTN_POWER:
289e9721e48Stedu 		if (notify_type == 0x80) {
290104b5a4bStedu 			switch (pwr_action) {
291104b5a4bStedu 			case 0:
292104b5a4bStedu 				break;
293104b5a4bStedu 			case 1:
29490680e5dSjordan 				acpi_addtask(sc->sc_acpi, acpi_powerdown_task,
29590680e5dSjordan 				    sc->sc_acpi, 0);
296104b5a4bStedu 				break;
297104b5a4bStedu #ifndef SMALL_KERNEL
298104b5a4bStedu 			case 2:
299104b5a4bStedu 				goto sleep;
300104b5a4bStedu #endif
301104b5a4bStedu 			}
302e9721e48Stedu 		}
303155ddd61Smarco 		break;
304155ddd61Smarco 	default:
305023a3e08Smarco 		printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc),
306023a3e08Smarco 		    sc->sc_btn_type);
307155ddd61Smarco 		break;
308155ddd61Smarco 	}
309155ddd61Smarco 
31024469788Smarco 	return (0);
31124469788Smarco }
3122204e1dfSmpi 
3132204e1dfSmpi int
3142204e1dfSmpi acpibtn_activate(struct device *self, int act)
3152204e1dfSmpi {
3162204e1dfSmpi 	struct acpibtn_softc	*sc = (struct acpibtn_softc *)self;
3172204e1dfSmpi 	int64_t			lid_open = 1;
3182204e1dfSmpi 
3192204e1dfSmpi 	switch (act) {
3202204e1dfSmpi 	case DVACT_WAKEUP:
3212204e1dfSmpi 		switch (sc->sc_btn_type) {
3222204e1dfSmpi 		case ACPIBTN_LID:
3232204e1dfSmpi 			aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
3242204e1dfSmpi 			    "_LID", 0, NULL, &lid_open);
3252204e1dfSmpi 			sc->sc_sens.value = lid_open;
3262204e1dfSmpi 			break;
3272204e1dfSmpi 		}
3282204e1dfSmpi 		break;
3292204e1dfSmpi 	}
3302204e1dfSmpi 	return (0);
3312204e1dfSmpi }
332