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