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