1 /* $OpenBSD: acpibtn.c,v 1.44 2017/03/02 10:38:10 natano 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[] = { ACPI_DEV_LD, ACPI_DEV_PBD, ACPI_DEV_SBD, 0 }; 78 79 /* 80 * acpibtn_numopenlids 81 * 82 * Return the number of _LID devices that are in the "open" state. 83 * Used to determine if we should go back to sleep/hibernate if we 84 * woke up with the all the lids still closed for some reason. If 85 * the machine has no lids, returns -1. 86 */ 87 int 88 acpibtn_numopenlids(void) 89 { 90 struct acpi_lid *lid; 91 int64_t val; 92 int ct = 0; 93 94 /* If we have no lids ... */ 95 if (SLIST_EMPTY(&acpibtn_lids)) 96 return (-1); 97 98 /* 99 * Determine how many lids are open. Assumes _LID evals to 100 * non-0 or 0, for on / off (which is what the spec says). 101 */ 102 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 103 if (!aml_evalinteger(lid->abl_softc->sc_acpi, 104 lid->abl_softc->sc_devnode, "_LID", 0, NULL, &val) && 105 val != 0) 106 ct++; 107 return (ct); 108 } 109 110 int 111 acpibtn_setpsw(struct acpibtn_softc *sc, int psw) 112 { 113 struct aml_value val; 114 115 bzero(&val, sizeof val); 116 val.type = AML_OBJTYPE_INTEGER; 117 val.v_integer = psw; 118 val.length = 1; 119 120 return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val, 121 NULL)); 122 } 123 124 void 125 acpibtn_disable_psw(void) 126 { 127 struct acpi_lid *lid; 128 129 /* disable _LID for wakeup */ 130 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 131 acpibtn_setpsw(lid->abl_softc, 0); 132 } 133 134 void 135 acpibtn_enable_psw(void) 136 { 137 struct acpi_lid *lid; 138 139 /* enable _LID for wakeup */ 140 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 141 acpibtn_setpsw(lid->abl_softc, 1); 142 } 143 144 int 145 acpibtn_match(struct device *parent, void *match, void *aux) 146 { 147 struct acpi_attach_args *aa = aux; 148 struct cfdata *cf = match; 149 150 /* sanity */ 151 return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name)); 152 } 153 154 void 155 acpibtn_attach(struct device *parent, struct device *self, void *aux) 156 { 157 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 158 struct acpi_attach_args *aa = aux; 159 struct acpi_lid *lid; 160 int64_t lid_open = 1; 161 int64_t st; 162 163 sc->sc_acpi = (struct acpi_softc *)parent; 164 sc->sc_devnode = aa->aaa_node; 165 166 printf(": %s\n", sc->sc_devnode->name); 167 168 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, &st)) 169 st = STA_PRESENT | STA_ENABLED | STA_DEV_OK; 170 if ((st & (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) != 171 (STA_PRESENT | STA_ENABLED | STA_DEV_OK)) 172 return; 173 174 if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) { 175 sc->sc_btn_type = ACPIBTN_LID; 176 177 /* Set PSW (if present) to disable wake on this LID */ 178 (void)acpibtn_setpsw(sc, 0); 179 lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO); 180 lid->abl_softc = sc; 181 SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link); 182 } else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD)) 183 sc->sc_btn_type = ACPIBTN_POWER; 184 else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD)) 185 sc->sc_btn_type = ACPIBTN_SLEEP; 186 187 if (sc->sc_btn_type == ACPIBTN_LID) { 188 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 189 sizeof(sc->sc_sensdev.xname)); 190 strlcpy(sc->sc_sens.desc, "lid open", 191 sizeof(sc->sc_sens.desc)); 192 sc->sc_sens.type = SENSOR_INDICATOR; 193 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 194 sensordev_install(&sc->sc_sensdev); 195 196 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 197 "_LID", 0, NULL, &lid_open); 198 sc->sc_sens.value = lid_open; 199 } 200 201 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify, 202 sc, ACPIDEV_NOPOLL); 203 } 204 205 int 206 acpibtn_notify(struct aml_node *node, int notify_type, void *arg) 207 { 208 struct acpibtn_softc *sc = arg; 209 #ifndef SMALL_KERNEL 210 extern int lid_action; 211 int64_t lid; 212 #endif 213 214 dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type, 215 sc->sc_devnode->name); 216 217 switch (sc->sc_btn_type) { 218 case ACPIBTN_LID: 219 #ifndef SMALL_KERNEL 220 /* 221 * Notification of 0x80 for lid opens or closes. We 222 * need to check the current status by calling the 223 * _LID method. 0 means the lid is closed and we 224 * should go to sleep. 225 */ 226 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 227 "_LID", 0, NULL, &lid)) 228 return (0); 229 sc->sc_sens.value = lid; 230 231 if (lid != 0) 232 break; 233 234 switch (lid_action) { 235 case 1: 236 goto sleep; 237 #ifdef HIBERNATE 238 case 2: 239 /* Request to go to sleep */ 240 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ)) 241 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 242 sc->sc_acpi, ACPI_SLEEP_HIBERNATE); 243 break; 244 #endif 245 default: 246 break; 247 } 248 #endif /* SMALL_KERNEL */ 249 break; 250 case ACPIBTN_SLEEP: 251 #ifndef SMALL_KERNEL 252 switch (notify_type) { 253 case 0x02: 254 /* "something" has been taken care of by the system */ 255 break; 256 case 0x80: 257 sleep: 258 /* Request to go to sleep */ 259 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 260 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 261 sc->sc_acpi, ACPI_SLEEP_SUSPEND); 262 break; 263 } 264 #endif /* SMALL_KERNEL */ 265 break; 266 case ACPIBTN_POWER: 267 if (notify_type == 0x80) 268 acpi_addtask(sc->sc_acpi, acpi_powerdown_task, 269 sc->sc_acpi, 0); 270 break; 271 default: 272 printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc), 273 sc->sc_btn_type); 274 break; 275 } 276 277 return (0); 278 } 279 280 int 281 acpibtn_activate(struct device *self, int act) 282 { 283 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 284 int64_t lid_open = 1; 285 286 switch (act) { 287 case DVACT_WAKEUP: 288 switch (sc->sc_btn_type) { 289 case ACPIBTN_LID: 290 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 291 "_LID", 0, NULL, &lid_open); 292 sc->sc_sens.value = lid_open; 293 break; 294 } 295 break; 296 } 297 return (0); 298 } 299