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