1 /* $OpenBSD: acpibtn.c,v 1.42 2016/01/07 12:08:18 mpi 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_suspend; 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 if (lid_suspend == 0) 231 break; 232 if (lid == 0) 233 goto sleep; 234 #endif /* SMALL_KERNEL */ 235 break; 236 case ACPIBTN_SLEEP: 237 #ifndef SMALL_KERNEL 238 switch (notify_type) { 239 case 0x02: 240 /* "something" has been taken care of by the system */ 241 break; 242 case 0x80: 243 sleep: 244 /* Request to go to sleep */ 245 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 246 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 247 sc->sc_acpi, ACPI_STATE_S3); 248 break; 249 } 250 #endif /* SMALL_KERNEL */ 251 break; 252 case ACPIBTN_POWER: 253 if (notify_type == 0x80) 254 acpi_addtask(sc->sc_acpi, acpi_powerdown_task, 255 sc->sc_acpi, 0); 256 break; 257 default: 258 printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc), 259 sc->sc_btn_type); 260 break; 261 } 262 263 return (0); 264 } 265 266 int 267 acpibtn_activate(struct device *self, int act) 268 { 269 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 270 int64_t lid_open = 1; 271 272 switch (act) { 273 case DVACT_WAKEUP: 274 switch (sc->sc_btn_type) { 275 case ACPIBTN_LID: 276 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 277 "_LID", 0, NULL, &lid_open); 278 sc->sc_sens.value = lid_open; 279 break; 280 } 281 break; 282 } 283 return (0); 284 } 285