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