1 /* $NetBSD: thinkpad_acpi.c,v 1.41 2012/08/14 14:36:43 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.41 2012/08/14 14:36:43 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 #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 NULL 137 }; 138 139 static int 140 thinkpad_match(device_t parent, cfdata_t match, void *opaque) 141 { 142 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 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 /* We only support hotkey version 0x0100 */ 152 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", 153 &ver))) 154 return 0; 155 156 if (ver != THINKPAD_HKEY_VERSION) 157 return 0; 158 159 /* Cool, looks like we're good to go */ 160 return 1; 161 } 162 163 static void 164 thinkpad_attach(device_t parent, device_t self, void *opaque) 165 { 166 thinkpad_softc_t *sc = device_private(self); 167 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 168 struct sysmon_pswitch *psw; 169 device_t curdev; 170 deviter_t di; 171 ACPI_STATUS rv; 172 ACPI_INTEGER val; 173 int i; 174 175 sc->sc_dev = self; 176 sc->sc_powhdl = NULL; 177 sc->sc_cmoshdl = NULL; 178 sc->sc_node = aa->aa_node; 179 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 180 181 aprint_naive("\n"); 182 aprint_normal("\n"); 183 184 sc->sc_ecdev = NULL; 185 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 186 curdev != NULL; curdev = deviter_next(&di)) 187 if (device_is_a(curdev, "acpiecdt") || 188 device_is_a(curdev, "acpiec")) { 189 sc->sc_ecdev = curdev; 190 break; 191 } 192 deviter_release(&di); 193 194 if (sc->sc_ecdev) 195 aprint_debug_dev(self, "using EC at %s\n", 196 device_xname(sc->sc_ecdev)); 197 198 /* Get the supported event mask */ 199 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 200 if (ACPI_FAILURE(rv)) { 201 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 202 AcpiFormatException(rv)); 203 goto fail; 204 } 205 206 /* Enable all supported events */ 207 rv = thinkpad_mask_init(sc, val); 208 if (ACPI_FAILURE(rv)) { 209 aprint_error_dev(self, "couldn't set event mask: %s\n", 210 AcpiFormatException(rv)); 211 goto fail; 212 } 213 214 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler); 215 216 /* 217 * Obtain a handle for CMOS commands. This is used by T61. 218 */ 219 (void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl); 220 221 /* 222 * Obtain a handle to the power resource available on many models. 223 * Since pmf(9) is not yet integrated with the ACPI power resource 224 * code, this must be turned on manually upon resume. Otherwise the 225 * system may, for instance, resume from S3 with usb(4) powered down. 226 */ 227 (void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl); 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 int i; 279 280 acpi_deregister_notify(sc->sc_node); 281 282 for (i = 0; i < TP_PSW_LAST; i++) 283 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 284 285 if (sc->sc_sme != NULL) 286 sysmon_envsys_unregister(sc->sc_sme); 287 288 pmf_device_deregister(self); 289 290 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 291 thinkpad_brightness_up, true); 292 293 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 294 thinkpad_brightness_down, true); 295 296 return 0; 297 } 298 299 static void 300 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 301 { 302 device_t self = opaque; 303 thinkpad_softc_t *sc; 304 305 sc = device_private(self); 306 307 if (notify != 0x80) { 308 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 309 return; 310 } 311 312 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 313 } 314 315 static void 316 thinkpad_get_hotkeys(void *opaque) 317 { 318 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 319 device_t self = sc->sc_dev; 320 ACPI_STATUS rv; 321 ACPI_INTEGER val; 322 int type, event; 323 324 for (;;) { 325 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 326 if (ACPI_FAILURE(rv)) { 327 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 328 AcpiFormatException(rv)); 329 return; 330 } 331 332 if (val == 0) 333 return; 334 335 type = (val & 0xf000) >> 12; 336 event = val & 0x0fff; 337 338 if (type != 1) 339 /* Only type 1 events are supported for now */ 340 continue; 341 342 switch (event) { 343 case THINKPAD_NOTIFY_BrightnessUp: 344 thinkpad_brightness_up(self); 345 break; 346 case THINKPAD_NOTIFY_BrightnessDown: 347 thinkpad_brightness_down(self); 348 break; 349 case THINKPAD_NOTIFY_WirelessSwitch: 350 thinkpad_wireless_toggle(sc); 351 break; 352 case THINKPAD_NOTIFY_wWANSwitch: 353 thinkpad_wwan_toggle(sc); 354 break; 355 case THINKPAD_NOTIFY_SleepButton: 356 if (sc->sc_smpsw_valid == false) 357 break; 358 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 359 PSWITCH_EVENT_PRESSED); 360 break; 361 case THINKPAD_NOTIFY_HibernateButton: 362 #if notyet 363 if (sc->sc_smpsw_valid == false) 364 break; 365 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 366 PSWITCH_EVENT_PRESSED); 367 #endif 368 break; 369 case THINKPAD_NOTIFY_DisplayCycle: 370 if (sc->sc_smpsw_valid == false) 371 break; 372 sysmon_pswitch_event( 373 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 374 PSWITCH_EVENT_PRESSED); 375 break; 376 case THINKPAD_NOTIFY_LockScreen: 377 if (sc->sc_smpsw_valid == false) 378 break; 379 sysmon_pswitch_event( 380 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 381 PSWITCH_EVENT_PRESSED); 382 break; 383 case THINKPAD_NOTIFY_BatteryInfo: 384 if (sc->sc_smpsw_valid == false) 385 break; 386 sysmon_pswitch_event( 387 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 388 PSWITCH_EVENT_PRESSED); 389 break; 390 case THINKPAD_NOTIFY_EjectButton: 391 if (sc->sc_smpsw_valid == false) 392 break; 393 sysmon_pswitch_event( 394 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 395 PSWITCH_EVENT_PRESSED); 396 break; 397 case THINKPAD_NOTIFY_Zoom: 398 if (sc->sc_smpsw_valid == false) 399 break; 400 sysmon_pswitch_event( 401 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 402 PSWITCH_EVENT_PRESSED); 403 break; 404 case THINKPAD_NOTIFY_ThinkVantage: 405 if (sc->sc_smpsw_valid == false) 406 break; 407 sysmon_pswitch_event( 408 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 409 PSWITCH_EVENT_PRESSED); 410 break; 411 case THINKPAD_NOTIFY_FnF1: 412 case THINKPAD_NOTIFY_PointerSwitch: 413 case THINKPAD_NOTIFY_FnF10: 414 case THINKPAD_NOTIFY_FnF11: 415 case THINKPAD_NOTIFY_ThinkLight: 416 case THINKPAD_NOTIFY_VolumeUp: 417 case THINKPAD_NOTIFY_VolumeDown: 418 case THINKPAD_NOTIFY_VolumeMute: 419 /* XXXJDM we should deliver hotkeys as keycodes */ 420 break; 421 default: 422 aprint_debug_dev(self, "notify event 0x%03x\n", event); 423 break; 424 } 425 } 426 } 427 428 static ACPI_STATUS 429 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 430 { 431 ACPI_OBJECT param[2]; 432 ACPI_OBJECT_LIST params; 433 ACPI_STATUS rv; 434 int i; 435 436 /* Update hotkey mask */ 437 params.Count = 2; 438 params.Pointer = param; 439 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 440 441 for (i = 0; i < 32; i++) { 442 param[0].Integer.Value = i + 1; 443 param[1].Integer.Value = (((1 << i) & mask) != 0); 444 445 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 446 ¶ms, NULL); 447 if (ACPI_FAILURE(rv)) 448 return rv; 449 } 450 451 /* Enable hotkey events */ 452 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 453 if (ACPI_FAILURE(rv)) { 454 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 455 AcpiFormatException(rv)); 456 return rv; 457 } 458 459 /* Claim ownership of brightness control */ 460 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 461 462 return AE_OK; 463 } 464 465 static void 466 thinkpad_sensors_init(thinkpad_softc_t *sc) 467 { 468 int i, j; 469 470 if (sc->sc_ecdev == NULL) 471 return; /* no chance of this working */ 472 473 sc->sc_sme = sysmon_envsys_create(); 474 475 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 476 477 sc->sc_sensor[i].units = ENVSYS_STEMP; 478 sc->sc_sensor[i].state = ENVSYS_SINVALID; 479 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 480 481 (void)snprintf(sc->sc_sensor[i].desc, 482 sizeof(sc->sc_sensor[i].desc), "temperature %d", i); 483 484 if (sysmon_envsys_sensor_attach(sc->sc_sme, 485 &sc->sc_sensor[i]) != 0) 486 goto fail; 487 } 488 489 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 490 491 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 492 sc->sc_sensor[i].state = ENVSYS_SINVALID; 493 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 494 495 (void)snprintf(sc->sc_sensor[i].desc, 496 sizeof(sc->sc_sensor[i].desc), "fan speed %d", j); 497 498 if (sysmon_envsys_sensor_attach(sc->sc_sme, 499 &sc->sc_sensor[i]) != 0) 500 goto fail; 501 } 502 503 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 504 sc->sc_sme->sme_cookie = sc; 505 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 506 507 if (sysmon_envsys_register(sc->sc_sme) != 0) 508 goto fail; 509 510 return; 511 512 fail: 513 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 514 sysmon_envsys_destroy(sc->sc_sme); 515 sc->sc_sme = NULL; 516 } 517 518 static void 519 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 520 { 521 switch (edata->units) { 522 case ENVSYS_STEMP: 523 thinkpad_temp_refresh(sme, edata); 524 break; 525 case ENVSYS_SFANRPM: 526 thinkpad_fan_refresh(sme, edata); 527 break; 528 default: 529 break; 530 } 531 } 532 533 static void 534 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 535 { 536 thinkpad_softc_t *sc = sme->sme_cookie; 537 char sname[5] = "TMP?"; 538 ACPI_INTEGER val; 539 ACPI_STATUS rv; 540 int temp; 541 542 sname[3] = '0' + edata->sensor; 543 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 544 if (ACPI_FAILURE(rv)) { 545 edata->state = ENVSYS_SINVALID; 546 return; 547 } 548 temp = (int)val; 549 if (temp > 127 || temp < -127) { 550 edata->state = ENVSYS_SINVALID; 551 return; 552 } 553 554 edata->value_cur = temp * 1000000 + 273150000; 555 edata->state = ENVSYS_SVALID; 556 } 557 558 static void 559 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 560 { 561 thinkpad_softc_t *sc = sme->sme_cookie; 562 ACPI_INTEGER lo; 563 ACPI_INTEGER hi; 564 ACPI_STATUS rv; 565 int rpm; 566 567 /* 568 * Read the low byte first to avoid a firmware bug. 569 */ 570 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 571 if (ACPI_FAILURE(rv)) { 572 edata->state = ENVSYS_SINVALID; 573 return; 574 } 575 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 576 if (ACPI_FAILURE(rv)) { 577 edata->state = ENVSYS_SINVALID; 578 return; 579 } 580 rpm = ((((int)hi) << 8) | ((int)lo)); 581 if (rpm < 0) { 582 edata->state = ENVSYS_SINVALID; 583 return; 584 } 585 586 edata->value_cur = rpm; 587 edata->state = ENVSYS_SVALID; 588 } 589 590 static void 591 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 592 { 593 /* Ignore return value, as the hardware may not support bluetooth */ 594 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 595 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, NULL); 596 } 597 598 static void 599 thinkpad_wwan_toggle(thinkpad_softc_t *sc) 600 { 601 /* Ignore return value, as the hardware may not support wireless WAN */ 602 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "WTGL", NULL, NULL); 603 } 604 605 static uint8_t 606 thinkpad_brightness_read(thinkpad_softc_t *sc) 607 { 608 uint32_t val = 0; 609 610 AcpiOsWritePort(IO_RTC, 0x6c, 8); 611 AcpiOsReadPort(IO_RTC + 1, &val, 8); 612 613 return val & 7; 614 } 615 616 static void 617 thinkpad_brightness_up(device_t self) 618 { 619 thinkpad_softc_t *sc = device_private(self); 620 621 if (thinkpad_brightness_read(sc) == 7) 622 return; 623 624 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 625 } 626 627 static void 628 thinkpad_brightness_down(device_t self) 629 { 630 thinkpad_softc_t *sc = device_private(self); 631 632 if (thinkpad_brightness_read(sc) == 0) 633 return; 634 635 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 636 } 637 638 static void 639 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 640 { 641 ACPI_STATUS rv; 642 643 if (sc->sc_cmoshdl == NULL) 644 return; 645 646 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 647 648 if (ACPI_FAILURE(rv)) 649 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n", 650 AcpiFormatException(rv)); 651 } 652 653 static bool 654 thinkpad_resume(device_t dv, const pmf_qual_t *qual) 655 { 656 thinkpad_softc_t *sc = device_private(dv); 657 658 if (sc->sc_powhdl == NULL) 659 return true; 660 661 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true); 662 663 return true; 664 } 665 666 MODULE(MODULE_CLASS_DRIVER, thinkpad, NULL); 667 668 #ifdef _MODULE 669 #include "ioconf.c" 670 #endif 671 672 static int 673 thinkpad_modcmd(modcmd_t cmd, void *aux) 674 { 675 int rv = 0; 676 677 switch (cmd) { 678 679 case MODULE_CMD_INIT: 680 681 #ifdef _MODULE 682 rv = config_init_component(cfdriver_ioconf_thinkpad, 683 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 684 #endif 685 break; 686 687 case MODULE_CMD_FINI: 688 689 #ifdef _MODULE 690 rv = config_fini_component(cfdriver_ioconf_thinkpad, 691 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 692 #endif 693 break; 694 695 default: 696 rv = ENOTTY; 697 } 698 699 return rv; 700 } 701