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