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