1 /* $OpenBSD: acpibtn.c,v 1.35 2013/08/21 20:10:47 landry 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/proc.h> 20 #include <sys/signalvar.h> 21 #include <sys/systm.h> 22 #include <sys/device.h> 23 #include <sys/malloc.h> 24 25 #include <machine/bus.h> 26 #include <machine/apmvar.h> 27 28 #include <dev/acpi/acpireg.h> 29 #include <dev/acpi/acpivar.h> 30 #include <dev/acpi/acpidev.h> 31 #include <dev/acpi/amltypes.h> 32 #include <dev/acpi/dsdt.h> 33 34 #include <sys/sensors.h> 35 36 int acpibtn_match(struct device *, void *, void *); 37 void acpibtn_attach(struct device *, struct device *, void *); 38 int acpibtn_notify(struct aml_node *, int, void *); 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_getsta(struct acpibtn_softc *); 60 int acpibtn_setpsw(struct acpibtn_softc *, int); 61 62 struct acpi_lid { 63 struct acpibtn_softc *abl_softc; 64 SLIST_ENTRY(acpi_lid) abl_link; 65 }; 66 SLIST_HEAD(acpi_lid_head, acpi_lid) acpibtn_lids = 67 SLIST_HEAD_INITIALIZER(acpibtn_lids); 68 69 struct cfattach acpibtn_ca = { 70 sizeof(struct acpibtn_softc), acpibtn_match, acpibtn_attach 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 int 80 acpibtn_setpsw(struct acpibtn_softc *sc, int psw) 81 { 82 struct aml_value val; 83 84 bzero(&val, sizeof val); 85 val.type = AML_OBJTYPE_INTEGER; 86 val.v_integer = psw; 87 val.length = 1; 88 89 return (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSW", 1, &val, 90 NULL)); 91 } 92 93 void 94 acpibtn_disable_psw(void) 95 { 96 struct acpi_lid *lid; 97 98 /* disable _LID for wakeup */ 99 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 100 acpibtn_setpsw(lid->abl_softc, 0); 101 } 102 103 void 104 acpibtn_enable_psw(void) 105 { 106 struct acpi_lid *lid; 107 108 /* enable _LID for wakeup */ 109 SLIST_FOREACH(lid, &acpibtn_lids, abl_link) 110 acpibtn_setpsw(lid->abl_softc, 1); 111 } 112 113 int 114 acpibtn_match(struct device *parent, void *match, void *aux) 115 { 116 struct acpi_attach_args *aa = aux; 117 struct cfdata *cf = match; 118 119 /* sanity */ 120 return (acpi_matchhids(aa, acpibtn_hids, cf->cf_driver->cd_name)); 121 } 122 123 void 124 acpibtn_attach(struct device *parent, struct device *self, void *aux) 125 { 126 struct acpibtn_softc *sc = (struct acpibtn_softc *)self; 127 struct acpi_attach_args *aa = aux; 128 struct acpi_lid *lid; 129 int64_t lid_open; 130 131 sc->sc_acpi = (struct acpi_softc *)parent; 132 sc->sc_devnode = aa->aaa_node; 133 134 if (!strcmp(aa->aaa_dev, ACPI_DEV_LD)) { 135 sc->sc_btn_type = ACPIBTN_LID; 136 if (acpibtn_setpsw(sc, 0) == 0) { 137 lid = malloc(sizeof(*lid), M_DEVBUF, M_WAITOK | M_ZERO); 138 lid->abl_softc = sc; 139 SLIST_INSERT_HEAD(&acpibtn_lids, lid, abl_link); 140 } 141 } else if (!strcmp(aa->aaa_dev, ACPI_DEV_PBD)) 142 sc->sc_btn_type = ACPIBTN_POWER; 143 else if (!strcmp(aa->aaa_dev, ACPI_DEV_SBD)) 144 sc->sc_btn_type = ACPIBTN_SLEEP; 145 146 acpibtn_getsta(sc); 147 148 printf(": %s\n", sc->sc_devnode->name); 149 150 if (sc->sc_btn_type == ACPIBTN_LID) { 151 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 152 sizeof(sc->sc_sensdev.xname)); 153 strlcpy(sc->sc_sens.desc, "lid open", 154 sizeof(sc->sc_sens.desc)); 155 sc->sc_sens.type = SENSOR_INDICATOR; 156 sensor_attach(&sc->sc_sensdev, &sc->sc_sens); 157 sensordev_install(&sc->sc_sensdev); 158 159 aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 160 "_LID", 0, NULL, &lid_open); 161 sc->sc_sens.value = lid_open; 162 } 163 164 aml_register_notify(sc->sc_devnode, aa->aaa_dev, acpibtn_notify, 165 sc, ACPIDEV_NOPOLL); 166 } 167 168 int 169 acpibtn_getsta(struct acpibtn_softc *sc) 170 { 171 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_STA", 0, NULL, NULL) != 0) { 172 dnprintf(20, "%s: no _STA\n", DEVNAME(sc)); 173 /* XXX not all buttons have _STA so FALLTROUGH */ 174 } 175 176 return (0); 177 } 178 179 int 180 acpibtn_notify(struct aml_node *node, int notify_type, void *arg) 181 { 182 struct acpibtn_softc *sc = arg; 183 #ifndef SMALL_KERNEL 184 extern int lid_suspend; 185 int64_t lid; 186 #endif 187 188 dnprintf(10, "acpibtn_notify: %.2x %s\n", notify_type, 189 sc->sc_devnode->name); 190 191 switch (sc->sc_btn_type) { 192 case ACPIBTN_LID: 193 #ifndef SMALL_KERNEL 194 /* 195 * Notification of 0x80 for lid opens or closes. We 196 * need to check the current status by calling the 197 * _LID method. 0 means the lid is closed and we 198 * should go to sleep. 199 */ 200 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, 201 "_LID", 0, NULL, &lid)) 202 return (0); 203 sc->sc_sens.value = lid; 204 if (lid_suspend == 0) 205 break; 206 if (lid == 0) 207 goto sleep; 208 #endif /* SMALL_KERNEL */ 209 break; 210 case ACPIBTN_SLEEP: 211 #ifndef SMALL_KERNEL 212 switch (notify_type) { 213 case 0x02: 214 /* "something" has been taken care of by the system */ 215 break; 216 case 0x80: 217 sleep: 218 /* Request to go to sleep */ 219 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ)) 220 acpi_addtask(sc->sc_acpi, acpi_sleep_task, 221 sc->sc_acpi, ACPI_STATE_S3); 222 break; 223 } 224 #endif /* SMALL_KERNEL */ 225 break; 226 case ACPIBTN_POWER: 227 if (notify_type == 0x80) 228 acpi_addtask(sc->sc_acpi, acpi_powerdown_task, 229 sc->sc_acpi, 0); 230 break; 231 default: 232 printf("%s: spurious acpi button interrupt %i\n", DEVNAME(sc), 233 sc->sc_btn_type); 234 break; 235 } 236 237 return (0); 238 } 239