1 /* $NetBSD: thinkpad_acpi.c,v 1.33 2011/01/18 18:56:25 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.33 2011/01/18 18:56:25 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/device.h> 34 #include <sys/module.h> 35 #include <sys/systm.h> 36 37 #include <dev/acpi/acpireg.h> 38 #include <dev/acpi/acpivar.h> 39 #include <dev/acpi/acpi_ecvar.h> 40 41 #include <dev/isa/isareg.h> 42 43 #define _COMPONENT ACPI_RESOURCE_COMPONENT 44 ACPI_MODULE_NAME ("thinkpad_acpi") 45 46 #define THINKPAD_NTEMPSENSORS 8 47 #define THINKPAD_NFANSENSORS 1 48 #define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS) 49 50 typedef struct thinkpad_softc { 51 device_t sc_dev; 52 device_t sc_ecdev; 53 struct acpi_devnode *sc_node; 54 ACPI_HANDLE sc_cmoshdl; 55 bool sc_cmoshdl_valid; 56 57 #define TP_PSW_SLEEP 0 58 #define TP_PSW_HIBERNATE 1 59 #define TP_PSW_DISPLAY_CYCLE 2 60 #define TP_PSW_LOCK_SCREEN 3 61 #define TP_PSW_BATTERY_INFO 4 62 #define TP_PSW_EJECT_BUTTON 5 63 #define TP_PSW_ZOOM_BUTTON 6 64 #define TP_PSW_VENDOR_BUTTON 7 65 #define TP_PSW_LAST 8 66 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 67 bool sc_smpsw_valid; 68 69 struct sysmon_envsys *sc_sme; 70 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 71 72 int sc_display_state; 73 } thinkpad_softc_t; 74 75 /* Hotkey events */ 76 #define THINKPAD_NOTIFY_FnF1 0x001 77 #define THINKPAD_NOTIFY_LockScreen 0x002 78 #define THINKPAD_NOTIFY_BatteryInfo 0x003 79 #define THINKPAD_NOTIFY_SleepButton 0x004 80 #define THINKPAD_NOTIFY_WirelessSwitch 0x005 81 #define THINKPAD_NOTIFY_FnF6 0x006 82 #define THINKPAD_NOTIFY_DisplayCycle 0x007 83 #define THINKPAD_NOTIFY_PointerSwitch 0x008 84 #define THINKPAD_NOTIFY_EjectButton 0x009 85 #define THINKPAD_NOTIFY_FnF10 0x00a 86 #define THINKPAD_NOTIFY_FnF11 0x00b 87 #define THINKPAD_NOTIFY_HibernateButton 0x00c 88 #define THINKPAD_NOTIFY_BrightnessUp 0x010 89 #define THINKPAD_NOTIFY_BrightnessDown 0x011 90 #define THINKPAD_NOTIFY_ThinkLight 0x012 91 #define THINKPAD_NOTIFY_Zoom 0x014 92 #define THINKPAD_NOTIFY_VolumeUp 0x015 93 #define THINKPAD_NOTIFY_VolumeDown 0x016 94 #define THINKPAD_NOTIFY_VolumeMute 0x017 95 #define THINKPAD_NOTIFY_ThinkVantage 0x018 96 97 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 98 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 99 100 #define THINKPAD_HKEY_VERSION 0x0100 101 102 #define THINKPAD_DISPLAY_LCD 0x01 103 #define THINKPAD_DISPLAY_CRT 0x02 104 #define THINKPAD_DISPLAY_DVI 0x08 105 #define THINKPAD_DISPLAY_ALL \ 106 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 107 108 static int thinkpad_match(device_t, cfdata_t, void *); 109 static void thinkpad_attach(device_t, device_t, void *); 110 static int thinkpad_detach(device_t, int); 111 112 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t); 113 static void thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *); 114 static void thinkpad_get_hotkeys(void *); 115 116 static void thinkpad_sensors_init(thinkpad_softc_t *); 117 static void thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); 118 static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *); 119 static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *); 120 121 static void thinkpad_wireless_toggle(thinkpad_softc_t *); 122 123 static bool thinkpad_resume(device_t, const pmf_qual_t *); 124 static void thinkpad_brightness_up(device_t); 125 static void thinkpad_brightness_down(device_t); 126 static uint8_t thinkpad_brightness_read(thinkpad_softc_t *sc); 127 static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 128 129 CFATTACH_DECL_NEW(thinkpad, sizeof(thinkpad_softc_t), 130 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL); 131 132 static const char * const thinkpad_ids[] = { 133 "IBM0068", 134 NULL 135 }; 136 137 static int 138 thinkpad_match(device_t parent, cfdata_t match, void *opaque) 139 { 140 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 141 ACPI_INTEGER ver; 142 143 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 144 return 0; 145 146 if (!acpi_match_hid(aa->aa_node->ad_devinfo, thinkpad_ids)) 147 return 0; 148 149 /* We only support hotkey version 0x0100 */ 150 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", 151 &ver))) 152 return 0; 153 154 if (ver != THINKPAD_HKEY_VERSION) 155 return 0; 156 157 /* Cool, looks like we're good to go */ 158 return 1; 159 } 160 161 static void 162 thinkpad_attach(device_t parent, device_t self, void *opaque) 163 { 164 thinkpad_softc_t *sc = device_private(self); 165 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 166 struct sysmon_pswitch *psw; 167 device_t curdev; 168 deviter_t di; 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 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 191 curdev != NULL; curdev = deviter_next(&di)) 192 if (device_is_a(curdev, "acpiecdt") || 193 device_is_a(curdev, "acpiec")) { 194 sc->sc_ecdev = curdev; 195 break; 196 } 197 deviter_release(&di); 198 199 if (sc->sc_ecdev) 200 aprint_debug_dev(self, "using EC at %s\n", 201 device_xname(sc->sc_ecdev)); 202 203 /* Get the supported event mask */ 204 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 205 if (ACPI_FAILURE(rv)) { 206 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 207 AcpiFormatException(rv)); 208 goto fail; 209 } 210 211 /* Enable all supported events */ 212 rv = thinkpad_mask_init(sc, val); 213 if (ACPI_FAILURE(rv)) { 214 aprint_error_dev(self, "couldn't set event mask: %s\n", 215 AcpiFormatException(rv)); 216 goto fail; 217 } 218 219 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler); 220 221 /* Register power switches with sysmon */ 222 psw = sc->sc_smpsw; 223 sc->sc_smpsw_valid = true; 224 225 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 226 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 227 #if notyet 228 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 229 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 230 #endif 231 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 232 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 233 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 234 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 235 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 236 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 237 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 238 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 239 240 for (i = 0; i < TP_PSW_LAST; i++) { 241 /* not supported yet */ 242 if (i == TP_PSW_HIBERNATE) 243 continue; 244 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 245 aprint_error_dev(self, 246 "couldn't register with sysmon\n"); 247 sc->sc_smpsw_valid = false; 248 break; 249 } 250 } 251 252 /* Register temperature and fan sensors with envsys */ 253 thinkpad_sensors_init(sc); 254 255 fail: 256 if (!pmf_device_register(self, NULL, thinkpad_resume)) 257 aprint_error_dev(self, "couldn't establish power handler\n"); 258 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 259 thinkpad_brightness_up, true)) 260 aprint_error_dev(self, "couldn't register event handler\n"); 261 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 262 thinkpad_brightness_down, true)) 263 aprint_error_dev(self, "couldn't register event handler\n"); 264 } 265 266 static int 267 thinkpad_detach(device_t self, int flags) 268 { 269 struct thinkpad_softc *sc = device_private(self); 270 int i; 271 272 acpi_deregister_notify(sc->sc_node); 273 274 for (i = 0; i < TP_PSW_LAST; i++) 275 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 276 277 if (sc->sc_sme != NULL) 278 sysmon_envsys_unregister(sc->sc_sme); 279 280 pmf_device_deregister(self); 281 282 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 283 thinkpad_brightness_up, true); 284 285 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 286 thinkpad_brightness_down, true); 287 288 return 0; 289 } 290 291 static void 292 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 293 { 294 device_t self = opaque; 295 thinkpad_softc_t *sc; 296 297 sc = device_private(self); 298 299 if (notify != 0x80) { 300 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 301 return; 302 } 303 304 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 305 } 306 307 static void 308 thinkpad_get_hotkeys(void *opaque) 309 { 310 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 311 device_t self = sc->sc_dev; 312 ACPI_STATUS rv; 313 ACPI_INTEGER val; 314 int type, event; 315 316 for (;;) { 317 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 318 if (ACPI_FAILURE(rv)) { 319 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 320 AcpiFormatException(rv)); 321 return; 322 } 323 324 if (val == 0) 325 return; 326 327 type = (val & 0xf000) >> 12; 328 event = val & 0x0fff; 329 330 if (type != 1) 331 /* Only type 1 events are supported for now */ 332 continue; 333 334 switch (event) { 335 case THINKPAD_NOTIFY_BrightnessUp: 336 thinkpad_brightness_up(self); 337 break; 338 case THINKPAD_NOTIFY_BrightnessDown: 339 thinkpad_brightness_down(self); 340 break; 341 case THINKPAD_NOTIFY_WirelessSwitch: 342 thinkpad_wireless_toggle(sc); 343 break; 344 case THINKPAD_NOTIFY_SleepButton: 345 if (sc->sc_smpsw_valid == false) 346 break; 347 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 348 PSWITCH_EVENT_PRESSED); 349 break; 350 case THINKPAD_NOTIFY_HibernateButton: 351 #if notyet 352 if (sc->sc_smpsw_valid == false) 353 break; 354 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 355 PSWITCH_EVENT_PRESSED); 356 #endif 357 break; 358 case THINKPAD_NOTIFY_DisplayCycle: 359 if (sc->sc_smpsw_valid == false) 360 break; 361 sysmon_pswitch_event( 362 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 363 PSWITCH_EVENT_PRESSED); 364 break; 365 case THINKPAD_NOTIFY_LockScreen: 366 if (sc->sc_smpsw_valid == false) 367 break; 368 sysmon_pswitch_event( 369 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 370 PSWITCH_EVENT_PRESSED); 371 break; 372 case THINKPAD_NOTIFY_BatteryInfo: 373 if (sc->sc_smpsw_valid == false) 374 break; 375 sysmon_pswitch_event( 376 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 377 PSWITCH_EVENT_PRESSED); 378 break; 379 case THINKPAD_NOTIFY_EjectButton: 380 if (sc->sc_smpsw_valid == false) 381 break; 382 sysmon_pswitch_event( 383 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 384 PSWITCH_EVENT_PRESSED); 385 break; 386 case THINKPAD_NOTIFY_Zoom: 387 if (sc->sc_smpsw_valid == false) 388 break; 389 sysmon_pswitch_event( 390 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 391 PSWITCH_EVENT_PRESSED); 392 break; 393 case THINKPAD_NOTIFY_ThinkVantage: 394 if (sc->sc_smpsw_valid == false) 395 break; 396 sysmon_pswitch_event( 397 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 398 PSWITCH_EVENT_PRESSED); 399 break; 400 case THINKPAD_NOTIFY_FnF1: 401 case THINKPAD_NOTIFY_FnF6: 402 case THINKPAD_NOTIFY_PointerSwitch: 403 case THINKPAD_NOTIFY_FnF10: 404 case THINKPAD_NOTIFY_FnF11: 405 case THINKPAD_NOTIFY_ThinkLight: 406 case THINKPAD_NOTIFY_VolumeUp: 407 case THINKPAD_NOTIFY_VolumeDown: 408 case THINKPAD_NOTIFY_VolumeMute: 409 /* XXXJDM we should deliver hotkeys as keycodes */ 410 break; 411 default: 412 aprint_debug_dev(self, "notify event 0x%03x\n", event); 413 break; 414 } 415 } 416 } 417 418 static ACPI_STATUS 419 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 420 { 421 ACPI_OBJECT param[2]; 422 ACPI_OBJECT_LIST params; 423 ACPI_STATUS rv; 424 int i; 425 426 /* Update hotkey mask */ 427 params.Count = 2; 428 params.Pointer = param; 429 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 430 431 for (i = 0; i < 32; i++) { 432 param[0].Integer.Value = i + 1; 433 param[1].Integer.Value = (((1 << i) & mask) != 0); 434 435 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 436 ¶ms, NULL); 437 if (ACPI_FAILURE(rv)) 438 return rv; 439 } 440 441 /* Enable hotkey events */ 442 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 443 if (ACPI_FAILURE(rv)) { 444 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 445 AcpiFormatException(rv)); 446 return rv; 447 } 448 449 /* Claim ownership of brightness control */ 450 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 451 452 return AE_OK; 453 } 454 455 static void 456 thinkpad_sensors_init(thinkpad_softc_t *sc) 457 { 458 int i, j; 459 460 if (sc->sc_ecdev == NULL) 461 return; /* no chance of this working */ 462 463 sc->sc_sme = sysmon_envsys_create(); 464 465 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 466 467 sc->sc_sensor[i].units = ENVSYS_STEMP; 468 469 (void)snprintf(sc->sc_sensor[i].desc, 470 sizeof(sc->sc_sensor[i].desc), "temperature %d", i); 471 472 if (sysmon_envsys_sensor_attach(sc->sc_sme, 473 &sc->sc_sensor[i]) != 0) 474 goto fail; 475 } 476 477 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 478 479 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 480 481 (void)snprintf(sc->sc_sensor[i].desc, 482 sizeof(sc->sc_sensor[i].desc), "fan speed %d", j); 483 484 if (sysmon_envsys_sensor_attach(sc->sc_sme, 485 &sc->sc_sensor[i]) != 0) 486 goto fail; 487 } 488 489 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 490 sc->sc_sme->sme_cookie = sc; 491 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 492 493 if (sysmon_envsys_register(sc->sc_sme) != 0) 494 goto fail; 495 496 return; 497 498 fail: 499 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 500 sysmon_envsys_destroy(sc->sc_sme); 501 sc->sc_sme = NULL; 502 } 503 504 static void 505 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 506 { 507 switch (edata->units) { 508 case ENVSYS_STEMP: 509 thinkpad_temp_refresh(sme, edata); 510 break; 511 case ENVSYS_SFANRPM: 512 thinkpad_fan_refresh(sme, edata); 513 break; 514 default: 515 break; 516 } 517 } 518 519 static void 520 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 521 { 522 thinkpad_softc_t *sc = sme->sme_cookie; 523 char sname[5] = "TMP?"; 524 ACPI_INTEGER val; 525 ACPI_STATUS rv; 526 int temp; 527 528 sname[3] = '0' + edata->sensor; 529 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 530 if (ACPI_FAILURE(rv)) { 531 edata->state = ENVSYS_SINVALID; 532 return; 533 } 534 temp = (int)val; 535 if (temp > 127 || temp < -127) { 536 edata->state = ENVSYS_SINVALID; 537 return; 538 } 539 540 edata->value_cur = temp * 1000000 + 273150000; 541 edata->state = ENVSYS_SVALID; 542 } 543 544 static void 545 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 546 { 547 thinkpad_softc_t *sc = sme->sme_cookie; 548 ACPI_INTEGER lo; 549 ACPI_INTEGER hi; 550 ACPI_STATUS rv; 551 int rpm; 552 553 /* 554 * Read the low byte first to avoid a firmware bug. 555 */ 556 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 557 if (ACPI_FAILURE(rv)) { 558 edata->state = ENVSYS_SINVALID; 559 return; 560 } 561 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 562 if (ACPI_FAILURE(rv)) { 563 edata->state = ENVSYS_SINVALID; 564 return; 565 } 566 rpm = ((((int)hi) << 8) | ((int)lo)); 567 if (rpm < 0) { 568 edata->state = ENVSYS_SINVALID; 569 return; 570 } 571 572 edata->value_cur = rpm; 573 if (rpm < edata->value_min || edata->value_min == -1) 574 edata->value_min = rpm; 575 if (rpm > edata->value_max || edata->value_max == -1) 576 edata->value_max = rpm; 577 edata->state = ENVSYS_SVALID; 578 } 579 580 static void 581 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 582 { 583 /* Ignore return value, as the hardware may not support bluetooth */ 584 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 585 } 586 587 static uint8_t 588 thinkpad_brightness_read(thinkpad_softc_t *sc) 589 { 590 uint32_t val = 0; 591 592 AcpiOsWritePort(IO_RTC, 0x6c, 8); 593 AcpiOsReadPort(IO_RTC + 1, &val, 8); 594 595 return val & 7; 596 } 597 598 static void 599 thinkpad_brightness_up(device_t self) 600 { 601 thinkpad_softc_t *sc = device_private(self); 602 603 if (thinkpad_brightness_read(sc) == 7) 604 return; 605 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 606 } 607 608 static void 609 thinkpad_brightness_down(device_t self) 610 { 611 thinkpad_softc_t *sc = device_private(self); 612 613 if (thinkpad_brightness_read(sc) == 0) 614 return; 615 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 616 } 617 618 static void 619 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 620 { 621 ACPI_STATUS rv; 622 623 if (sc->sc_cmoshdl_valid == false) 624 return; 625 626 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 627 if (ACPI_FAILURE(rv)) 628 aprint_error_dev(sc->sc_dev, "couldn't evalute CMOS: %s\n", 629 AcpiFormatException(rv)); 630 } 631 632 static bool 633 thinkpad_resume(device_t dv, const pmf_qual_t *qual) 634 { 635 ACPI_STATUS rv; 636 ACPI_HANDLE pubs; 637 638 rv = AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &pubs); 639 if (ACPI_FAILURE(rv)) 640 return true; /* not fatal */ 641 642 rv = AcpiEvaluateObject(pubs, "_ON", NULL, NULL); 643 if (ACPI_FAILURE(rv)) 644 aprint_error_dev(dv, "failed to execute PUBS._ON: %s\n", 645 AcpiFormatException(rv)); 646 647 return true; 648 } 649 650 #ifdef _MODULE 651 652 MODULE(MODULE_CLASS_DRIVER, thinkpad, NULL); 653 CFDRIVER_DECL(thinkpad, DV_DULL, NULL); 654 655 static int thinkpadloc[] = { -1 }; 656 extern struct cfattach thinkpad_ca; 657 658 static struct cfparent acpiparent = { 659 "acpinodebus", NULL, DVUNIT_ANY 660 }; 661 662 static struct cfdata thinkpad_cfdata[] = { 663 { 664 .cf_name = "thinkpad", 665 .cf_atname = "thinkpad", 666 .cf_unit = 0, 667 .cf_fstate = FSTATE_STAR, 668 .cf_loc = thinkpadloc, 669 .cf_flags = 0, 670 .cf_pspec = &acpiparent, 671 }, 672 673 { NULL, NULL, 0, 0, NULL, 0, NULL } 674 }; 675 676 static int 677 thinkpad_modcmd(modcmd_t cmd, void *opaque) 678 { 679 int err; 680 681 switch (cmd) { 682 683 case MODULE_CMD_INIT: 684 685 err = config_cfdriver_attach(&thinkpad_cd); 686 687 if (err != 0) 688 return err; 689 690 err = config_cfattach_attach("thinkpad", &thinkpad_ca); 691 692 if (err != 0) { 693 config_cfdriver_detach(&thinkpad_cd); 694 return err; 695 } 696 697 err = config_cfdata_attach(thinkpad_cfdata, 1); 698 699 if (err != 0) { 700 config_cfattach_detach("thinkpad", &thinkpad_ca); 701 config_cfdriver_detach(&thinkpad_cd); 702 return err; 703 } 704 705 return 0; 706 707 case MODULE_CMD_FINI: 708 709 err = config_cfdata_detach(thinkpad_cfdata); 710 711 if (err != 0) 712 return err; 713 714 config_cfattach_detach("thinkpad", &thinkpad_ca); 715 config_cfdriver_detach(&thinkpad_cd); 716 717 return 0; 718 719 default: 720 return ENOTTY; 721 } 722 } 723 724 #endif /* _MODULE */ 725