1 /* $NetBSD: thinkpad_acpi.c,v 1.44 2013/03/30 19:05:20 christos 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.44 2013/03/30 19:05:20 christos 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 /* FnF4 */ 59 #define TP_PSW_HIBERNATE 1 /* FnF12 */ 60 #define TP_PSW_DISPLAY_CYCLE 2 /* FnF7 */ 61 #define TP_PSW_LOCK_SCREEN 3 /* FnF2 */ 62 #define TP_PSW_BATTERY_INFO 4 /* FnF3 */ 63 #define TP_PSW_EJECT_BUTTON 5 /* FnF9 */ 64 #define TP_PSW_ZOOM_BUTTON 6 /* FnSPACE */ 65 #define TP_PSW_VENDOR_BUTTON 7 /* ThinkVantage */ 66 #define TP_PSW_FNF1_BUTTON 8 /* FnF1 */ 67 #define TP_PSW_WIRELESS_BUTTON 9 /* FnF5 */ 68 #define TP_PSW_WWAN_BUTTON 10 /* FnF6 */ 69 #define TP_PSW_POINTER_BUTTON 11 /* FnF8 */ 70 #define TP_PSW_FNF10_BUTTON 12 /* FnF10 */ 71 #define TP_PSW_FNF11_BUTTON 13 /* FnF11 */ 72 #define TP_PSW_BRIGHTNESS_UP 14 73 #define TP_PSW_BRIGHTNESS_DOWN 15 74 #define TP_PSW_THINKLIGHT 16 75 #define TP_PSW_VOLUME_UP 17 76 #define TP_PSW_VOLUME_DOWN 18 77 #define TP_PSW_VOLUME_MUTE 19 78 #define TP_PSW_LAST 20 79 80 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 81 bool sc_smpsw_valid; 82 83 struct sysmon_envsys *sc_sme; 84 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 85 86 int sc_display_state; 87 } thinkpad_softc_t; 88 89 /* Hotkey events */ 90 #define THINKPAD_NOTIFY_FnF1 0x001 91 #define THINKPAD_NOTIFY_LockScreen 0x002 92 #define THINKPAD_NOTIFY_BatteryInfo 0x003 93 #define THINKPAD_NOTIFY_SleepButton 0x004 94 #define THINKPAD_NOTIFY_WirelessSwitch 0x005 95 #define THINKPAD_NOTIFY_wWANSwitch 0x006 96 #define THINKPAD_NOTIFY_DisplayCycle 0x007 97 #define THINKPAD_NOTIFY_PointerSwitch 0x008 98 #define THINKPAD_NOTIFY_EjectButton 0x009 99 #define THINKPAD_NOTIFY_FnF10 0x00a /* XXX: Not seen on T61 */ 100 #define THINKPAD_NOTIFY_FnF11 0x00b 101 #define THINKPAD_NOTIFY_HibernateButton 0x00c 102 #define THINKPAD_NOTIFY_BrightnessUp 0x010 103 #define THINKPAD_NOTIFY_BrightnessDown 0x011 104 #define THINKPAD_NOTIFY_ThinkLight 0x012 105 #define THINKPAD_NOTIFY_Zoom 0x014 106 #define THINKPAD_NOTIFY_VolumeUp 0x015 /* XXX: Not seen on T61 */ 107 #define THINKPAD_NOTIFY_VolumeDown 0x016 /* XXX: Not seen on T61 */ 108 #define THINKPAD_NOTIFY_VolumeMute 0x017 /* XXX: Not seen on T61 */ 109 #define THINKPAD_NOTIFY_ThinkVantage 0x018 110 111 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 112 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 113 114 #define THINKPAD_HKEY_VERSION 0x0100 115 116 #define THINKPAD_DISPLAY_LCD 0x01 117 #define THINKPAD_DISPLAY_CRT 0x02 118 #define THINKPAD_DISPLAY_DVI 0x08 119 #define THINKPAD_DISPLAY_ALL \ 120 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 121 122 static int thinkpad_match(device_t, cfdata_t, void *); 123 static void thinkpad_attach(device_t, device_t, void *); 124 static int thinkpad_detach(device_t, int); 125 126 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t); 127 static void thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *); 128 static void thinkpad_get_hotkeys(void *); 129 130 static void thinkpad_sensors_init(thinkpad_softc_t *); 131 static void thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); 132 static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *); 133 static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *); 134 135 static void thinkpad_wireless_toggle(thinkpad_softc_t *); 136 static void thinkpad_wwan_toggle(thinkpad_softc_t *); 137 138 static bool thinkpad_resume(device_t, const pmf_qual_t *); 139 static void thinkpad_brightness_up(device_t); 140 static void thinkpad_brightness_down(device_t); 141 static uint8_t thinkpad_brightness_read(thinkpad_softc_t *sc); 142 static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 143 144 CFATTACH_DECL_NEW(thinkpad, sizeof(thinkpad_softc_t), 145 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL); 146 147 static const char * const thinkpad_ids[] = { 148 "IBM0068", 149 "LEN0068", 150 NULL 151 }; 152 153 static int 154 thinkpad_match(device_t parent, cfdata_t match, void *opaque) 155 { 156 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 157 ACPI_INTEGER ver; 158 159 if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE) 160 return 0; 161 162 if (!acpi_match_hid(aa->aa_node->ad_devinfo, thinkpad_ids)) 163 return 0; 164 165 /* We only support hotkey version 0x0100 */ 166 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", 167 &ver))) 168 return 0; 169 170 if (ver != THINKPAD_HKEY_VERSION) 171 return 0; 172 173 /* Cool, looks like we're good to go */ 174 return 1; 175 } 176 177 static void 178 thinkpad_attach(device_t parent, device_t self, void *opaque) 179 { 180 thinkpad_softc_t *sc = device_private(self); 181 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 182 struct sysmon_pswitch *psw; 183 device_t curdev; 184 deviter_t di; 185 ACPI_STATUS rv; 186 ACPI_INTEGER val; 187 int i; 188 189 sc->sc_dev = self; 190 sc->sc_powhdl = NULL; 191 sc->sc_cmoshdl = NULL; 192 sc->sc_node = aa->aa_node; 193 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 194 195 aprint_naive("\n"); 196 aprint_normal("\n"); 197 198 sc->sc_ecdev = NULL; 199 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 200 curdev != NULL; curdev = deviter_next(&di)) 201 if (device_is_a(curdev, "acpiecdt") || 202 device_is_a(curdev, "acpiec")) { 203 sc->sc_ecdev = curdev; 204 break; 205 } 206 deviter_release(&di); 207 208 if (sc->sc_ecdev) 209 aprint_debug_dev(self, "using EC at %s\n", 210 device_xname(sc->sc_ecdev)); 211 212 /* Get the supported event mask */ 213 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 214 if (ACPI_FAILURE(rv)) { 215 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 216 AcpiFormatException(rv)); 217 goto fail; 218 } 219 220 /* Enable all supported events */ 221 rv = thinkpad_mask_init(sc, val); 222 if (ACPI_FAILURE(rv)) { 223 aprint_error_dev(self, "couldn't set event mask: %s\n", 224 AcpiFormatException(rv)); 225 goto fail; 226 } 227 228 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler); 229 230 /* 231 * Obtain a handle for CMOS commands. This is used by T61. 232 */ 233 (void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl); 234 235 /* 236 * Obtain a handle to the power resource available on many models. 237 * Since pmf(9) is not yet integrated with the ACPI power resource 238 * code, this must be turned on manually upon resume. Otherwise the 239 * system may, for instance, resume from S3 with usb(4) powered down. 240 */ 241 (void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl); 242 243 /* Register power switches with sysmon */ 244 psw = sc->sc_smpsw; 245 sc->sc_smpsw_valid = true; 246 247 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 248 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 249 #if notyet 250 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 251 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 252 #endif 253 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 254 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 255 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 256 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 257 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 258 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 259 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 260 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 261 #ifndef THINKPAD_NORMAL_HOTKEYS 262 psw[TP_PSW_FNF1_BUTTON].smpsw_name = PSWITCH_HK_FNF1_BUTTON; 263 psw[TP_PSW_WIRELESS_BUTTON].smpsw_name = PSWITCH_HK_WIRELESS_BUTTON; 264 psw[TP_PSW_WWAN_BUTTON].smpsw_name = PSWITCH_HK_WWAN_BUTTON; 265 psw[TP_PSW_POINTER_BUTTON].smpsw_name = PSWITCH_HK_POINTER_BUTTON; 266 psw[TP_PSW_FNF10_BUTTON].smpsw_name = PSWITCH_HK_FNF10_BUTTON; 267 psw[TP_PSW_FNF11_BUTTON].smpsw_name = PSWITCH_HK_FNF11_BUTTON; 268 psw[TP_PSW_BRIGHTNESS_UP].smpsw_name = PSWITCH_HK_BRIGHTNESS_UP; 269 psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name = PSWITCH_HK_BRIGHTNESS_DOWN; 270 psw[TP_PSW_THINKLIGHT].smpsw_name = PSWITCH_HK_THINKLIGHT; 271 psw[TP_PSW_VOLUME_UP].smpsw_name = PSWITCH_HK_VOLUME_UP; 272 psw[TP_PSW_VOLUME_DOWN].smpsw_name = PSWITCH_HK_VOLUME_DOWN; 273 psw[TP_PSW_VOLUME_MUTE].smpsw_name = PSWITCH_HK_VOLUME_MUTE; 274 #endif /* THINKPAD_NORMAL_HOTKEYS */ 275 276 for (i = 0; i < TP_PSW_LAST; i++) { 277 /* not supported yet */ 278 if (i == TP_PSW_HIBERNATE) 279 continue; 280 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 281 aprint_error_dev(self, 282 "couldn't register with sysmon\n"); 283 sc->sc_smpsw_valid = false; 284 break; 285 } 286 } 287 288 /* Register temperature and fan sensors with envsys */ 289 thinkpad_sensors_init(sc); 290 291 fail: 292 if (!pmf_device_register(self, NULL, thinkpad_resume)) 293 aprint_error_dev(self, "couldn't establish power handler\n"); 294 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 295 thinkpad_brightness_up, true)) 296 aprint_error_dev(self, "couldn't register event handler\n"); 297 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 298 thinkpad_brightness_down, true)) 299 aprint_error_dev(self, "couldn't register event handler\n"); 300 } 301 302 static int 303 thinkpad_detach(device_t self, int flags) 304 { 305 struct thinkpad_softc *sc = device_private(self); 306 int i; 307 308 acpi_deregister_notify(sc->sc_node); 309 310 for (i = 0; i < TP_PSW_LAST; i++) 311 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 312 313 if (sc->sc_sme != NULL) 314 sysmon_envsys_unregister(sc->sc_sme); 315 316 pmf_device_deregister(self); 317 318 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 319 thinkpad_brightness_up, true); 320 321 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 322 thinkpad_brightness_down, true); 323 324 return 0; 325 } 326 327 static void 328 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 329 { 330 device_t self = opaque; 331 thinkpad_softc_t *sc; 332 333 sc = device_private(self); 334 335 if (notify != 0x80) { 336 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 337 return; 338 } 339 340 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 341 } 342 343 static void 344 thinkpad_get_hotkeys(void *opaque) 345 { 346 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 347 device_t self = sc->sc_dev; 348 ACPI_STATUS rv; 349 ACPI_INTEGER val; 350 int type, event; 351 352 for (;;) { 353 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 354 if (ACPI_FAILURE(rv)) { 355 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 356 AcpiFormatException(rv)); 357 return; 358 } 359 360 if (val == 0) 361 return; 362 363 type = (val & 0xf000) >> 12; 364 event = val & 0x0fff; 365 366 if (type != 1) 367 /* Only type 1 events are supported for now */ 368 continue; 369 370 switch (event) { 371 case THINKPAD_NOTIFY_BrightnessUp: 372 thinkpad_brightness_up(self); 373 #ifndef THINKPAD_NORMAL_HOTKEYS 374 if (sc->sc_smpsw_valid == false) 375 break; 376 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP], 377 PSWITCH_EVENT_PRESSED); 378 #endif 379 break; 380 case THINKPAD_NOTIFY_BrightnessDown: 381 thinkpad_brightness_down(self); 382 #ifndef THINKPAD_NORMAL_HOTKEYS 383 if (sc->sc_smpsw_valid == false) 384 break; 385 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN], 386 PSWITCH_EVENT_PRESSED); 387 #endif 388 break; 389 case THINKPAD_NOTIFY_WirelessSwitch: 390 thinkpad_wireless_toggle(sc); 391 #ifndef THINKPAD_NORMAL_HOTKEYS 392 if (sc->sc_smpsw_valid == false) 393 break; 394 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON], 395 PSWITCH_EVENT_PRESSED); 396 #endif 397 break; 398 case THINKPAD_NOTIFY_wWANSwitch: 399 thinkpad_wwan_toggle(sc); 400 #ifndef THINKPAD_NORMAL_HOTKEYS 401 if (sc->sc_smpsw_valid == false) 402 break; 403 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON], 404 PSWITCH_EVENT_PRESSED); 405 #endif 406 break; 407 case THINKPAD_NOTIFY_SleepButton: 408 if (sc->sc_smpsw_valid == false) 409 break; 410 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 411 PSWITCH_EVENT_PRESSED); 412 break; 413 case THINKPAD_NOTIFY_HibernateButton: 414 #if notyet 415 if (sc->sc_smpsw_valid == false) 416 break; 417 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 418 PSWITCH_EVENT_PRESSED); 419 #endif 420 break; 421 case THINKPAD_NOTIFY_DisplayCycle: 422 if (sc->sc_smpsw_valid == false) 423 break; 424 sysmon_pswitch_event( 425 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 426 PSWITCH_EVENT_PRESSED); 427 break; 428 case THINKPAD_NOTIFY_LockScreen: 429 if (sc->sc_smpsw_valid == false) 430 break; 431 sysmon_pswitch_event( 432 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 433 PSWITCH_EVENT_PRESSED); 434 break; 435 case THINKPAD_NOTIFY_BatteryInfo: 436 if (sc->sc_smpsw_valid == false) 437 break; 438 sysmon_pswitch_event( 439 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 440 PSWITCH_EVENT_PRESSED); 441 break; 442 case THINKPAD_NOTIFY_EjectButton: 443 if (sc->sc_smpsw_valid == false) 444 break; 445 sysmon_pswitch_event( 446 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 447 PSWITCH_EVENT_PRESSED); 448 break; 449 case THINKPAD_NOTIFY_Zoom: 450 if (sc->sc_smpsw_valid == false) 451 break; 452 sysmon_pswitch_event( 453 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 454 PSWITCH_EVENT_PRESSED); 455 break; 456 case THINKPAD_NOTIFY_ThinkVantage: 457 if (sc->sc_smpsw_valid == false) 458 break; 459 sysmon_pswitch_event( 460 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 461 PSWITCH_EVENT_PRESSED); 462 break; 463 #ifndef THINKPAD_NORMAL_HOTKEYS 464 case THINKPAD_NOTIFY_FnF1: 465 if (sc->sc_smpsw_valid == false) 466 break; 467 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON], 468 PSWITCH_EVENT_PRESSED); 469 break; 470 case THINKPAD_NOTIFY_PointerSwitch: 471 if (sc->sc_smpsw_valid == false) 472 break; 473 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON], 474 PSWITCH_EVENT_PRESSED); 475 break; 476 case THINKPAD_NOTIFY_FnF11: 477 if (sc->sc_smpsw_valid == false) 478 break; 479 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON], 480 PSWITCH_EVENT_PRESSED); 481 break; 482 case THINKPAD_NOTIFY_ThinkLight: 483 if (sc->sc_smpsw_valid == false) 484 break; 485 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT], 486 PSWITCH_EVENT_PRESSED); 487 break; 488 /* 489 * For some reason the next four aren't seen on my T61. 490 */ 491 case THINKPAD_NOTIFY_FnF10: 492 if (sc->sc_smpsw_valid == false) 493 break; 494 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON], 495 PSWITCH_EVENT_PRESSED); 496 break; 497 case THINKPAD_NOTIFY_VolumeUp: 498 if (sc->sc_smpsw_valid == false) 499 break; 500 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP], 501 PSWITCH_EVENT_PRESSED); 502 break; 503 case THINKPAD_NOTIFY_VolumeDown: 504 if (sc->sc_smpsw_valid == false) 505 break; 506 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN], 507 PSWITCH_EVENT_PRESSED); 508 break; 509 case THINKPAD_NOTIFY_VolumeMute: 510 if (sc->sc_smpsw_valid == false) 511 break; 512 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE], 513 PSWITCH_EVENT_PRESSED); 514 break; 515 #else 516 case THINKPAD_NOTIFY_FnF1: 517 case THINKPAD_NOTIFY_PointerSwitch: 518 case THINKPAD_NOTIFY_FnF10: 519 case THINKPAD_NOTIFY_FnF11: 520 case THINKPAD_NOTIFY_ThinkLight: 521 case THINKPAD_NOTIFY_VolumeUp: 522 case THINKPAD_NOTIFY_VolumeDown: 523 case THINKPAD_NOTIFY_VolumeMute: 524 /* XXXJDM we should deliver hotkeys as keycodes */ 525 break; 526 #endif /* THINKPAD_NORMAL_HOTKEYS */ 527 default: 528 aprint_debug_dev(self, "notify event 0x%03x\n", event); 529 break; 530 } 531 } 532 } 533 534 static ACPI_STATUS 535 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 536 { 537 ACPI_OBJECT param[2]; 538 ACPI_OBJECT_LIST params; 539 ACPI_STATUS rv; 540 int i; 541 542 /* Update hotkey mask */ 543 params.Count = 2; 544 params.Pointer = param; 545 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 546 547 for (i = 0; i < 32; i++) { 548 param[0].Integer.Value = i + 1; 549 param[1].Integer.Value = (((1 << i) & mask) != 0); 550 551 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 552 ¶ms, NULL); 553 if (ACPI_FAILURE(rv)) 554 return rv; 555 } 556 557 /* Enable hotkey events */ 558 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 559 if (ACPI_FAILURE(rv)) { 560 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 561 AcpiFormatException(rv)); 562 return rv; 563 } 564 565 /* Claim ownership of brightness control */ 566 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 567 568 return AE_OK; 569 } 570 571 static void 572 thinkpad_sensors_init(thinkpad_softc_t *sc) 573 { 574 int i, j; 575 576 if (sc->sc_ecdev == NULL) 577 return; /* no chance of this working */ 578 579 sc->sc_sme = sysmon_envsys_create(); 580 581 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 582 583 sc->sc_sensor[i].units = ENVSYS_STEMP; 584 sc->sc_sensor[i].state = ENVSYS_SINVALID; 585 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 586 587 (void)snprintf(sc->sc_sensor[i].desc, 588 sizeof(sc->sc_sensor[i].desc), "temperature %d", i); 589 590 if (sysmon_envsys_sensor_attach(sc->sc_sme, 591 &sc->sc_sensor[i]) != 0) 592 goto fail; 593 } 594 595 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 596 597 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 598 sc->sc_sensor[i].state = ENVSYS_SINVALID; 599 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 600 601 (void)snprintf(sc->sc_sensor[i].desc, 602 sizeof(sc->sc_sensor[i].desc), "fan speed %d", j); 603 604 if (sysmon_envsys_sensor_attach(sc->sc_sme, 605 &sc->sc_sensor[i]) != 0) 606 goto fail; 607 } 608 609 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 610 sc->sc_sme->sme_cookie = sc; 611 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 612 613 if (sysmon_envsys_register(sc->sc_sme) != 0) 614 goto fail; 615 616 return; 617 618 fail: 619 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 620 sysmon_envsys_destroy(sc->sc_sme); 621 sc->sc_sme = NULL; 622 } 623 624 static void 625 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 626 { 627 switch (edata->units) { 628 case ENVSYS_STEMP: 629 thinkpad_temp_refresh(sme, edata); 630 break; 631 case ENVSYS_SFANRPM: 632 thinkpad_fan_refresh(sme, edata); 633 break; 634 default: 635 break; 636 } 637 } 638 639 static void 640 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 641 { 642 thinkpad_softc_t *sc = sme->sme_cookie; 643 char sname[5] = "TMP?"; 644 ACPI_INTEGER val; 645 ACPI_STATUS rv; 646 int temp; 647 648 sname[3] = '0' + edata->sensor; 649 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 650 if (ACPI_FAILURE(rv)) { 651 edata->state = ENVSYS_SINVALID; 652 return; 653 } 654 temp = (int)val; 655 if (temp > 127 || temp < -127) { 656 edata->state = ENVSYS_SINVALID; 657 return; 658 } 659 660 edata->value_cur = temp * 1000000 + 273150000; 661 edata->state = ENVSYS_SVALID; 662 } 663 664 static void 665 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 666 { 667 thinkpad_softc_t *sc = sme->sme_cookie; 668 ACPI_INTEGER lo; 669 ACPI_INTEGER hi; 670 ACPI_STATUS rv; 671 int rpm; 672 673 /* 674 * Read the low byte first to avoid a firmware bug. 675 */ 676 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 677 if (ACPI_FAILURE(rv)) { 678 edata->state = ENVSYS_SINVALID; 679 return; 680 } 681 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 682 if (ACPI_FAILURE(rv)) { 683 edata->state = ENVSYS_SINVALID; 684 return; 685 } 686 rpm = ((((int)hi) << 8) | ((int)lo)); 687 if (rpm < 0) { 688 edata->state = ENVSYS_SINVALID; 689 return; 690 } 691 692 edata->value_cur = rpm; 693 edata->state = ENVSYS_SVALID; 694 } 695 696 static void 697 thinkpad_wireless_toggle(thinkpad_softc_t *sc) 698 { 699 /* Ignore return value, as the hardware may not support bluetooth */ 700 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 701 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, NULL); 702 } 703 704 static void 705 thinkpad_wwan_toggle(thinkpad_softc_t *sc) 706 { 707 /* Ignore return value, as the hardware may not support wireless WAN */ 708 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "WTGL", NULL, NULL); 709 } 710 711 static uint8_t 712 thinkpad_brightness_read(thinkpad_softc_t *sc) 713 { 714 uint32_t val = 0; 715 716 AcpiOsWritePort(IO_RTC, 0x6c, 8); 717 AcpiOsReadPort(IO_RTC + 1, &val, 8); 718 719 return val & 7; 720 } 721 722 static void 723 thinkpad_brightness_up(device_t self) 724 { 725 thinkpad_softc_t *sc = device_private(self); 726 727 if (thinkpad_brightness_read(sc) == 7) 728 return; 729 730 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 731 } 732 733 static void 734 thinkpad_brightness_down(device_t self) 735 { 736 thinkpad_softc_t *sc = device_private(self); 737 738 if (thinkpad_brightness_read(sc) == 0) 739 return; 740 741 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 742 } 743 744 static void 745 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 746 { 747 ACPI_STATUS rv; 748 749 if (sc->sc_cmoshdl == NULL) 750 return; 751 752 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 753 754 if (ACPI_FAILURE(rv)) 755 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n", 756 AcpiFormatException(rv)); 757 } 758 759 static bool 760 thinkpad_resume(device_t dv, const pmf_qual_t *qual) 761 { 762 thinkpad_softc_t *sc = device_private(dv); 763 764 if (sc->sc_powhdl == NULL) 765 return true; 766 767 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true); 768 769 return true; 770 } 771 772 MODULE(MODULE_CLASS_DRIVER, thinkpad, NULL); 773 774 #ifdef _MODULE 775 #include "ioconf.c" 776 #endif 777 778 static int 779 thinkpad_modcmd(modcmd_t cmd, void *aux) 780 { 781 int rv = 0; 782 783 switch (cmd) { 784 785 case MODULE_CMD_INIT: 786 787 #ifdef _MODULE 788 rv = config_init_component(cfdriver_ioconf_thinkpad, 789 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 790 #endif 791 break; 792 793 case MODULE_CMD_FINI: 794 795 #ifdef _MODULE 796 rv = config_fini_component(cfdriver_ioconf_thinkpad, 797 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 798 #endif 799 break; 800 801 default: 802 rv = ENOTTY; 803 } 804 805 return rv; 806 } 807