1 /* $NetBSD: thinkpad_acpi.c,v 1.28 2010/03/05 14:00:17 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.28 2010/03/05 14:00:17 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, 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 /* Install notify handler for events */ 223 rv = AcpiInstallNotifyHandler(sc->sc_node->ad_handle, 224 ACPI_DEVICE_NOTIFY, thinkpad_notify_handler, sc); 225 if (ACPI_FAILURE(rv)) 226 aprint_error_dev(self, "couldn't install notify handler: %s\n", 227 AcpiFormatException(rv)); 228 229 /* Register power switches with sysmon */ 230 psw = sc->sc_smpsw; 231 sc->sc_smpsw_valid = true; 232 233 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 234 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 235 #if notyet 236 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 237 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 238 #endif 239 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 240 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 241 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 242 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 243 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 244 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 245 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 246 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 247 248 for (i = 0; i < TP_PSW_LAST; i++) { 249 /* not supported yet */ 250 if (i == TP_PSW_HIBERNATE) 251 continue; 252 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 253 aprint_error_dev(self, 254 "couldn't register with sysmon\n"); 255 sc->sc_smpsw_valid = false; 256 break; 257 } 258 } 259 260 /* Register temperature and fan sensors with envsys */ 261 thinkpad_sensors_init(sc); 262 263 fail: 264 if (!pmf_device_register(self, NULL, thinkpad_resume)) 265 aprint_error_dev(self, "couldn't establish power handler\n"); 266 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 267 thinkpad_brightness_up, true)) 268 aprint_error_dev(self, "couldn't register event handler\n"); 269 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 270 thinkpad_brightness_down, true)) 271 aprint_error_dev(self, "couldn't register event handler\n"); 272 } 273 274 static int 275 thinkpad_detach(device_t self, int flags) 276 { 277 struct thinkpad_softc *sc = device_private(self); 278 ACPI_STATUS rv; 279 int i; 280 281 rv = AcpiRemoveNotifyHandler(sc->sc_node->ad_handle, 282 ACPI_DEVICE_NOTIFY, thinkpad_notify_handler); 283 284 if (ACPI_FAILURE(rv)) 285 return EBUSY; 286 287 for (i = 0; i < TP_PSW_LAST; i++) 288 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 289 290 if (sc->sc_sme != NULL) 291 sysmon_envsys_unregister(sc->sc_sme); 292 293 pmf_device_deregister(self); 294 295 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 296 thinkpad_brightness_up, true); 297 298 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 299 thinkpad_brightness_down, true); 300 301 return 0; 302 } 303 304 static void 305 thinkpad_notify_handler(ACPI_HANDLE hdl, UINT32 notify, void *opaque) 306 { 307 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 308 device_t self = sc->sc_dev; 309 ACPI_STATUS rv; 310 311 if (notify != 0x80) { 312 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 313 return; 314 } 315 316 rv = AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 317 if (ACPI_FAILURE(rv)) 318 aprint_error_dev(self, "couldn't queue hotkey handler: %s\n", 319 AcpiFormatException(rv)); 320 } 321 322 static void 323 thinkpad_get_hotkeys(void *opaque) 324 { 325 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 326 device_t self = sc->sc_dev; 327 ACPI_STATUS rv; 328 ACPI_INTEGER val; 329 int type, event; 330 331 for (;;) { 332 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 333 if (ACPI_FAILURE(rv)) { 334 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 335 AcpiFormatException(rv)); 336 return; 337 } 338 339 if (val == 0) 340 return; 341 342 type = (val & 0xf000) >> 12; 343 event = val & 0x0fff; 344 345 if (type != 1) 346 /* Only type 1 events are supported for now */ 347 continue; 348 349 switch (event) { 350 case THINKPAD_NOTIFY_BrightnessUp: 351 thinkpad_brightness_up(self); 352 break; 353 case THINKPAD_NOTIFY_BrightnessDown: 354 thinkpad_brightness_down(self); 355 break; 356 case THINKPAD_NOTIFY_WirelessSwitch: 357 thinkpad_wireless_toggle(sc); 358 break; 359 case THINKPAD_NOTIFY_SleepButton: 360 if (sc->sc_smpsw_valid == false) 361 break; 362 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 363 PSWITCH_EVENT_PRESSED); 364 break; 365 case THINKPAD_NOTIFY_HibernateButton: 366 #if notyet 367 if (sc->sc_smpsw_valid == false) 368 break; 369 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 370 PSWITCH_EVENT_PRESSED); 371 #endif 372 break; 373 case THINKPAD_NOTIFY_DisplayCycle: 374 if (sc->sc_smpsw_valid == false) 375 break; 376 sysmon_pswitch_event( 377 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 378 PSWITCH_EVENT_PRESSED); 379 break; 380 case THINKPAD_NOTIFY_LockScreen: 381 if (sc->sc_smpsw_valid == false) 382 break; 383 sysmon_pswitch_event( 384 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 385 PSWITCH_EVENT_PRESSED); 386 break; 387 case THINKPAD_NOTIFY_BatteryInfo: 388 if (sc->sc_smpsw_valid == false) 389 break; 390 sysmon_pswitch_event( 391 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 392 PSWITCH_EVENT_PRESSED); 393 break; 394 case THINKPAD_NOTIFY_EjectButton: 395 if (sc->sc_smpsw_valid == false) 396 break; 397 sysmon_pswitch_event( 398 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 399 PSWITCH_EVENT_PRESSED); 400 break; 401 case THINKPAD_NOTIFY_Zoom: 402 if (sc->sc_smpsw_valid == false) 403 break; 404 sysmon_pswitch_event( 405 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 406 PSWITCH_EVENT_PRESSED); 407 break; 408 case THINKPAD_NOTIFY_ThinkVantage: 409 if (sc->sc_smpsw_valid == false) 410 break; 411 sysmon_pswitch_event( 412 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 413 PSWITCH_EVENT_PRESSED); 414 break; 415 case THINKPAD_NOTIFY_FnF1: 416 case THINKPAD_NOTIFY_FnF6: 417 case THINKPAD_NOTIFY_PointerSwitch: 418 case THINKPAD_NOTIFY_FnF10: 419 case THINKPAD_NOTIFY_FnF11: 420 case THINKPAD_NOTIFY_ThinkLight: 421 case THINKPAD_NOTIFY_VolumeUp: 422 case THINKPAD_NOTIFY_VolumeDown: 423 case THINKPAD_NOTIFY_VolumeMute: 424 /* XXXJDM we should deliver hotkeys as keycodes */ 425 break; 426 default: 427 aprint_debug_dev(self, "notify event 0x%03x\n", event); 428 break; 429 } 430 } 431 } 432 433 static ACPI_STATUS 434 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 435 { 436 ACPI_OBJECT param[2]; 437 ACPI_OBJECT_LIST params; 438 ACPI_STATUS rv; 439 int i; 440 441 /* Update hotkey mask */ 442 params.Count = 2; 443 params.Pointer = param; 444 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 445 446 for (i = 0; i < 32; i++) { 447 param[0].Integer.Value = i + 1; 448 param[1].Integer.Value = (((1 << i) & mask) != 0); 449 450 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 451 ¶ms, NULL); 452 if (ACPI_FAILURE(rv)) 453 return rv; 454 } 455 456 /* Enable hotkey events */ 457 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 458 if (ACPI_FAILURE(rv)) { 459 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 460 AcpiFormatException(rv)); 461 return rv; 462 } 463 464 /* Claim ownership of brightness control */ 465 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 466 467 return AE_OK; 468 } 469 470 static void 471 thinkpad_sensors_init(thinkpad_softc_t *sc) 472 { 473 int i, j; 474 475 if (sc->sc_ecdev == NULL) 476 return; /* no chance of this working */ 477 478 sc->sc_sme = sysmon_envsys_create(); 479 480 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 481 482 sc->sc_sensor[i].units = ENVSYS_STEMP; 483 484 (void)snprintf(sc->sc_sensor[i].desc, 485 sizeof(sc->sc_sensor[i].desc), "TMP%d", i); 486 487 if (sysmon_envsys_sensor_attach(sc->sc_sme, 488 &sc->sc_sensor[i]) != 0) 489 goto fail; 490 } 491 492 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 493 494 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 495 496 (void)snprintf(sc->sc_sensor[i].desc, 497 sizeof(sc->sc_sensor[i].desc), "FAN%d", j); 498 499 if (sysmon_envsys_sensor_attach(sc->sc_sme, 500 &sc->sc_sensor[i]) != 0) 501 goto fail; 502 } 503 504 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 505 sc->sc_sme->sme_cookie = sc; 506 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 507 508 if (sysmon_envsys_register(sc->sc_sme) != 0) 509 goto fail; 510 511 return; 512 513 fail: 514 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 515 sysmon_envsys_destroy(sc->sc_sme); 516 sc->sc_sme = NULL; 517 } 518 519 static void 520 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 521 { 522 switch (edata->units) { 523 case ENVSYS_STEMP: 524 thinkpad_temp_refresh(sme, edata); 525 break; 526 case ENVSYS_SFANRPM: 527 thinkpad_fan_refresh(sme, edata); 528 break; 529 default: 530 break; 531 } 532 } 533 534 static void 535 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 536 { 537 thinkpad_softc_t *sc = sme->sme_cookie; 538 char sname[5] = "TMP?"; 539 ACPI_INTEGER val; 540 ACPI_STATUS rv; 541 int temp; 542 543 sname[3] = '0' + edata->sensor; 544 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 545 if (ACPI_FAILURE(rv)) { 546 edata->state = ENVSYS_SINVALID; 547 return; 548 } 549 temp = (int)val; 550 if (temp > 127 || temp < -127) { 551 edata->state = ENVSYS_SINVALID; 552 return; 553 } 554 555 edata->value_cur = temp * 1000000 + 273150000; 556 edata->state = ENVSYS_SVALID; 557 } 558 559 static void 560 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 561 { 562 thinkpad_softc_t *sc = sme->sme_cookie; 563 ACPI_INTEGER lo; 564 ACPI_INTEGER hi; 565 ACPI_STATUS rv; 566 int rpm; 567 568 /* 569 * Read the low byte first to avoid a firmware bug. 570 */ 571 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 572 if (ACPI_FAILURE(rv)) { 573 edata->state = ENVSYS_SINVALID; 574 return; 575 } 576 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 577 if (ACPI_FAILURE(rv)) { 578 edata->state = ENVSYS_SINVALID; 579 return; 580 } 581 rpm = ((((int)hi) << 8) | ((int)lo)); 582 if (rpm < 0) { 583 edata->state = ENVSYS_SINVALID; 584 return; 585 } 586 587 edata->value_cur = rpm; 588 if (rpm < edata->value_min || edata->value_min == -1) 589 edata->value_min = rpm; 590 if (rpm > edata->value_max || edata->value_max == -1) 591 edata->value_max = rpm; 592 edata->state = ENVSYS_SVALID; 593 } 594 595 static void 596 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 597 { 598 /* Ignore return value, as the hardware may not support bluetooth */ 599 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 600 } 601 602 static uint8_t 603 thinkpad_brightness_read(thinkpad_softc_t *sc) 604 { 605 #if defined(__i386__) || defined(__amd64__) 606 /* 607 * We have two ways to get the current brightness -- either via 608 * magic RTC registers, or using the EC. Since I don't dare mess 609 * with the EC, and Thinkpads are x86-only, this will have to do 610 * for now. 611 */ 612 outb(IO_RTC, 0x6c); 613 return inb(IO_RTC+1) & 7; 614 #else 615 return 0; 616 #endif 617 } 618 619 static void 620 thinkpad_brightness_up(device_t self) 621 { 622 thinkpad_softc_t *sc = device_private(self); 623 624 if (thinkpad_brightness_read(sc) == 7) 625 return; 626 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 627 } 628 629 static void 630 thinkpad_brightness_down(device_t self) 631 { 632 thinkpad_softc_t *sc = device_private(self); 633 634 if (thinkpad_brightness_read(sc) == 0) 635 return; 636 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 637 } 638 639 static void 640 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 641 { 642 ACPI_STATUS rv; 643 644 if (sc->sc_cmoshdl_valid == false) 645 return; 646 647 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 648 if (ACPI_FAILURE(rv)) 649 aprint_error_dev(sc->sc_dev, "couldn't evalute CMOS: %s\n", 650 AcpiFormatException(rv)); 651 } 652 653 static bool 654 thinkpad_resume(device_t dv, const pmf_qual_t *qual) 655 { 656 ACPI_STATUS rv; 657 ACPI_HANDLE pubs; 658 659 rv = AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &pubs); 660 if (ACPI_FAILURE(rv)) 661 return true; /* not fatal */ 662 663 rv = AcpiEvaluateObject(pubs, "_ON", NULL, NULL); 664 if (ACPI_FAILURE(rv)) 665 aprint_error_dev(dv, "failed to execute PUBS._ON: %s\n", 666 AcpiFormatException(rv)); 667 668 return true; 669 } 670 671 #ifdef _MODULE 672 673 MODULE(MODULE_CLASS_DRIVER, thinkpad, NULL); 674 CFDRIVER_DECL(thinkpad, DV_DULL, NULL); 675 676 static int thinkpadloc[] = { -1 }; 677 extern struct cfattach thinkpad_ca; 678 679 static struct cfparent acpiparent = { 680 "acpinodebus", NULL, DVUNIT_ANY 681 }; 682 683 static struct cfdata thinkpad_cfdata[] = { 684 { 685 .cf_name = "thinkpad", 686 .cf_atname = "thinkpad", 687 .cf_unit = 0, 688 .cf_fstate = FSTATE_STAR, 689 .cf_loc = thinkpadloc, 690 .cf_flags = 0, 691 .cf_pspec = &acpiparent, 692 }, 693 694 { NULL } 695 }; 696 697 static int 698 thinkpad_modcmd(modcmd_t cmd, void *opaque) 699 { 700 int err; 701 702 switch (cmd) { 703 704 case MODULE_CMD_INIT: 705 706 err = config_cfdriver_attach(&thinkpad_cd); 707 708 if (err != 0) 709 return err; 710 711 err = config_cfattach_attach("thinkpad", &thinkpad_ca); 712 713 if (err != 0) { 714 config_cfdriver_detach(&thinkpad_cd); 715 return err; 716 } 717 718 err = config_cfdata_attach(thinkpad_cfdata, 1); 719 720 if (err != 0) { 721 config_cfattach_detach("thinkpad", &thinkpad_ca); 722 config_cfdriver_detach(&thinkpad_cd); 723 return err; 724 } 725 726 return 0; 727 728 case MODULE_CMD_FINI: 729 730 err = config_cfdata_detach(thinkpad_cfdata); 731 732 if (err != 0) 733 return err; 734 735 config_cfattach_detach("thinkpad", &thinkpad_ca); 736 config_cfdriver_detach(&thinkpad_cd); 737 738 return 0; 739 740 default: 741 return ENOTTY; 742 } 743 } 744 745 #endif /* _MODULE */ 746