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