1 /* $NetBSD: thinkpad_acpi.c,v 1.25 2010/01/31 17:53:31 jruoho Exp $ */ 2 3 /*- 4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.25 2010/01/31 17:53:31 jruoho Exp $"); 31 32 #include <sys/types.h> 33 #include <sys/param.h> 34 #include <sys/malloc.h> 35 #include <sys/buf.h> 36 #include <sys/callout.h> 37 #include <sys/kernel.h> 38 #include <sys/device.h> 39 #include <sys/pmf.h> 40 #include <sys/queue.h> 41 #include <sys/kmem.h> 42 43 #include <dev/acpi/acpireg.h> 44 #include <dev/acpi/acpivar.h> 45 #include <dev/acpi/acpi_ecvar.h> 46 47 #if defined(__i386__) || defined(__amd64__) 48 #include <dev/isa/isareg.h> 49 #include <machine/pio.h> 50 #endif 51 52 #define _COMPONENT ACPI_RESOURCE_COMPONENT 53 ACPI_MODULE_NAME ("thinkpad_acpi") 54 55 #define THINKPAD_NTEMPSENSORS 8 56 #define THINKPAD_NFANSENSORS 1 57 #define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS) 58 59 typedef struct thinkpad_softc { 60 device_t sc_dev; 61 device_t sc_ecdev; 62 struct acpi_devnode *sc_node; 63 ACPI_HANDLE sc_cmoshdl; 64 bool sc_cmoshdl_valid; 65 66 #define TP_PSW_SLEEP 0 67 #define TP_PSW_HIBERNATE 1 68 #define TP_PSW_DISPLAY_CYCLE 2 69 #define TP_PSW_LOCK_SCREEN 3 70 #define TP_PSW_BATTERY_INFO 4 71 #define TP_PSW_EJECT_BUTTON 5 72 #define TP_PSW_ZOOM_BUTTON 6 73 #define TP_PSW_VENDOR_BUTTON 7 74 #define TP_PSW_LAST 8 75 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 76 bool sc_smpsw_valid; 77 78 struct sysmon_envsys *sc_sme; 79 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 80 81 int sc_display_state; 82 } thinkpad_softc_t; 83 84 /* Hotkey events */ 85 #define THINKPAD_NOTIFY_FnF1 0x001 86 #define THINKPAD_NOTIFY_LockScreen 0x002 87 #define THINKPAD_NOTIFY_BatteryInfo 0x003 88 #define THINKPAD_NOTIFY_SleepButton 0x004 89 #define THINKPAD_NOTIFY_WirelessSwitch 0x005 90 #define THINKPAD_NOTIFY_FnF6 0x006 91 #define THINKPAD_NOTIFY_DisplayCycle 0x007 92 #define THINKPAD_NOTIFY_PointerSwitch 0x008 93 #define THINKPAD_NOTIFY_EjectButton 0x009 94 #define THINKPAD_NOTIFY_FnF10 0x00a 95 #define THINKPAD_NOTIFY_FnF11 0x00b 96 #define THINKPAD_NOTIFY_HibernateButton 0x00c 97 #define THINKPAD_NOTIFY_BrightnessUp 0x010 98 #define THINKPAD_NOTIFY_BrightnessDown 0x011 99 #define THINKPAD_NOTIFY_ThinkLight 0x012 100 #define THINKPAD_NOTIFY_Zoom 0x014 101 #define THINKPAD_NOTIFY_VolumeUp 0x015 102 #define THINKPAD_NOTIFY_VolumeDown 0x016 103 #define THINKPAD_NOTIFY_VolumeMute 0x017 104 #define THINKPAD_NOTIFY_ThinkVantage 0x018 105 106 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 107 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 108 109 #define THINKPAD_HKEY_VERSION 0x0100 110 111 #define THINKPAD_DISPLAY_LCD 0x01 112 #define THINKPAD_DISPLAY_CRT 0x02 113 #define THINKPAD_DISPLAY_DVI 0x08 114 #define THINKPAD_DISPLAY_ALL \ 115 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 116 117 static int thinkpad_match(device_t, cfdata_t, void *); 118 static void thinkpad_attach(device_t, device_t, void *); 119 static int thinkpad_detach(device_t, int); 120 121 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t); 122 static void thinkpad_notify_handler(ACPI_HANDLE, UINT32, void *); 123 static void thinkpad_get_hotkeys(void *); 124 125 static void thinkpad_sensors_init(thinkpad_softc_t *); 126 static void thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); 127 static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *); 128 static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *); 129 130 static void thinkpad_wireless_toggle(thinkpad_softc_t *); 131 132 static bool thinkpad_resume(device_t, pmf_qual_t); 133 static void thinkpad_brightness_up(device_t); 134 static void thinkpad_brightness_down(device_t); 135 static uint8_t thinkpad_brightness_read(thinkpad_softc_t *sc); 136 static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 137 138 CFATTACH_DECL_NEW(thinkpad, sizeof(thinkpad_softc_t), 139 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL); 140 141 static const char * const thinkpad_ids[] = { 142 "IBM0068", 143 NULL 144 }; 145 146 static int 147 thinkpad_match(device_t parent, cfdata_t match, void *opaque) 148 { 149 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 150 ACPI_INTEGER ver; 151 152 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 153 return 0; 154 155 if (!acpi_match_hid(aa->aa_node->ad_devinfo, thinkpad_ids)) 156 return 0; 157 158 /* We only support hotkey version 0x0100 */ 159 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", 160 &ver))) 161 return 0; 162 163 if (ver != THINKPAD_HKEY_VERSION) 164 return 0; 165 166 /* Cool, looks like we're good to go */ 167 return 1; 168 } 169 170 static void 171 thinkpad_attach(device_t parent, device_t self, void *opaque) 172 { 173 thinkpad_softc_t *sc = device_private(self); 174 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 175 struct sysmon_pswitch *psw; 176 device_t curdev; 177 deviter_t di; 178 ACPI_STATUS rv; 179 ACPI_INTEGER val; 180 int i; 181 182 sc->sc_node = aa->aa_node; 183 sc->sc_dev = self; 184 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 185 186 aprint_naive("\n"); 187 aprint_normal("\n"); 188 189 /* T61 uses \UCMS method for issuing CMOS commands */ 190 rv = AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl); 191 if (ACPI_FAILURE(rv)) 192 sc->sc_cmoshdl_valid = false; 193 else { 194 aprint_debug_dev(self, "using CMOS at \\UCMS\n"); 195 sc->sc_cmoshdl_valid = true; 196 } 197 198 sc->sc_ecdev = NULL; 199 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 200 curdev != NULL; curdev = deviter_next(&di)) 201 if (device_is_a(curdev, "acpiecdt") || 202 device_is_a(curdev, "acpiec")) { 203 sc->sc_ecdev = curdev; 204 break; 205 } 206 deviter_release(&di); 207 208 if (sc->sc_ecdev) 209 aprint_debug_dev(self, "using EC at %s\n", 210 device_xname(sc->sc_ecdev)); 211 212 /* Get the supported event mask */ 213 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 214 if (ACPI_FAILURE(rv)) { 215 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 216 AcpiFormatException(rv)); 217 goto fail; 218 } 219 220 /* Enable all supported events */ 221 rv = thinkpad_mask_init(sc, val); 222 if (ACPI_FAILURE(rv)) { 223 aprint_error_dev(self, "couldn't set event mask: %s\n", 224 AcpiFormatException(rv)); 225 goto fail; 226 } 227 228 /* Install notify handler for events */ 229 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 230 ACPI_DEVICE_NOTIFY, thinkpad_notify_handler, sc); 231 if (ACPI_FAILURE(rv)) 232 aprint_error_dev(self, "couldn't install notify handler: %s\n", 233 AcpiFormatException(rv)); 234 235 /* Register power switches with sysmon */ 236 psw = sc->sc_smpsw; 237 sc->sc_smpsw_valid = true; 238 239 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 240 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 241 #if notyet 242 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 243 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 244 #endif 245 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 246 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 247 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 248 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 249 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 250 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 251 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 252 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 253 254 for (i = 0; i < TP_PSW_LAST; i++) { 255 /* not supported yet */ 256 if (i == TP_PSW_HIBERNATE) 257 continue; 258 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 259 aprint_error_dev(self, 260 "couldn't register with sysmon\n"); 261 sc->sc_smpsw_valid = false; 262 break; 263 } 264 } 265 266 /* Register temperature and fan sensors with envsys */ 267 thinkpad_sensors_init(sc); 268 269 fail: 270 if (!pmf_device_register(self, NULL, thinkpad_resume)) 271 aprint_error_dev(self, "couldn't establish power handler\n"); 272 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 273 thinkpad_brightness_up, true)) 274 aprint_error_dev(self, "couldn't register event handler\n"); 275 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 276 thinkpad_brightness_down, true)) 277 aprint_error_dev(self, "couldn't register event handler\n"); 278 } 279 280 static int 281 thinkpad_detach(device_t self, int flags) 282 { 283 struct thinkpad_softc *sc = device_private(self); 284 ACPI_STATUS rv; 285 int i; 286 287 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle, 288 ACPI_DEVICE_NOTIFY, thinkpad_notify_handler); 289 290 if (ACPI_FAILURE(rv)) 291 return EBUSY; 292 293 for (i = 0; i < TP_PSW_LAST; i++) 294 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 295 296 if (sc->sc_sme != NULL) 297 sysmon_envsys_unregister(sc->sc_sme); 298 299 pmf_device_deregister(self); 300 301 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 302 thinkpad_brightness_up, true); 303 304 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 305 thinkpad_brightness_down, true); 306 307 return 0; 308 } 309 310 static void 311 thinkpad_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque) 312 { 313 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 314 device_t self = sc->sc_dev; 315 ACPI_STATUS rv; 316 317 if (notify != 0x80) { 318 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 319 return; 320 } 321 322 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 323 if (ACPI_FAILURE(rv)) 324 aprint_error_dev(self, "couldn't queue hotkey handler: %s\n", 325 AcpiFormatException(rv)); 326 } 327 328 static void 329 thinkpad_get_hotkeys(void *opaque) 330 { 331 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 332 device_t self = sc->sc_dev; 333 ACPI_STATUS rv; 334 ACPI_INTEGER val; 335 int type, event; 336 337 for (;;) { 338 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 339 if (ACPI_FAILURE(rv)) { 340 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 341 AcpiFormatException(rv)); 342 return; 343 } 344 345 if (val == 0) 346 return; 347 348 type = (val & 0xf000) >> 12; 349 event = val & 0x0fff; 350 351 if (type != 1) 352 /* Only type 1 events are supported for now */ 353 continue; 354 355 switch (event) { 356 case THINKPAD_NOTIFY_BrightnessUp: 357 thinkpad_brightness_up(self); 358 break; 359 case THINKPAD_NOTIFY_BrightnessDown: 360 thinkpad_brightness_down(self); 361 break; 362 case THINKPAD_NOTIFY_WirelessSwitch: 363 thinkpad_wireless_toggle(sc); 364 break; 365 case THINKPAD_NOTIFY_SleepButton: 366 if (sc->sc_smpsw_valid == false) 367 break; 368 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 369 PSWITCH_EVENT_PRESSED); 370 break; 371 case THINKPAD_NOTIFY_HibernateButton: 372 #if notyet 373 if (sc->sc_smpsw_valid == false) 374 break; 375 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 376 PSWITCH_EVENT_PRESSED); 377 #endif 378 break; 379 case THINKPAD_NOTIFY_DisplayCycle: 380 if (sc->sc_smpsw_valid == false) 381 break; 382 sysmon_pswitch_event( 383 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 384 PSWITCH_EVENT_PRESSED); 385 break; 386 case THINKPAD_NOTIFY_LockScreen: 387 if (sc->sc_smpsw_valid == false) 388 break; 389 sysmon_pswitch_event( 390 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 391 PSWITCH_EVENT_PRESSED); 392 break; 393 case THINKPAD_NOTIFY_BatteryInfo: 394 if (sc->sc_smpsw_valid == false) 395 break; 396 sysmon_pswitch_event( 397 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 398 PSWITCH_EVENT_PRESSED); 399 break; 400 case THINKPAD_NOTIFY_EjectButton: 401 if (sc->sc_smpsw_valid == false) 402 break; 403 sysmon_pswitch_event( 404 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 405 PSWITCH_EVENT_PRESSED); 406 break; 407 case THINKPAD_NOTIFY_Zoom: 408 if (sc->sc_smpsw_valid == false) 409 break; 410 sysmon_pswitch_event( 411 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 412 PSWITCH_EVENT_PRESSED); 413 break; 414 case THINKPAD_NOTIFY_ThinkVantage: 415 if (sc->sc_smpsw_valid == false) 416 break; 417 sysmon_pswitch_event( 418 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 419 PSWITCH_EVENT_PRESSED); 420 break; 421 case THINKPAD_NOTIFY_FnF1: 422 case THINKPAD_NOTIFY_FnF6: 423 case THINKPAD_NOTIFY_PointerSwitch: 424 case THINKPAD_NOTIFY_FnF10: 425 case THINKPAD_NOTIFY_FnF11: 426 case THINKPAD_NOTIFY_ThinkLight: 427 case THINKPAD_NOTIFY_VolumeUp: 428 case THINKPAD_NOTIFY_VolumeDown: 429 case THINKPAD_NOTIFY_VolumeMute: 430 /* XXXJDM we should deliver hotkeys as keycodes */ 431 break; 432 default: 433 aprint_debug_dev(self, "notify event 0x%03x\n", event); 434 break; 435 } 436 } 437 } 438 439 static ACPI_STATUS 440 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 441 { 442 ACPI_OBJECT param[2]; 443 ACPI_OBJECT_LIST params; 444 ACPI_STATUS rv; 445 int i; 446 447 /* Update hotkey mask */ 448 params.Count = 2; 449 params.Pointer = param; 450 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 451 452 for (i = 0; i < 32; i++) { 453 param[0].Integer.Value = i + 1; 454 param[1].Integer.Value = (((1 << i) & mask) != 0); 455 456 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 457 ¶ms, NULL); 458 if (ACPI_FAILURE(rv)) 459 return rv; 460 } 461 462 /* Enable hotkey events */ 463 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 464 if (ACPI_FAILURE(rv)) { 465 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 466 AcpiFormatException(rv)); 467 return rv; 468 } 469 470 /* Claim ownership of brightness control */ 471 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 472 473 return AE_OK; 474 } 475 476 static void 477 thinkpad_sensors_init(thinkpad_softc_t *sc) 478 { 479 int i, j; 480 481 if (sc->sc_ecdev == NULL) 482 return; /* no chance of this working */ 483 484 sc->sc_sme = sysmon_envsys_create(); 485 486 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 487 488 sc->sc_sensor[i].units = ENVSYS_STEMP; 489 490 (void)snprintf(sc->sc_sensor[i].desc, 491 sizeof(sc->sc_sensor[i].desc), "TMP%d", i); 492 493 if (sysmon_envsys_sensor_attach(sc->sc_sme, 494 &sc->sc_sensor[i]) != 0) 495 goto fail; 496 } 497 498 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 499 500 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 501 502 (void)snprintf(sc->sc_sensor[i].desc, 503 sizeof(sc->sc_sensor[i].desc), "FAN%d", j); 504 505 if (sysmon_envsys_sensor_attach(sc->sc_sme, 506 &sc->sc_sensor[i]) != 0) 507 goto fail; 508 } 509 510 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 511 sc->sc_sme->sme_cookie = sc; 512 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 513 514 if (sysmon_envsys_register(sc->sc_sme) != 0) 515 goto fail; 516 517 return; 518 519 fail: 520 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 521 sysmon_envsys_destroy(sc->sc_sme); 522 sc->sc_sme = NULL; 523 } 524 525 static void 526 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 527 { 528 switch (edata->units) { 529 case ENVSYS_STEMP: 530 thinkpad_temp_refresh(sme, edata); 531 break; 532 case ENVSYS_SFANRPM: 533 thinkpad_fan_refresh(sme, edata); 534 break; 535 default: 536 break; 537 } 538 } 539 540 static void 541 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 542 { 543 thinkpad_softc_t *sc = sme->sme_cookie; 544 char sname[5] = "TMP?"; 545 ACPI_INTEGER val; 546 ACPI_STATUS rv; 547 int temp; 548 549 sname[3] = '0' + edata->sensor; 550 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 551 if (ACPI_FAILURE(rv)) { 552 edata->state = ENVSYS_SINVALID; 553 return; 554 } 555 temp = (int)val; 556 if (temp > 127 || temp < -127) { 557 edata->state = ENVSYS_SINVALID; 558 return; 559 } 560 561 edata->value_cur = temp * 1000000 + 273150000; 562 edata->state = ENVSYS_SVALID; 563 } 564 565 static void 566 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 567 { 568 thinkpad_softc_t *sc = sme->sme_cookie; 569 ACPI_INTEGER lo; 570 ACPI_INTEGER hi; 571 ACPI_STATUS rv; 572 int rpm; 573 574 /* 575 * Read the low byte first to avoid a firmware bug. 576 */ 577 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 578 if (ACPI_FAILURE(rv)) { 579 edata->state = ENVSYS_SINVALID; 580 return; 581 } 582 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 583 if (ACPI_FAILURE(rv)) { 584 edata->state = ENVSYS_SINVALID; 585 return; 586 } 587 rpm = ((((int)hi) << 8) | ((int)lo)); 588 if (rpm < 0) { 589 edata->state = ENVSYS_SINVALID; 590 return; 591 } 592 593 edata->value_cur = rpm; 594 if (rpm < edata->value_min || edata->value_min == -1) 595 edata->value_min = rpm; 596 if (rpm > edata->value_max || edata->value_max == -1) 597 edata->value_max = rpm; 598 edata->state = ENVSYS_SVALID; 599 } 600 601 static void 602 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 603 { 604 /* Ignore return value, as the hardware may not support bluetooth */ 605 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 606 } 607 608 static uint8_t 609 thinkpad_brightness_read(thinkpad_softc_t *sc) 610 { 611 #if defined(__i386__) || defined(__amd64__) 612 /* 613 * We have two ways to get the current brightness -- either via 614 * magic RTC registers, or using the EC. Since I don't dare mess 615 * with the EC, and Thinkpads are x86-only, this will have to do 616 * for now. 617 */ 618 outb(IO_RTC, 0x6c); 619 return inb(IO_RTC+1) & 7; 620 #else 621 return 0; 622 #endif 623 } 624 625 static void 626 thinkpad_brightness_up(device_t self) 627 { 628 thinkpad_softc_t *sc = device_private(self); 629 630 if (thinkpad_brightness_read(sc) == 7) 631 return; 632 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 633 } 634 635 static void 636 thinkpad_brightness_down(device_t self) 637 { 638 thinkpad_softc_t *sc = device_private(self); 639 640 if (thinkpad_brightness_read(sc) == 0) 641 return; 642 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 643 } 644 645 static void 646 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 647 { 648 ACPI_STATUS rv; 649 650 if (sc->sc_cmoshdl_valid == false) 651 return; 652 653 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 654 if (ACPI_FAILURE(rv)) 655 aprint_error_dev(sc->sc_dev, "couldn't evalute CMOS: %s\n", 656 AcpiFormatException(rv)); 657 } 658 659 static bool 660 thinkpad_resume(device_t dv, pmf_qual_t qual) 661 { 662 ACPI_STATUS rv; 663 ACPI_HANDLE pubs; 664 665 rv = AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &pubs); 666 if (ACPI_FAILURE(rv)) 667 return true; /* not fatal */ 668 669 rv = AcpiEvaluateObject(pubs, "_ON", NULL, NULL); 670 if (ACPI_FAILURE(rv)) 671 aprint_error_dev(dv, "failed to execute PUBS._ON: %s\n", 672 AcpiFormatException(rv)); 673 674 return true; 675 } 676