1 /* $NetBSD: thinkpad_acpi.c,v 1.42 2012/11/24 20:56:39 riastradh 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.42 2012/11/24 20:56:39 riastradh 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 #include <dev/acpi/acpi_power.h> 41 42 #include <dev/isa/isareg.h> 43 44 #define _COMPONENT ACPI_RESOURCE_COMPONENT 45 ACPI_MODULE_NAME ("thinkpad_acpi") 46 47 #define THINKPAD_NTEMPSENSORS 8 48 #define THINKPAD_NFANSENSORS 1 49 #define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS) 50 51 typedef struct thinkpad_softc { 52 device_t sc_dev; 53 device_t sc_ecdev; 54 struct acpi_devnode *sc_node; 55 ACPI_HANDLE sc_powhdl; 56 ACPI_HANDLE sc_cmoshdl; 57 58 #define TP_PSW_SLEEP 0 59 #define TP_PSW_HIBERNATE 1 60 #define TP_PSW_DISPLAY_CYCLE 2 61 #define TP_PSW_LOCK_SCREEN 3 62 #define TP_PSW_BATTERY_INFO 4 63 #define TP_PSW_EJECT_BUTTON 5 64 #define TP_PSW_ZOOM_BUTTON 6 65 #define TP_PSW_VENDOR_BUTTON 7 66 #define TP_PSW_LAST 8 67 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 68 bool sc_smpsw_valid; 69 70 struct sysmon_envsys *sc_sme; 71 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 72 73 int sc_display_state; 74 } thinkpad_softc_t; 75 76 /* Hotkey events */ 77 #define THINKPAD_NOTIFY_FnF1 0x001 78 #define THINKPAD_NOTIFY_LockScreen 0x002 79 #define THINKPAD_NOTIFY_BatteryInfo 0x003 80 #define THINKPAD_NOTIFY_SleepButton 0x004 81 #define THINKPAD_NOTIFY_WirelessSwitch 0x005 82 #define THINKPAD_NOTIFY_wWANSwitch 0x006 83 #define THINKPAD_NOTIFY_DisplayCycle 0x007 84 #define THINKPAD_NOTIFY_PointerSwitch 0x008 85 #define THINKPAD_NOTIFY_EjectButton 0x009 86 #define THINKPAD_NOTIFY_FnF10 0x00a 87 #define THINKPAD_NOTIFY_FnF11 0x00b 88 #define THINKPAD_NOTIFY_HibernateButton 0x00c 89 #define THINKPAD_NOTIFY_BrightnessUp 0x010 90 #define THINKPAD_NOTIFY_BrightnessDown 0x011 91 #define THINKPAD_NOTIFY_ThinkLight 0x012 92 #define THINKPAD_NOTIFY_Zoom 0x014 93 #define THINKPAD_NOTIFY_VolumeUp 0x015 94 #define THINKPAD_NOTIFY_VolumeDown 0x016 95 #define THINKPAD_NOTIFY_VolumeMute 0x017 96 #define THINKPAD_NOTIFY_ThinkVantage 0x018 97 98 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 99 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 100 101 #define THINKPAD_HKEY_VERSION 0x0100 102 103 #define THINKPAD_DISPLAY_LCD 0x01 104 #define THINKPAD_DISPLAY_CRT 0x02 105 #define THINKPAD_DISPLAY_DVI 0x08 106 #define THINKPAD_DISPLAY_ALL \ 107 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 108 109 static int thinkpad_match(device_t, cfdata_t, void *); 110 static void thinkpad_attach(device_t, device_t, void *); 111 static int thinkpad_detach(device_t, int); 112 113 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t); 114 static void thinkpad_notify_handler(ACPI_HANDLE, uint32_t, 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 static void thinkpad_wwan_toggle(thinkpad_softc_t *); 124 125 static bool thinkpad_resume(device_t, const pmf_qual_t *); 126 static void thinkpad_brightness_up(device_t); 127 static void thinkpad_brightness_down(device_t); 128 static uint8_t thinkpad_brightness_read(thinkpad_softc_t *sc); 129 static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 130 131 CFATTACH_DECL_NEW(thinkpad, sizeof(thinkpad_softc_t), 132 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL); 133 134 static const char * const thinkpad_ids[] = { 135 "IBM0068", 136 "LEN0068", 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_dev = self; 177 sc->sc_powhdl = NULL; 178 sc->sc_cmoshdl = NULL; 179 sc->sc_node = aa->aa_node; 180 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 181 182 aprint_naive("\n"); 183 aprint_normal("\n"); 184 185 sc->sc_ecdev = NULL; 186 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 187 curdev != NULL; curdev = deviter_next(&di)) 188 if (device_is_a(curdev, "acpiecdt") || 189 device_is_a(curdev, "acpiec")) { 190 sc->sc_ecdev = curdev; 191 break; 192 } 193 deviter_release(&di); 194 195 if (sc->sc_ecdev) 196 aprint_debug_dev(self, "using EC at %s\n", 197 device_xname(sc->sc_ecdev)); 198 199 /* Get the supported event mask */ 200 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 201 if (ACPI_FAILURE(rv)) { 202 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 203 AcpiFormatException(rv)); 204 goto fail; 205 } 206 207 /* Enable all supported events */ 208 rv = thinkpad_mask_init(sc, val); 209 if (ACPI_FAILURE(rv)) { 210 aprint_error_dev(self, "couldn't set event mask: %s\n", 211 AcpiFormatException(rv)); 212 goto fail; 213 } 214 215 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler); 216 217 /* 218 * Obtain a handle for CMOS commands. This is used by T61. 219 */ 220 (void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl); 221 222 /* 223 * Obtain a handle to the power resource available on many models. 224 * Since pmf(9) is not yet integrated with the ACPI power resource 225 * code, this must be turned on manually upon resume. Otherwise the 226 * system may, for instance, resume from S3 with usb(4) powered down. 227 */ 228 (void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl); 229 230 /* Register power switches with sysmon */ 231 psw = sc->sc_smpsw; 232 sc->sc_smpsw_valid = true; 233 234 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 235 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 236 #if notyet 237 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 238 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 239 #endif 240 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 241 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 242 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 243 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 244 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 245 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 246 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 247 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 248 249 for (i = 0; i < TP_PSW_LAST; i++) { 250 /* not supported yet */ 251 if (i == TP_PSW_HIBERNATE) 252 continue; 253 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 254 aprint_error_dev(self, 255 "couldn't register with sysmon\n"); 256 sc->sc_smpsw_valid = false; 257 break; 258 } 259 } 260 261 /* Register temperature and fan sensors with envsys */ 262 thinkpad_sensors_init(sc); 263 264 fail: 265 if (!pmf_device_register(self, NULL, thinkpad_resume)) 266 aprint_error_dev(self, "couldn't establish power handler\n"); 267 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 268 thinkpad_brightness_up, true)) 269 aprint_error_dev(self, "couldn't register event handler\n"); 270 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 271 thinkpad_brightness_down, true)) 272 aprint_error_dev(self, "couldn't register event handler\n"); 273 } 274 275 static int 276 thinkpad_detach(device_t self, int flags) 277 { 278 struct thinkpad_softc *sc = device_private(self); 279 int i; 280 281 acpi_deregister_notify(sc->sc_node); 282 283 for (i = 0; i < TP_PSW_LAST; i++) 284 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 285 286 if (sc->sc_sme != NULL) 287 sysmon_envsys_unregister(sc->sc_sme); 288 289 pmf_device_deregister(self); 290 291 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 292 thinkpad_brightness_up, true); 293 294 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 295 thinkpad_brightness_down, true); 296 297 return 0; 298 } 299 300 static void 301 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 302 { 303 device_t self = opaque; 304 thinkpad_softc_t *sc; 305 306 sc = device_private(self); 307 308 if (notify != 0x80) { 309 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 310 return; 311 } 312 313 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 314 } 315 316 static void 317 thinkpad_get_hotkeys(void *opaque) 318 { 319 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 320 device_t self = sc->sc_dev; 321 ACPI_STATUS rv; 322 ACPI_INTEGER val; 323 int type, event; 324 325 for (;;) { 326 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 327 if (ACPI_FAILURE(rv)) { 328 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 329 AcpiFormatException(rv)); 330 return; 331 } 332 333 if (val == 0) 334 return; 335 336 type = (val & 0xf000) >> 12; 337 event = val & 0x0fff; 338 339 if (type != 1) 340 /* Only type 1 events are supported for now */ 341 continue; 342 343 switch (event) { 344 case THINKPAD_NOTIFY_BrightnessUp: 345 thinkpad_brightness_up(self); 346 break; 347 case THINKPAD_NOTIFY_BrightnessDown: 348 thinkpad_brightness_down(self); 349 break; 350 case THINKPAD_NOTIFY_WirelessSwitch: 351 thinkpad_wireless_toggle(sc); 352 break; 353 case THINKPAD_NOTIFY_wWANSwitch: 354 thinkpad_wwan_toggle(sc); 355 break; 356 case THINKPAD_NOTIFY_SleepButton: 357 if (sc->sc_smpsw_valid == false) 358 break; 359 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 360 PSWITCH_EVENT_PRESSED); 361 break; 362 case THINKPAD_NOTIFY_HibernateButton: 363 #if notyet 364 if (sc->sc_smpsw_valid == false) 365 break; 366 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 367 PSWITCH_EVENT_PRESSED); 368 #endif 369 break; 370 case THINKPAD_NOTIFY_DisplayCycle: 371 if (sc->sc_smpsw_valid == false) 372 break; 373 sysmon_pswitch_event( 374 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 375 PSWITCH_EVENT_PRESSED); 376 break; 377 case THINKPAD_NOTIFY_LockScreen: 378 if (sc->sc_smpsw_valid == false) 379 break; 380 sysmon_pswitch_event( 381 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 382 PSWITCH_EVENT_PRESSED); 383 break; 384 case THINKPAD_NOTIFY_BatteryInfo: 385 if (sc->sc_smpsw_valid == false) 386 break; 387 sysmon_pswitch_event( 388 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 389 PSWITCH_EVENT_PRESSED); 390 break; 391 case THINKPAD_NOTIFY_EjectButton: 392 if (sc->sc_smpsw_valid == false) 393 break; 394 sysmon_pswitch_event( 395 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 396 PSWITCH_EVENT_PRESSED); 397 break; 398 case THINKPAD_NOTIFY_Zoom: 399 if (sc->sc_smpsw_valid == false) 400 break; 401 sysmon_pswitch_event( 402 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 403 PSWITCH_EVENT_PRESSED); 404 break; 405 case THINKPAD_NOTIFY_ThinkVantage: 406 if (sc->sc_smpsw_valid == false) 407 break; 408 sysmon_pswitch_event( 409 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 410 PSWITCH_EVENT_PRESSED); 411 break; 412 case THINKPAD_NOTIFY_FnF1: 413 case THINKPAD_NOTIFY_PointerSwitch: 414 case THINKPAD_NOTIFY_FnF10: 415 case THINKPAD_NOTIFY_FnF11: 416 case THINKPAD_NOTIFY_ThinkLight: 417 case THINKPAD_NOTIFY_VolumeUp: 418 case THINKPAD_NOTIFY_VolumeDown: 419 case THINKPAD_NOTIFY_VolumeMute: 420 /* XXXJDM we should deliver hotkeys as keycodes */ 421 break; 422 default: 423 aprint_debug_dev(self, "notify event 0x%03x\n", event); 424 break; 425 } 426 } 427 } 428 429 static ACPI_STATUS 430 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 431 { 432 ACPI_OBJECT param[2]; 433 ACPI_OBJECT_LIST params; 434 ACPI_STATUS rv; 435 int i; 436 437 /* Update hotkey mask */ 438 params.Count = 2; 439 params.Pointer = param; 440 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 441 442 for (i = 0; i < 32; i++) { 443 param[0].Integer.Value = i + 1; 444 param[1].Integer.Value = (((1 << i) & mask) != 0); 445 446 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 447 ¶ms, NULL); 448 if (ACPI_FAILURE(rv)) 449 return rv; 450 } 451 452 /* Enable hotkey events */ 453 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 454 if (ACPI_FAILURE(rv)) { 455 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 456 AcpiFormatException(rv)); 457 return rv; 458 } 459 460 /* Claim ownership of brightness control */ 461 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 462 463 return AE_OK; 464 } 465 466 static void 467 thinkpad_sensors_init(thinkpad_softc_t *sc) 468 { 469 int i, j; 470 471 if (sc->sc_ecdev == NULL) 472 return; /* no chance of this working */ 473 474 sc->sc_sme = sysmon_envsys_create(); 475 476 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 477 478 sc->sc_sensor[i].units = ENVSYS_STEMP; 479 sc->sc_sensor[i].state = ENVSYS_SINVALID; 480 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 481 482 (void)snprintf(sc->sc_sensor[i].desc, 483 sizeof(sc->sc_sensor[i].desc), "temperature %d", i); 484 485 if (sysmon_envsys_sensor_attach(sc->sc_sme, 486 &sc->sc_sensor[i]) != 0) 487 goto fail; 488 } 489 490 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 491 492 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 493 sc->sc_sensor[i].state = ENVSYS_SINVALID; 494 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 495 496 (void)snprintf(sc->sc_sensor[i].desc, 497 sizeof(sc->sc_sensor[i].desc), "fan speed %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 edata->state = ENVSYS_SVALID; 589 } 590 591 static void 592 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 593 { 594 /* Ignore return value, as the hardware may not support bluetooth */ 595 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 596 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, NULL); 597 } 598 599 static void 600 thinkpad_wwan_toggle(thinkpad_softc_t *sc) 601 { 602 /* Ignore return value, as the hardware may not support wireless WAN */ 603 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "WTGL", NULL, NULL); 604 } 605 606 static uint8_t 607 thinkpad_brightness_read(thinkpad_softc_t *sc) 608 { 609 uint32_t val = 0; 610 611 AcpiOsWritePort(IO_RTC, 0x6c, 8); 612 AcpiOsReadPort(IO_RTC + 1, &val, 8); 613 614 return val & 7; 615 } 616 617 static void 618 thinkpad_brightness_up(device_t self) 619 { 620 thinkpad_softc_t *sc = device_private(self); 621 622 if (thinkpad_brightness_read(sc) == 7) 623 return; 624 625 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 626 } 627 628 static void 629 thinkpad_brightness_down(device_t self) 630 { 631 thinkpad_softc_t *sc = device_private(self); 632 633 if (thinkpad_brightness_read(sc) == 0) 634 return; 635 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 == NULL) 645 return; 646 647 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 648 649 if (ACPI_FAILURE(rv)) 650 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n", 651 AcpiFormatException(rv)); 652 } 653 654 static bool 655 thinkpad_resume(device_t dv, const pmf_qual_t *qual) 656 { 657 thinkpad_softc_t *sc = device_private(dv); 658 659 if (sc->sc_powhdl == NULL) 660 return true; 661 662 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true); 663 664 return true; 665 } 666 667 MODULE(MODULE_CLASS_DRIVER, thinkpad, NULL); 668 669 #ifdef _MODULE 670 #include "ioconf.c" 671 #endif 672 673 static int 674 thinkpad_modcmd(modcmd_t cmd, void *aux) 675 { 676 int rv = 0; 677 678 switch (cmd) { 679 680 case MODULE_CMD_INIT: 681 682 #ifdef _MODULE 683 rv = config_init_component(cfdriver_ioconf_thinkpad, 684 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 685 #endif 686 break; 687 688 case MODULE_CMD_FINI: 689 690 #ifdef _MODULE 691 rv = config_fini_component(cfdriver_ioconf_thinkpad, 692 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 693 #endif 694 break; 695 696 default: 697 rv = ENOTTY; 698 } 699 700 return rv; 701 } 702