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