1 /* $OpenBSD: acpithinkpad.c,v 1.20 2009/04/26 02:59:05 cnst Exp $ */ 2 /* 3 * Copyright (c) 2008 joshua stein <jcs@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/systm.h> 20 21 #include <dev/acpi/acpireg.h> 22 #include <dev/acpi/acpivar.h> 23 #include <dev/acpi/acpidev.h> 24 #include <dev/acpi/amltypes.h> 25 #include <dev/acpi/dsdt.h> 26 27 #define THINKPAD_HKEY_VERSION 0x0100 28 29 #define THINKPAD_CMOS_VOLUME_DOWN 0x00 30 #define THINKPAD_CMOS_VOLUME_UP 0x01 31 #define THINKPAD_CMOS_VOLUME_MUTE 0x02 32 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 33 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 34 35 #define THINKPAD_BLUETOOTH_PRESENT 0x01 36 #define THINKPAD_BLUETOOTH_ENABLED 0x02 37 38 /* wan (not wifi) card */ 39 #define THINKPAD_WAN_PRESENT 0x01 40 #define THINKPAD_WAN_ENABLED 0x02 41 42 /* type 1 events */ 43 #define THINKPAD_BUTTON_FN_F1 0x001 44 #define THINKPAD_BUTTON_LOCK_SCREEN 0x002 45 #define THINKPAD_BUTTON_BATTERY_INFO 0x003 46 #define THINKPAD_BUTTON_SUSPEND 0x004 47 #define THINKPAD_BUTTON_WIRELESS 0x005 48 #define THINKPAD_BUTTON_FN_F6 0x006 49 #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x007 50 #define THINKPAD_BUTTON_POINTER_SWITCH 0x008 51 #define THINKPAD_BUTTON_EJECT 0x009 52 #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x010 53 #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x011 54 #define THINKPAD_BUTTON_THINKLIGHT 0x012 55 #define THINKPAD_BUTTON_FN_SPACE 0x014 56 #define THINKPAD_BUTTON_VOLUME_UP 0x015 57 #define THINKPAD_BUTTON_VOLUME_DOWN 0x016 58 #define THINKPAD_BUTTON_VOLUME_MUTE 0x017 59 #define THINKPAD_BUTTON_THINKVANTAGE 0x018 60 #define THINKPAD_BUTTON_FN_F11 0x00b 61 #define THINKPAD_BUTTON_HIBERNATE 0x00c 62 63 /* type 5 events */ 64 #define THINKPAD_LID_OPEN 0x001 65 #define THINKPAD_LID_CLOSED 0x002 66 #define THINKPAD_TABLET_SCREEN_NORMAL 0x00a 67 #define THINKPAD_TABLET_SCREEN_ROTATED 0x009 68 #define THINKPAD_BRIGHTNESS_CHANGED 0x010 69 #define THINKPAD_TABLET_PEN_INSERTED 0x00b 70 #define THINKPAD_TABLET_PEN_REMOVED 0x00c 71 72 /* type 6 events */ 73 #define THINKPAD_POWER_CHANGED 0x030 74 75 /* type 7 events */ 76 #define THINKPAD_SWITCH_WIRELESS 0x000 77 78 #define THINKPAD_NSENSORS 9 79 #define THINKPAD_NTEMPSENSORS 8 80 81 #define THINKPAD_ECOFFSET_FANLO 0x84 82 #define THINKPAD_ECOFFSET_FANHI 0x85 83 84 struct acpithinkpad_softc { 85 struct device sc_dev; 86 87 struct acpiec_softc *sc_ec; 88 struct acpi_softc *sc_acpi; 89 struct aml_node *sc_devnode; 90 91 struct ksensor sc_sens[THINKPAD_NSENSORS]; 92 struct ksensordev sc_sensdev; 93 }; 94 95 extern void acpiec_read(struct acpiec_softc *, u_int8_t, int, u_int8_t *); 96 97 int thinkpad_match(struct device *, void *, void *); 98 void thinkpad_attach(struct device *, struct device *, void *); 99 int thinkpad_hotkey(struct aml_node *, int, void *); 100 int thinkpad_enable_events(struct acpithinkpad_softc *); 101 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *); 102 int thinkpad_toggle_wan(struct acpithinkpad_softc *); 103 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t); 104 int thinkpad_volume_down(struct acpithinkpad_softc *); 105 int thinkpad_volume_up(struct acpithinkpad_softc *); 106 int thinkpad_volume_mute(struct acpithinkpad_softc *); 107 int thinkpad_brightness_up(struct acpithinkpad_softc *); 108 int thinkpad_brightness_down(struct acpithinkpad_softc *); 109 110 void thinkpad_sensor_attach(struct acpithinkpad_softc *sc); 111 void thinkpad_sensor_refresh(void *); 112 113 struct cfattach acpithinkpad_ca = { 114 sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach 115 }; 116 117 struct cfdriver acpithinkpad_cd = { 118 NULL, "acpithinkpad", DV_DULL 119 }; 120 121 const char *acpithinkpad_hids[] = { ACPI_DEV_THINKPAD, 0 }; 122 123 int 124 thinkpad_match(struct device *parent, void *match, void *aux) 125 { 126 struct acpi_attach_args *aa = aux; 127 struct cfdata *cf = match; 128 int64_t res; 129 int rv = 0; 130 131 if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name)) 132 return (0); 133 134 if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node, 135 "MHKV", 0, NULL, &res)) 136 return (0); 137 138 if (res == THINKPAD_HKEY_VERSION) 139 rv = 1; 140 141 return (rv); 142 } 143 144 void 145 thinkpad_sensor_attach(struct acpithinkpad_softc *sc) 146 { 147 int i; 148 149 if (sc->sc_acpi->sc_ec == NULL) 150 return; 151 sc->sc_ec = sc->sc_acpi->sc_ec; 152 153 /* Add temperature probes */ 154 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc), 155 sizeof(sc->sc_sensdev.xname)); 156 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 157 sc->sc_sens[i].type = SENSOR_TEMP; 158 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 159 } 160 161 /* Add fan probe */ 162 sc->sc_sens[i].type = SENSOR_FANRPM; 163 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[i]); 164 165 sensordev_install(&sc->sc_sensdev); 166 } 167 168 void 169 thinkpad_sensor_refresh(void *arg) 170 { 171 struct acpithinkpad_softc *sc = arg; 172 u_int8_t lo, hi, i; 173 int64_t tmp; 174 char sname[5]; 175 176 /* Refresh sensor readings */ 177 for (i=0; i<THINKPAD_NTEMPSENSORS; i++) { 178 snprintf(sname, sizeof(sname), "TMP%d", i); 179 aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, 180 sname, 0, 0, &tmp); 181 sc->sc_sens[i].value = (tmp * 1000000) + 273150000; 182 if (tmp > 127 || tmp < -127) 183 sc->sc_sens[i].flags = SENSOR_FINVALID; 184 } 185 186 /* Read fan RPM */ 187 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo); 188 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi); 189 sc->sc_sens[i].value = ((hi << 8L) + lo); 190 } 191 192 void 193 thinkpad_attach(struct device *parent, struct device *self, void *aux) 194 { 195 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self; 196 struct acpi_attach_args *aa = aux; 197 198 sc->sc_acpi = (struct acpi_softc *)parent; 199 sc->sc_devnode = aa->aaa_node; 200 201 printf("\n"); 202 203 /* set event mask to receive everything */ 204 thinkpad_enable_events(sc); 205 thinkpad_sensor_attach(sc); 206 207 /* run thinkpad_hotkey on button presses */ 208 aml_register_notify(sc->sc_devnode, aa->aaa_dev, 209 thinkpad_hotkey, sc, ACPIDEV_POLL); 210 } 211 212 int 213 thinkpad_enable_events(struct acpithinkpad_softc *sc) 214 { 215 struct aml_value arg, args[2]; 216 int64_t mask; 217 int i, rv = 1; 218 219 /* get the supported event mask */ 220 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA", 0, NULL, &mask)) { 221 printf("%s: no MHKA\n", DEVNAME(sc)); 222 goto fail; 223 } 224 225 /* update hotkey mask */ 226 bzero(args, sizeof(args)); 227 args[0].type = args[1].type = AML_OBJTYPE_INTEGER; 228 for (i = 0; i < 32; i++) { 229 args[0].v_integer = i + 1; 230 args[1].v_integer = (((1 << i) & mask) != 0); 231 232 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM", 2, args, 233 NULL)) { 234 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc)); 235 goto fail; 236 } 237 } 238 239 /* enable hotkeys */ 240 bzero(&arg, sizeof(arg)); 241 arg.type = AML_OBJTYPE_INTEGER; 242 arg.v_integer = 1; 243 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC", 1, &arg, NULL)) { 244 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc)); 245 goto fail; 246 } 247 248 rv = 0; 249 fail: 250 return (rv); 251 } 252 253 int 254 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg) 255 { 256 struct acpithinkpad_softc *sc = arg; 257 int type, event, handled, rv = 1, tot = 0; 258 int64_t val; 259 260 if (notify_type == 0x00) { 261 /* poll sensors */ 262 thinkpad_sensor_refresh(sc); 263 return 0; 264 } 265 if (notify_type != 0x80) 266 goto fail; 267 268 for (;;) { 269 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP", 0, NULL, 270 &val)) 271 goto done; 272 if (val == 0) 273 goto done; 274 275 type = (val & 0xf000) >> 12; 276 event = val & 0x0fff; 277 handled = 0; 278 279 switch (type) { 280 case 1: 281 switch (event) { 282 case THINKPAD_BUTTON_BRIGHTNESS_UP: 283 thinkpad_brightness_up(sc); 284 handled = 1; 285 break; 286 case THINKPAD_BUTTON_BRIGHTNESS_DOWN: 287 thinkpad_brightness_down(sc); 288 handled = 1; 289 break; 290 case THINKPAD_BUTTON_WIRELESS: 291 thinkpad_toggle_bluetooth(sc); 292 handled = 1; 293 break; 294 case THINKPAD_BUTTON_SUSPEND: 295 handled = 1; 296 /* 297 acpi_enter_sleep_state(sc->sc_acpi, 298 ACPI_STATE_S3); 299 */ 300 break; 301 case THINKPAD_BUTTON_HIBERNATE: 302 case THINKPAD_BUTTON_FN_F1: 303 case THINKPAD_BUTTON_LOCK_SCREEN: 304 case THINKPAD_BUTTON_BATTERY_INFO: 305 case THINKPAD_BUTTON_FN_F6: 306 case THINKPAD_BUTTON_EXTERNAL_SCREEN: 307 case THINKPAD_BUTTON_POINTER_SWITCH: 308 case THINKPAD_BUTTON_EJECT: 309 case THINKPAD_BUTTON_THINKLIGHT: 310 case THINKPAD_BUTTON_FN_SPACE: 311 handled = 1; 312 break; 313 case THINKPAD_BUTTON_VOLUME_DOWN: 314 thinkpad_volume_down(sc); 315 handled = 1; 316 break; 317 case THINKPAD_BUTTON_VOLUME_UP: 318 thinkpad_volume_up(sc); 319 handled = 1; 320 break; 321 case THINKPAD_BUTTON_VOLUME_MUTE: 322 thinkpad_volume_mute(sc); 323 handled = 1; 324 break; 325 case THINKPAD_BUTTON_THINKVANTAGE: 326 case THINKPAD_BUTTON_FN_F11: 327 handled = 1; 328 break; 329 } 330 break; 331 case 5: 332 switch (event) { 333 case THINKPAD_LID_OPEN: 334 case THINKPAD_LID_CLOSED: 335 case THINKPAD_TABLET_SCREEN_NORMAL: 336 case THINKPAD_TABLET_SCREEN_ROTATED: 337 case THINKPAD_BRIGHTNESS_CHANGED: 338 case THINKPAD_TABLET_PEN_INSERTED: 339 case THINKPAD_TABLET_PEN_REMOVED: 340 handled = 1; 341 break; 342 } 343 break; 344 case 6: 345 switch (event) { 346 case THINKPAD_POWER_CHANGED: 347 handled = 1; 348 break; 349 } 350 break; 351 case 7: 352 switch (event) { 353 case THINKPAD_SWITCH_WIRELESS: 354 handled = 1; 355 break; 356 } 357 break; 358 } 359 360 if (handled) 361 tot++; 362 else 363 printf("%s: unknown type %d event 0x%03x\n", 364 DEVNAME(sc), type, event); 365 } 366 done: 367 if (tot) 368 rv = 0; 369 fail: 370 return (rv); 371 } 372 373 int 374 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc) 375 { 376 struct aml_value arg; 377 int rv = 1; 378 int64_t bluetooth; 379 380 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC", 0, NULL, &bluetooth)) 381 goto fail; 382 383 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT)) 384 goto fail; 385 386 bzero(&arg, sizeof(arg)); 387 arg.type = AML_OBJTYPE_INTEGER; 388 arg.v_integer = bluetooth ^= THINKPAD_BLUETOOTH_ENABLED; 389 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC", 1, &arg, NULL)) { 390 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc)); 391 goto fail; 392 } 393 394 rv = 0; 395 fail: 396 return (rv); 397 } 398 399 int 400 thinkpad_toggle_wan(struct acpithinkpad_softc *sc) 401 { 402 struct aml_value arg; 403 int rv = 1; 404 int64_t wan; 405 406 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN", 0, NULL, &wan)) 407 goto fail; 408 409 if (!(wan & THINKPAD_WAN_PRESENT)) 410 goto fail; 411 412 bzero(&arg, sizeof(arg)); 413 arg.type = AML_OBJTYPE_INTEGER; 414 arg.v_integer = (wan ^= THINKPAD_WAN_ENABLED); 415 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN", 1, &arg, NULL)) { 416 printf("%s: couldn't toggle wan\n", DEVNAME(sc)); 417 goto fail; 418 } 419 420 rv = 0; 421 fail: 422 return (rv); 423 } 424 425 int 426 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd) 427 { 428 struct aml_value arg; 429 430 bzero(&arg, sizeof(arg)); 431 arg.type = AML_OBJTYPE_INTEGER; 432 arg.v_integer = cmd; 433 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, 434 NULL)) { 435 printf("%s: cmos command 0x%x failed\n", DEVNAME(sc), cmd); 436 return (1); 437 } 438 439 return (0); 440 } 441 442 int 443 thinkpad_volume_down(struct acpithinkpad_softc *sc) 444 { 445 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN)); 446 } 447 448 int 449 thinkpad_volume_up(struct acpithinkpad_softc *sc) 450 { 451 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP)); 452 } 453 454 int 455 thinkpad_volume_mute(struct acpithinkpad_softc *sc) 456 { 457 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE)); 458 } 459 460 int 461 thinkpad_brightness_up(struct acpithinkpad_softc *sc) 462 { 463 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP)); 464 } 465 466 int 467 thinkpad_brightness_down(struct acpithinkpad_softc *sc) 468 { 469 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN)); 470 } 471