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