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