1/* $NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos Exp $ */ 2 3/*- 4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos 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#include <sys/sysctl.h> 38 39#include <dev/acpi/acpireg.h> 40#include <dev/acpi/acpivar.h> 41#include <dev/acpi/acpi_ecvar.h> 42#include <dev/acpi/acpi_power.h> 43 44#include <dev/isa/isareg.h> 45 46#define _COMPONENT ACPI_RESOURCE_COMPONENT 47ACPI_MODULE_NAME ("thinkpad_acpi") 48 49#define THINKPAD_NTEMPSENSORS 8 50#define THINKPAD_NFANSENSORS 1 51#define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS) 52 53typedef struct tp_sysctl_param { 54 device_t sp_dev; 55 int sp_bat; 56} tp_sysctl_param_t; 57 58typedef union tp_batctl { 59 int have_any; 60 struct { 61 int charge_start:1; 62 int charge_stop:1; 63 int charge_inhibit:1; 64 int force_discharge:1; 65 int individual_control:1; 66 } have; 67} tp_batctl_t; 68 69typedef struct thinkpad_softc { 70 device_t sc_dev; 71 device_t sc_ecdev; 72 struct acpi_devnode *sc_node; 73 struct sysctllog *sc_log; 74 ACPI_HANDLE sc_powhdl; 75 ACPI_HANDLE sc_cmoshdl; 76 ACPI_INTEGER sc_ver; 77 78#define TP_PSW_SLEEP 0 /* FnF4 */ 79#define TP_PSW_HIBERNATE 1 /* FnF12 */ 80#define TP_PSW_DISPLAY_CYCLE 2 /* FnF7 */ 81#define TP_PSW_LOCK_SCREEN 3 /* FnF2 */ 82#define TP_PSW_BATTERY_INFO 4 /* FnF3 */ 83#define TP_PSW_EJECT_BUTTON 5 /* FnF9 */ 84#define TP_PSW_ZOOM_BUTTON 6 /* FnSPACE */ 85#define TP_PSW_VENDOR_BUTTON 7 /* ThinkVantage */ 86#define TP_PSW_FNF1_BUTTON 8 /* FnF1 */ 87#define TP_PSW_WIRELESS_BUTTON 9 /* FnF5 */ 88#define TP_PSW_WWAN_BUTTON 10 /* FnF6 */ 89#define TP_PSW_POINTER_BUTTON 11 /* FnF8 */ 90#define TP_PSW_FNF10_BUTTON 12 /* FnF10 */ 91#define TP_PSW_FNF11_BUTTON 13 /* FnF11 */ 92#define TP_PSW_BRIGHTNESS_UP 14 93#define TP_PSW_BRIGHTNESS_DOWN 15 94#define TP_PSW_THINKLIGHT 16 95#define TP_PSW_VOLUME_UP 17 96#define TP_PSW_VOLUME_DOWN 18 97#define TP_PSW_VOLUME_MUTE 19 98#define TP_PSW_STAR_BUTTON 20 99#define TP_PSW_SCISSORS_BUTTON 21 100#define TP_PSW_BLUETOOTH_BUTTON 22 101#define TP_PSW_KEYBOARD_BUTTON 23 102#define TP_PSW_LAST 24 103 104 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 105 bool sc_smpsw_valid; 106 107 struct sysmon_envsys *sc_sme; 108 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 109 110 int sc_display_state; 111 112#define THINKPAD_BAT_ANY 0 113#define THINKPAD_BAT_PRIMARY 1 114#define THINKPAD_BAT_SECONDARY 2 115#define THINKPAD_BAT_LAST 3 116 117 tp_batctl_t sc_batctl; 118 tp_sysctl_param_t sc_scparam[THINKPAD_BAT_LAST]; 119} thinkpad_softc_t; 120 121/* Hotkey events */ 122#define THINKPAD_NOTIFY_FnF1 0x001 123#define THINKPAD_NOTIFY_LockScreen 0x002 124#define THINKPAD_NOTIFY_BatteryInfo 0x003 125#define THINKPAD_NOTIFY_SleepButton 0x004 126#define THINKPAD_NOTIFY_WirelessSwitch 0x005 127#define THINKPAD_NOTIFY_wWANSwitch 0x006 128#define THINKPAD_NOTIFY_DisplayCycle 0x007 129#define THINKPAD_NOTIFY_PointerSwitch 0x008 130#define THINKPAD_NOTIFY_EjectButton 0x009 131#define THINKPAD_NOTIFY_FnF10 0x00a /* XXX: Not seen on T61 */ 132#define THINKPAD_NOTIFY_FnF11 0x00b 133#define THINKPAD_NOTIFY_HibernateButton 0x00c 134#define THINKPAD_NOTIFY_BrightnessUp 0x010 135#define THINKPAD_NOTIFY_BrightnessDown 0x011 136#define THINKPAD_NOTIFY_ThinkLight 0x012 137#define THINKPAD_NOTIFY_Zoom 0x014 138#define THINKPAD_NOTIFY_VolumeUp 0x015 /* XXX: Not seen on T61 */ 139#define THINKPAD_NOTIFY_VolumeDown 0x016 /* XXX: Not seen on T61 */ 140#define THINKPAD_NOTIFY_VolumeMute 0x017 /* XXX: Not seen on T61 */ 141#define THINKPAD_NOTIFY_ThinkVantage 0x018 142#define THINKPAD_NOTIFY_Star 0x311 143#define THINKPAD_NOTIFY_Scissors 0x312 144#define THINKPAD_NOTIFY_Bluetooth 0x314 145#define THINKPAD_NOTIFY_Keyboard 0x315 146 147#define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 148#define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 149 150#define THINKPAD_HKEY_VERSION_1 0x0100 151#define THINKPAD_HKEY_VERSION_2 0x0200 152 153#define THINKPAD_DISPLAY_LCD 0x01 154#define THINKPAD_DISPLAY_CRT 0x02 155#define THINKPAD_DISPLAY_DVI 0x08 156#define THINKPAD_DISPLAY_ALL \ 157 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 158 159#define THINKPAD_GET_CHARGE_START "BCTG" 160#define THINKPAD_SET_CHARGE_START "BCCS" 161#define THINKPAD_GET_CHARGE_STOP "BCSG" 162#define THINKPAD_SET_CHARGE_STOP "BCSS" 163#define THINKPAD_GET_FORCE_DISCHARGE "BDSG" 164#define THINKPAD_SET_FORCE_DISCHARGE "BDSS" 165#define THINKPAD_GET_CHARGE_INHIBIT "BICG" 166#define THINKPAD_SET_CHARGE_INHIBIT "BICS" 167 168#define THINKPAD_CALL_ERROR 0x80000000 169 170#define THINKPAD_BLUETOOTH_HWPRESENT 0x01 171#define THINKPAD_BLUETOOTH_RADIOSSW 0x02 172#define THINKPAD_BLUETOOTH_RESUMECTRL 0x04 173 174#define THINKPAD_WWAN_HWPRESENT 0x01 175#define THINKPAD_WWAN_RADIOSSW 0x02 176#define THINKPAD_WWAN_RESUMECTRL 0x04 177 178#define THINKPAD_UWB_HWPRESENT 0x01 179#define THINKPAD_UWB_RADIOSSW 0x02 180 181#define THINKPAD_RFK_BLUETOOTH 0 182#define THINKPAD_RFK_WWAN 1 183#define THINKPAD_RFK_UWB 2 184 185static int thinkpad_match(device_t, cfdata_t, void *); 186static void thinkpad_attach(device_t, device_t, void *); 187static int thinkpad_detach(device_t, int); 188 189static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t); 190static void thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *); 191static void thinkpad_get_hotkeys(void *); 192 193static void thinkpad_sensors_init(thinkpad_softc_t *); 194static void thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *); 195static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *); 196static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *); 197 198static void thinkpad_uwb_toggle(thinkpad_softc_t *); 199static void thinkpad_wwan_toggle(thinkpad_softc_t *); 200static void thinkpad_bluetooth_toggle(thinkpad_softc_t *); 201 202static bool thinkpad_resume(device_t, const pmf_qual_t *); 203static void thinkpad_brightness_up(device_t); 204static void thinkpad_brightness_down(device_t); 205static uint8_t thinkpad_brightness_read(thinkpad_softc_t *); 206static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 207 208static void thinkpad_battery_probe_support(device_t); 209static void thinkpad_battery_sysctl_setup(device_t); 210 211CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t), 212 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL, 213 0); 214 215static const struct device_compatible_entry compat_data[] = { 216 { .compat = "IBM0068" }, 217 { .compat = "LEN0068" }, 218 { .compat = "LEN0268" }, 219 DEVICE_COMPAT_EOL 220}; 221 222static int 223thinkpad_match(device_t parent, cfdata_t match, void *opaque) 224{ 225 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 226 ACPI_INTEGER ver; 227 int ret; 228 229 ret = acpi_compatible_match(aa, compat_data); 230 if (ret == 0) 231 return 0; 232 233 /* We only support hotkey versions 0x0100 and 0x0200 */ 234 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", 235 &ver))) 236 return 0; 237 238 switch (ver) { 239 case THINKPAD_HKEY_VERSION_1: 240 case THINKPAD_HKEY_VERSION_2: 241 break; 242 default: 243 return 0; 244 } 245 246 /* Cool, looks like we're good to go */ 247 return ret; 248} 249 250static void 251thinkpad_attach(device_t parent, device_t self, void *opaque) 252{ 253 thinkpad_softc_t *sc = device_private(self); 254 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 255 struct sysmon_pswitch *psw; 256 device_t curdev; 257 deviter_t di; 258 ACPI_STATUS rv; 259 ACPI_INTEGER val; 260 int i; 261 262 sc->sc_dev = self; 263 sc->sc_log = NULL; 264 sc->sc_powhdl = NULL; 265 sc->sc_cmoshdl = NULL; 266 sc->sc_node = aa->aa_node; 267 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 268 269 aprint_naive("\n"); 270 aprint_normal("\n"); 271 272 sc->sc_ecdev = NULL; 273 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 274 curdev != NULL; curdev = deviter_next(&di)) 275 if (device_is_a(curdev, "acpiecdt") || 276 device_is_a(curdev, "acpiec")) { 277 sc->sc_ecdev = curdev; 278 break; 279 } 280 deviter_release(&di); 281 282 if (sc->sc_ecdev) 283 aprint_debug_dev(self, "using EC at %s\n", 284 device_xname(sc->sc_ecdev)); 285 286 /* Query the version number */ 287 rv = acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", &sc->sc_ver); 288 if (ACPI_FAILURE(rv)) { 289 aprint_error_dev(self, "couldn't evaluate MHKV: %s\n", 290 AcpiFormatException(rv)); 291 goto fail; 292 } 293 aprint_normal_dev(self, "version %04x\n", (unsigned)sc->sc_ver); 294 295 /* Get the supported event mask */ 296 switch (sc->sc_ver) { 297 case THINKPAD_HKEY_VERSION_1: 298 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val); 299 if (ACPI_FAILURE(rv)) { 300 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n", 301 AcpiFormatException(rv)); 302 goto fail; 303 } 304 break; 305 case THINKPAD_HKEY_VERSION_2: { 306 ACPI_OBJECT args[1] = { 307 [0] = { .Integer = { 308 .Type = ACPI_TYPE_INTEGER, 309 .Value = 1, /* hotkey events */ 310 } }, 311 }; 312 ACPI_OBJECT_LIST arglist = { 313 .Count = __arraycount(args), 314 .Pointer = args, 315 }; 316 ACPI_OBJECT ret; 317 ACPI_BUFFER buf = { .Pointer = &ret, .Length = sizeof(ret) }; 318 319 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKA", 320 &arglist, &buf); 321 if (ACPI_FAILURE(rv)) { 322 aprint_error_dev(self, "couldn't evaluate MHKA(1):" 323 " %s\n", 324 AcpiFormatException(rv)); 325 goto fail; 326 } 327 if (buf.Length == 0 || ret.Type != ACPI_TYPE_INTEGER) { 328 aprint_error_dev(self, "failed to evaluate MHKA(1)\n"); 329 goto fail; 330 } 331 val = ret.Integer.Value; 332 break; 333 } 334 default: 335 panic("%s: invalid version %jd", device_xname(self), 336 (intmax_t)sc->sc_ver); 337 } 338 339 /* Enable all supported events */ 340 rv = thinkpad_mask_init(sc, val); 341 if (ACPI_FAILURE(rv)) { 342 aprint_error_dev(self, "couldn't set event mask: %s\n", 343 AcpiFormatException(rv)); 344 goto fail; 345 } 346 347 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler); 348 349 /* 350 * Obtain a handle for CMOS commands. This is used by T61. 351 */ 352 (void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl); 353 354 /* 355 * Obtain a handle to the power resource available on many models. 356 * Since pmf(9) is not yet integrated with the ACPI power resource 357 * code, this must be turned on manually upon resume. Otherwise the 358 * system may, for instance, resume from S3 with usb(4) powered down. 359 */ 360 (void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl); 361 362 /* Register power switches with sysmon */ 363 psw = sc->sc_smpsw; 364 sc->sc_smpsw_valid = true; 365 366 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self); 367 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP; 368#if notyet 369 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self); 370 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE; 371#endif 372 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++) 373 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY; 374 375 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE; 376 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN; 377 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO; 378 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON; 379 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON; 380 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON; 381#ifndef THINKPAD_NORMAL_HOTKEYS 382 psw[TP_PSW_FNF1_BUTTON].smpsw_name = PSWITCH_HK_FNF1_BUTTON; 383 psw[TP_PSW_WIRELESS_BUTTON].smpsw_name = PSWITCH_HK_WIRELESS_BUTTON; 384 psw[TP_PSW_WWAN_BUTTON].smpsw_name = PSWITCH_HK_WWAN_BUTTON; 385 psw[TP_PSW_POINTER_BUTTON].smpsw_name = PSWITCH_HK_POINTER_BUTTON; 386 psw[TP_PSW_FNF10_BUTTON].smpsw_name = PSWITCH_HK_FNF10_BUTTON; 387 psw[TP_PSW_FNF11_BUTTON].smpsw_name = PSWITCH_HK_FNF11_BUTTON; 388 psw[TP_PSW_BRIGHTNESS_UP].smpsw_name = PSWITCH_HK_BRIGHTNESS_UP; 389 psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name = PSWITCH_HK_BRIGHTNESS_DOWN; 390 psw[TP_PSW_THINKLIGHT].smpsw_name = PSWITCH_HK_THINKLIGHT; 391 psw[TP_PSW_VOLUME_UP].smpsw_name = PSWITCH_HK_VOLUME_UP; 392 psw[TP_PSW_VOLUME_DOWN].smpsw_name = PSWITCH_HK_VOLUME_DOWN; 393 psw[TP_PSW_VOLUME_MUTE].smpsw_name = PSWITCH_HK_VOLUME_MUTE; 394 psw[TP_PSW_STAR_BUTTON].smpsw_name = PSWITCH_HK_STAR_BUTTON; 395 psw[TP_PSW_SCISSORS_BUTTON].smpsw_name = PSWITCH_HK_SCISSORS_BUTTON; 396 psw[TP_PSW_BLUETOOTH_BUTTON].smpsw_name = PSWITCH_HK_BLUETOOTH_BUTTON; 397 psw[TP_PSW_KEYBOARD_BUTTON].smpsw_name = PSWITCH_HK_KEYBOARD_BUTTON; 398#endif /* THINKPAD_NORMAL_HOTKEYS */ 399 400 for (i = 0; i < TP_PSW_LAST; i++) { 401 /* not supported yet */ 402 if (i == TP_PSW_HIBERNATE) 403 continue; 404 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 405 aprint_error_dev(self, 406 "couldn't register with sysmon\n"); 407 sc->sc_smpsw_valid = false; 408 break; 409 } 410 } 411 412 /* Register temperature and fan sensors with envsys */ 413 thinkpad_sensors_init(sc); 414 415 /* Probe supported battery charge/control operations */ 416 thinkpad_battery_probe_support(self); 417 418 if (sc->sc_batctl.have_any) { 419 for (i = 0; i < THINKPAD_BAT_LAST; i++) { 420 sc->sc_scparam[i].sp_dev = self; 421 sc->sc_scparam[i].sp_bat = i; 422 } 423 thinkpad_battery_sysctl_setup(self); 424 } 425 426fail: 427 if (!pmf_device_register(self, NULL, thinkpad_resume)) 428 aprint_error_dev(self, "couldn't establish power handler\n"); 429 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 430 thinkpad_brightness_up, true)) 431 aprint_error_dev(self, "couldn't register event handler\n"); 432 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 433 thinkpad_brightness_down, true)) 434 aprint_error_dev(self, "couldn't register event handler\n"); 435} 436 437static int 438thinkpad_detach(device_t self, int flags) 439{ 440 struct thinkpad_softc *sc = device_private(self); 441 int i; 442 443 acpi_deregister_notify(sc->sc_node); 444 445 for (i = 0; i < TP_PSW_LAST; i++) 446 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 447 448 if (sc->sc_sme != NULL) 449 sysmon_envsys_unregister(sc->sc_sme); 450 451 if (sc->sc_log != NULL) 452 sysctl_teardown(&sc->sc_log); 453 454 pmf_device_deregister(self); 455 456 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 457 thinkpad_brightness_up, true); 458 459 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 460 thinkpad_brightness_down, true); 461 462 return 0; 463} 464 465static void 466thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 467{ 468 device_t self = opaque; 469 thinkpad_softc_t *sc; 470 471 sc = device_private(self); 472 473 if (notify != 0x80) { 474 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify); 475 return; 476 } 477 478 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc); 479} 480 481SDT_PROBE_DEFINE2(sdt, thinkpad, hotkey, MHKP, 482 "struct thinkpad_softc *"/*sc*/, 483 "ACPI_INTEGER"/*val*/); 484 485static void 486thinkpad_get_hotkeys(void *opaque) 487{ 488 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque; 489 device_t self = sc->sc_dev; 490 ACPI_STATUS rv; 491 ACPI_INTEGER val; 492 int type, event; 493 494 for (;;) { 495 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val); 496 if (ACPI_FAILURE(rv)) { 497 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n", 498 AcpiFormatException(rv)); 499 return; 500 } 501 SDT_PROBE2(sdt, thinkpad, hotkey, MHKP, sc, val); 502 503 if (val == 0) 504 return; 505 506 type = (val & 0xf000) >> 12; 507 event = val & 0x0fff; 508 509 if (type != 1) 510 /* Only type 1 events are supported for now */ 511 continue; 512 513 switch (event) { 514 case THINKPAD_NOTIFY_BrightnessUp: 515 thinkpad_brightness_up(self); 516#ifndef THINKPAD_NORMAL_HOTKEYS 517 if (sc->sc_smpsw_valid == false) 518 break; 519 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP], 520 PSWITCH_EVENT_PRESSED); 521#endif 522 break; 523 case THINKPAD_NOTIFY_BrightnessDown: 524 thinkpad_brightness_down(self); 525#ifndef THINKPAD_NORMAL_HOTKEYS 526 if (sc->sc_smpsw_valid == false) 527 break; 528 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN], 529 PSWITCH_EVENT_PRESSED); 530#endif 531 break; 532 case THINKPAD_NOTIFY_WirelessSwitch: 533 thinkpad_uwb_toggle(sc); 534 thinkpad_wwan_toggle(sc); 535 thinkpad_bluetooth_toggle(sc); 536#ifndef THINKPAD_NORMAL_HOTKEYS 537 if (sc->sc_smpsw_valid == false) 538 break; 539 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON], 540 PSWITCH_EVENT_PRESSED); 541#endif 542 break; 543 case THINKPAD_NOTIFY_Bluetooth: 544 thinkpad_bluetooth_toggle(sc); 545#ifndef THINKPAD_NORMAL_HOTKEYS 546 if (sc->sc_smpsw_valid == false) 547 break; 548 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BLUETOOTH_BUTTON], 549 PSWITCH_EVENT_PRESSED); 550#endif 551 break; 552 case THINKPAD_NOTIFY_wWANSwitch: 553 thinkpad_wwan_toggle(sc); 554#ifndef THINKPAD_NORMAL_HOTKEYS 555 if (sc->sc_smpsw_valid == false) 556 break; 557 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON], 558 PSWITCH_EVENT_PRESSED); 559#endif 560 break; 561 case THINKPAD_NOTIFY_SleepButton: 562 if (sc->sc_smpsw_valid == false) 563 break; 564 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP], 565 PSWITCH_EVENT_PRESSED); 566 break; 567 case THINKPAD_NOTIFY_HibernateButton: 568#if notyet 569 if (sc->sc_smpsw_valid == false) 570 break; 571 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE], 572 PSWITCH_EVENT_PRESSED); 573#endif 574 break; 575 case THINKPAD_NOTIFY_DisplayCycle: 576 if (sc->sc_smpsw_valid == false) 577 break; 578 sysmon_pswitch_event( 579 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE], 580 PSWITCH_EVENT_PRESSED); 581 break; 582 case THINKPAD_NOTIFY_LockScreen: 583 if (sc->sc_smpsw_valid == false) 584 break; 585 sysmon_pswitch_event( 586 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN], 587 PSWITCH_EVENT_PRESSED); 588 break; 589 case THINKPAD_NOTIFY_BatteryInfo: 590 if (sc->sc_smpsw_valid == false) 591 break; 592 sysmon_pswitch_event( 593 &sc->sc_smpsw[TP_PSW_BATTERY_INFO], 594 PSWITCH_EVENT_PRESSED); 595 break; 596 case THINKPAD_NOTIFY_EjectButton: 597 if (sc->sc_smpsw_valid == false) 598 break; 599 sysmon_pswitch_event( 600 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON], 601 PSWITCH_EVENT_PRESSED); 602 break; 603 case THINKPAD_NOTIFY_Zoom: 604 if (sc->sc_smpsw_valid == false) 605 break; 606 sysmon_pswitch_event( 607 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON], 608 PSWITCH_EVENT_PRESSED); 609 break; 610 case THINKPAD_NOTIFY_ThinkVantage: 611 if (sc->sc_smpsw_valid == false) 612 break; 613 sysmon_pswitch_event( 614 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON], 615 PSWITCH_EVENT_PRESSED); 616 break; 617#ifndef THINKPAD_NORMAL_HOTKEYS 618 case THINKPAD_NOTIFY_FnF1: 619 if (sc->sc_smpsw_valid == false) 620 break; 621 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON], 622 PSWITCH_EVENT_PRESSED); 623 break; 624 case THINKPAD_NOTIFY_PointerSwitch: 625 if (sc->sc_smpsw_valid == false) 626 break; 627 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON], 628 PSWITCH_EVENT_PRESSED); 629 break; 630 case THINKPAD_NOTIFY_FnF11: 631 if (sc->sc_smpsw_valid == false) 632 break; 633 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON], 634 PSWITCH_EVENT_PRESSED); 635 break; 636 case THINKPAD_NOTIFY_ThinkLight: 637 if (sc->sc_smpsw_valid == false) 638 break; 639 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT], 640 PSWITCH_EVENT_PRESSED); 641 break; 642 /* 643 * For some reason the next four aren't seen on my T61. 644 */ 645 case THINKPAD_NOTIFY_FnF10: 646 if (sc->sc_smpsw_valid == false) 647 break; 648 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON], 649 PSWITCH_EVENT_PRESSED); 650 break; 651 case THINKPAD_NOTIFY_VolumeUp: 652 if (sc->sc_smpsw_valid == false) 653 break; 654 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP], 655 PSWITCH_EVENT_PRESSED); 656 break; 657 case THINKPAD_NOTIFY_VolumeDown: 658 if (sc->sc_smpsw_valid == false) 659 break; 660 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN], 661 PSWITCH_EVENT_PRESSED); 662 break; 663 case THINKPAD_NOTIFY_VolumeMute: 664 if (sc->sc_smpsw_valid == false) 665 break; 666 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE], 667 PSWITCH_EVENT_PRESSED); 668 break; 669 case THINKPAD_NOTIFY_Star: 670 if (sc->sc_smpsw_valid == false) 671 break; 672 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_STAR_BUTTON], 673 PSWITCH_EVENT_PRESSED); 674 break; 675 case THINKPAD_NOTIFY_Scissors: 676 if (sc->sc_smpsw_valid == false) 677 break; 678 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SCISSORS_BUTTON], 679 PSWITCH_EVENT_PRESSED); 680 break; 681 case THINKPAD_NOTIFY_Keyboard: 682 if (sc->sc_smpsw_valid == false) 683 break; 684 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_KEYBOARD_BUTTON], 685 PSWITCH_EVENT_PRESSED); 686 break; 687#else 688 case THINKPAD_NOTIFY_FnF1: 689 case THINKPAD_NOTIFY_PointerSwitch: 690 case THINKPAD_NOTIFY_FnF10: 691 case THINKPAD_NOTIFY_FnF11: 692 case THINKPAD_NOTIFY_ThinkLight: 693 case THINKPAD_NOTIFY_VolumeUp: 694 case THINKPAD_NOTIFY_VolumeDown: 695 case THINKPAD_NOTIFY_VolumeMute: 696 case THINKPAD_NOTIFY_Star: 697 case THINKPAD_NOTIFY_Scissors: 698 case THINKPAD_NOTIFY_Keyboard: 699 /* XXXJDM we should deliver hotkeys as keycodes */ 700 break; 701#endif /* THINKPAD_NORMAL_HOTKEYS */ 702 default: 703 aprint_debug_dev(self, "notify event 0x%03x\n", event); 704 break; 705 } 706 } 707} 708 709static ACPI_STATUS 710thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask) 711{ 712 ACPI_OBJECT param[2]; 713 ACPI_OBJECT_LIST params; 714 ACPI_STATUS rv; 715 int i; 716 717 /* Update hotkey mask */ 718 params.Count = 2; 719 params.Pointer = param; 720 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER; 721 722 for (i = 0; i < 32; i++) { 723 param[0].Integer.Value = i + 1; 724 param[1].Integer.Value = ((__BIT(i) & mask) != 0); 725 726 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM", 727 ¶ms, NULL); 728 if (ACPI_FAILURE(rv)) 729 return rv; 730 } 731 732 /* Enable hotkey events */ 733 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1); 734 if (ACPI_FAILURE(rv)) { 735 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n", 736 AcpiFormatException(rv)); 737 return rv; 738 } 739 740 /* Claim ownership of brightness control */ 741 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0); 742 743 return AE_OK; 744} 745 746static void 747thinkpad_sensors_init(thinkpad_softc_t *sc) 748{ 749 int i, j; 750 751 if (sc->sc_ecdev == NULL) 752 return; /* no chance of this working */ 753 754 sc->sc_sme = sysmon_envsys_create(); 755 756 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) { 757 758 sc->sc_sensor[i].units = ENVSYS_STEMP; 759 sc->sc_sensor[i].state = ENVSYS_SINVALID; 760 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 761 762 (void)snprintf(sc->sc_sensor[i].desc, 763 sizeof(sc->sc_sensor[i].desc), "temperature %d", i); 764 765 if (sysmon_envsys_sensor_attach(sc->sc_sme, 766 &sc->sc_sensor[i]) != 0) 767 goto fail; 768 } 769 770 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) { 771 772 sc->sc_sensor[i].units = ENVSYS_SFANRPM; 773 sc->sc_sensor[i].state = ENVSYS_SINVALID; 774 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY; 775 776 (void)snprintf(sc->sc_sensor[i].desc, 777 sizeof(sc->sc_sensor[i].desc), "fan speed %d", j); 778 779 if (sysmon_envsys_sensor_attach(sc->sc_sme, 780 &sc->sc_sensor[i]) != 0) 781 goto fail; 782 } 783 784 sc->sc_sme->sme_name = device_xname(sc->sc_dev); 785 sc->sc_sme->sme_cookie = sc; 786 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh; 787 788 if (sysmon_envsys_register(sc->sc_sme) != 0) 789 goto fail; 790 791 return; 792 793fail: 794 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n"); 795 sysmon_envsys_destroy(sc->sc_sme); 796 sc->sc_sme = NULL; 797} 798 799static void 800thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 801{ 802 switch (edata->units) { 803 case ENVSYS_STEMP: 804 thinkpad_temp_refresh(sme, edata); 805 break; 806 case ENVSYS_SFANRPM: 807 thinkpad_fan_refresh(sme, edata); 808 break; 809 default: 810 break; 811 } 812} 813 814static void 815thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 816{ 817 thinkpad_softc_t *sc = sme->sme_cookie; 818 char sname[5] = "TMP?"; 819 ACPI_INTEGER val; 820 ACPI_STATUS rv; 821 int temp; 822 823 sname[3] = '0' + edata->sensor; 824 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val); 825 if (ACPI_FAILURE(rv)) { 826 edata->state = ENVSYS_SINVALID; 827 return; 828 } 829 temp = (int)val; 830 if (temp > 127 || temp < -127) { 831 edata->state = ENVSYS_SINVALID; 832 return; 833 } 834 835 edata->value_cur = temp * 1000000 + 273150000; 836 edata->state = ENVSYS_SVALID; 837} 838 839static void 840thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata) 841{ 842 thinkpad_softc_t *sc = sme->sme_cookie; 843 ACPI_INTEGER lo; 844 ACPI_INTEGER hi; 845 ACPI_STATUS rv; 846 int rpm; 847 848 /* 849 * Read the low byte first to avoid a firmware bug. 850 */ 851 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1); 852 if (ACPI_FAILURE(rv)) { 853 edata->state = ENVSYS_SINVALID; 854 return; 855 } 856 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1); 857 if (ACPI_FAILURE(rv)) { 858 edata->state = ENVSYS_SINVALID; 859 return; 860 } 861 862 rpm = ((((int)hi) << 8) | ((int)lo)); 863 if (rpm < 0) { 864 edata->state = ENVSYS_SINVALID; 865 return; 866 } 867 868 edata->value_cur = rpm; 869 edata->state = ENVSYS_SVALID; 870} 871 872static void 873thinkpad_bluetooth_toggle(thinkpad_softc_t *sc) 874{ 875 ACPI_BUFFER buf; 876 ACPI_OBJECT retobj; 877 ACPI_OBJECT param[1]; 878 ACPI_OBJECT_LIST params; 879 ACPI_STATUS rv; 880 881 /* Ignore return value, as the hardware may not support bluetooth */ 882 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL); 883 if (!ACPI_FAILURE(rv)) 884 return; 885 886 buf.Pointer = &retobj; 887 buf.Length = sizeof(retobj); 888 889 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GBDC", NULL, &buf); 890 if (ACPI_FAILURE(rv)) 891 return; 892 893 params.Count = 1; 894 params.Pointer = param; 895 param[0].Type = ACPI_TYPE_INTEGER; 896 param[0].Integer.Value = 897 (retobj.Integer.Value & THINKPAD_BLUETOOTH_RADIOSSW) == 0 898 ? THINKPAD_BLUETOOTH_RADIOSSW | THINKPAD_BLUETOOTH_RESUMECTRL 899 : 0; 900 901 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SBDC", ¶ms, NULL); 902} 903 904static void 905thinkpad_wwan_toggle(thinkpad_softc_t *sc) 906{ 907 ACPI_BUFFER buf; 908 ACPI_OBJECT retobj; 909 ACPI_OBJECT param[1]; 910 ACPI_OBJECT_LIST params; 911 ACPI_STATUS rv; 912 913 buf.Pointer = &retobj; 914 buf.Length = sizeof(retobj); 915 916 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, &buf); 917 if (ACPI_FAILURE(rv)) 918 return; 919 920 params.Count = 1; 921 params.Pointer = param; 922 param[0].Type = ACPI_TYPE_INTEGER; 923 param[0].Integer.Value = 924 (retobj.Integer.Value & THINKPAD_WWAN_RADIOSSW) == 0 925 ? THINKPAD_WWAN_RADIOSSW | THINKPAD_WWAN_RESUMECTRL 926 : 0; 927 928 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SWAN", ¶ms, NULL); 929} 930 931static void 932thinkpad_uwb_toggle(thinkpad_softc_t *sc) 933{ 934 ACPI_BUFFER buf; 935 ACPI_OBJECT retobj; 936 ACPI_OBJECT param[1]; 937 ACPI_OBJECT_LIST params; 938 ACPI_STATUS rv; 939 940 buf.Pointer = &retobj; 941 buf.Length = sizeof(retobj); 942 943 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GUWB", NULL, &buf); 944 if (ACPI_FAILURE(rv)) 945 return; 946 947 params.Count = 1; 948 params.Pointer = param; 949 param[0].Type = ACPI_TYPE_INTEGER; 950 param[0].Integer.Value = 951 (retobj.Integer.Value & THINKPAD_UWB_RADIOSSW) == 0 952 ? THINKPAD_UWB_RADIOSSW 953 : 0; 954 955 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SUWB", ¶ms, NULL); 956} 957 958static uint8_t 959thinkpad_brightness_read(thinkpad_softc_t *sc) 960{ 961 uint32_t val = 0; 962 963 AcpiOsWritePort(IO_RTC, 0x6c, 8); 964 AcpiOsReadPort(IO_RTC + 1, &val, 8); 965 966 return val & 7; 967} 968 969static void 970thinkpad_brightness_up(device_t self) 971{ 972 thinkpad_softc_t *sc = device_private(self); 973 974 if (thinkpad_brightness_read(sc) == 7) 975 return; 976 977 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP); 978} 979 980static void 981thinkpad_brightness_down(device_t self) 982{ 983 thinkpad_softc_t *sc = device_private(self); 984 985 if (thinkpad_brightness_read(sc) == 0) 986 return; 987 988 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN); 989} 990 991static void 992thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd) 993{ 994 ACPI_STATUS rv; 995 996 if (sc->sc_cmoshdl == NULL) 997 return; 998 999 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 1000 1001 if (ACPI_FAILURE(rv)) 1002 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n", 1003 AcpiFormatException(rv)); 1004} 1005 1006static uint32_t 1007thinkpad_call_method(device_t self, const char *path, uint32_t arg) 1008{ 1009 thinkpad_softc_t *sc = device_private(self); 1010 ACPI_HANDLE handle = sc->sc_node->ad_handle; 1011 ACPI_OBJECT args[1]; 1012 ACPI_OBJECT_LIST arg_list; 1013 ACPI_OBJECT rval; 1014 ACPI_BUFFER buf; 1015 ACPI_STATUS rv; 1016 1017 args[0].Type = ACPI_TYPE_INTEGER; 1018 args[0].Integer.Value = arg; 1019 arg_list.Pointer = &args[0]; 1020 arg_list.Count = __arraycount(args); 1021 1022 memset(&rval, 0, sizeof rval); 1023 buf.Pointer = &rval; 1024 buf.Length = sizeof rval; 1025 1026 rv = AcpiEvaluateObjectTyped(handle, path, &arg_list, &buf, 1027 ACPI_TYPE_INTEGER); 1028 1029 if (ACPI_FAILURE(rv)) { 1030 aprint_error_dev(self, "call %s.%s(%x) failed: %s\n", 1031 acpi_name(handle), path, (unsigned)arg, 1032 AcpiFormatException(rv)); 1033 return THINKPAD_CALL_ERROR; 1034 } 1035 1036 return rval.Integer.Value; 1037} 1038 1039static void 1040thinkpad_battery_probe_support(device_t self) 1041{ 1042 thinkpad_softc_t *sc = device_private(self); 1043 ACPI_HANDLE hdl = sc->sc_node->ad_handle, tmp; 1044 ACPI_STATUS rv; 1045 uint32_t val; 1046 1047 sc->sc_batctl.have_any = 0; 1048 1049 rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_START, &tmp); 1050 if (ACPI_SUCCESS(rv)) { 1051 val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_START, 1052 THINKPAD_BAT_PRIMARY); 1053 if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100)) { 1054 sc->sc_batctl.have.charge_start = 1; 1055 if (val & 0x200) 1056 sc->sc_batctl.have.individual_control = 1; 1057 } 1058 } 1059 1060 rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_STOP, &tmp); 1061 if (ACPI_SUCCESS(rv)) { 1062 val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_STOP, 1063 THINKPAD_BAT_PRIMARY); 1064 if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100)) 1065 sc->sc_batctl.have.charge_stop = 1; 1066 } 1067 1068 rv = AcpiGetHandle(hdl, THINKPAD_GET_FORCE_DISCHARGE, &tmp); 1069 if (ACPI_SUCCESS(rv)) { 1070 val = thinkpad_call_method(self, THINKPAD_GET_FORCE_DISCHARGE, 1071 THINKPAD_BAT_PRIMARY); 1072 if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100)) 1073 sc->sc_batctl.have.force_discharge = 1; 1074 } 1075 1076 rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_INHIBIT, &tmp); 1077 if (ACPI_SUCCESS(rv)) { 1078 val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_INHIBIT, 1079 THINKPAD_BAT_PRIMARY); 1080 if (!(val & THINKPAD_CALL_ERROR) && (val & 0x20)) 1081 sc->sc_batctl.have.charge_inhibit = 1; 1082 } 1083 1084 if (sc->sc_batctl.have_any) 1085 aprint_verbose_dev(self, "battery control capabilities: %x\n", 1086 sc->sc_batctl.have_any); 1087} 1088 1089static int 1090thinkpad_battery_sysctl_charge_start(SYSCTLFN_ARGS) 1091{ 1092 struct sysctlnode node = *rnode; 1093 tp_sysctl_param_t *sp = node.sysctl_data; 1094 int charge_start; 1095 int err; 1096 1097 charge_start = thinkpad_call_method(sp->sp_dev, 1098 THINKPAD_GET_CHARGE_START, sp->sp_bat) & 0xff; 1099 1100 node.sysctl_data = &charge_start; 1101 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 1102 if (err || newp == NULL) 1103 return err; 1104 1105 if (charge_start < 0 || charge_start > 99) 1106 return EINVAL; 1107 1108 if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_START, 1109 charge_start | sp->sp_bat<<8) & THINKPAD_CALL_ERROR) 1110 return EIO; 1111 1112 return 0; 1113} 1114 1115static int 1116thinkpad_battery_sysctl_charge_stop(SYSCTLFN_ARGS) 1117{ 1118 struct sysctlnode node = *rnode; 1119 tp_sysctl_param_t *sp = node.sysctl_data; 1120 int charge_stop; 1121 int err; 1122 1123 charge_stop = thinkpad_call_method(sp->sp_dev, 1124 THINKPAD_GET_CHARGE_STOP, sp->sp_bat) & 0xff; 1125 1126 if (charge_stop == 0) 1127 charge_stop = 100; 1128 1129 node.sysctl_data = &charge_stop; 1130 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 1131 if (err || newp == NULL) 1132 return err; 1133 1134 if (charge_stop < 1 || charge_stop > 100) 1135 return EINVAL; 1136 1137 if (charge_stop == 100) 1138 charge_stop = 0; 1139 1140 if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_STOP, 1141 charge_stop | sp->sp_bat<<8) & THINKPAD_CALL_ERROR) 1142 return EIO; 1143 1144 return 0; 1145} 1146 1147static int 1148thinkpad_battery_sysctl_charge_inhibit(SYSCTLFN_ARGS) 1149{ 1150 struct sysctlnode node = *rnode; 1151 tp_sysctl_param_t *sp = node.sysctl_data; 1152 bool charge_inhibit; 1153 int err; 1154 1155 charge_inhibit = thinkpad_call_method(sp->sp_dev, 1156 THINKPAD_GET_CHARGE_INHIBIT, sp->sp_bat) & 0x01; 1157 1158 node.sysctl_data = &charge_inhibit; 1159 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 1160 if (err || newp == NULL) 1161 return err; 1162 1163 if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_INHIBIT, 1164 charge_inhibit | sp->sp_bat<<4 | 0xffff<<8) & THINKPAD_CALL_ERROR) 1165 return EIO; 1166 1167 return 0; 1168} 1169 1170static int 1171thinkpad_battery_sysctl_force_discharge(SYSCTLFN_ARGS) 1172{ 1173 struct sysctlnode node = *rnode; 1174 tp_sysctl_param_t *sp = node.sysctl_data; 1175 bool force_discharge; 1176 int err; 1177 1178 force_discharge = thinkpad_call_method(sp->sp_dev, 1179 THINKPAD_GET_FORCE_DISCHARGE, sp->sp_bat) & 0x01; 1180 1181 node.sysctl_data = &force_discharge; 1182 err = sysctl_lookup(SYSCTLFN_CALL(&node)); 1183 if (err || newp == NULL) 1184 return err; 1185 1186 if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_FORCE_DISCHARGE, 1187 force_discharge | sp->sp_bat<<8) & THINKPAD_CALL_ERROR) 1188 return EIO; 1189 1190 return 0; 1191} 1192 1193static void 1194thinkpad_battery_sysctl_setup_controls(device_t self, 1195 const struct sysctlnode *rnode, int battery) 1196{ 1197 thinkpad_softc_t *sc = device_private(self); 1198 1199 if (sc->sc_batctl.have.charge_start) 1200 (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, 1201 CTLFLAG_READWRITE, CTLTYPE_INT, "charge_start", 1202 SYSCTL_DESCR("charge start threshold (0-99)"), 1203 thinkpad_battery_sysctl_charge_start, 0, 1204 (void *)&(sc->sc_scparam[battery]), 0, 1205 CTL_CREATE, CTL_EOL); 1206 1207 if (sc->sc_batctl.have.charge_stop) 1208 (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, 1209 CTLFLAG_READWRITE, CTLTYPE_INT, "charge_stop", 1210 SYSCTL_DESCR("charge stop threshold (1-100)"), 1211 thinkpad_battery_sysctl_charge_stop, 0, 1212 (void *)&(sc->sc_scparam[battery]), 0, 1213 CTL_CREATE, CTL_EOL); 1214 1215 if (sc->sc_batctl.have.charge_inhibit) 1216 (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, 1217 CTLFLAG_READWRITE, CTLTYPE_BOOL, "charge_inhibit", 1218 SYSCTL_DESCR("charge inhibit"), 1219 thinkpad_battery_sysctl_charge_inhibit, 0, 1220 (void *)&(sc->sc_scparam[battery]), 0, 1221 CTL_CREATE, CTL_EOL); 1222 1223 if (sc->sc_batctl.have.force_discharge) 1224 (void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL, 1225 CTLFLAG_READWRITE, CTLTYPE_BOOL, "force_discharge", 1226 SYSCTL_DESCR("force discharge"), 1227 thinkpad_battery_sysctl_force_discharge, 0, 1228 (void *)&(sc->sc_scparam[battery]), 0, 1229 CTL_CREATE, CTL_EOL); 1230} 1231 1232static void 1233thinkpad_battery_sysctl_setup(device_t self) 1234{ 1235 thinkpad_softc_t *sc = device_private(self); 1236 const struct sysctlnode *rnode, *cnode; 1237 int err; 1238 1239 err = sysctl_createv(&sc->sc_log, 0, NULL, &rnode, 1240 0, CTLTYPE_NODE, "acpi", NULL, 1241 NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL); 1242 if (err) 1243 goto fail; 1244 1245 err = sysctl_createv(&sc->sc_log, 0, &rnode, &rnode, 1246 0, CTLTYPE_NODE, device_xname(self), 1247 SYSCTL_DESCR("ThinkPad ACPI controls"), 1248 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 1249 if (err) 1250 goto fail; 1251 1252 if (sc->sc_batctl.have.individual_control) { 1253 err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode, 1254 0, CTLTYPE_NODE, "bat0", 1255 SYSCTL_DESCR("battery charge controls (primary battery)"), 1256 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 1257 if (err) 1258 goto fail; 1259 1260 thinkpad_battery_sysctl_setup_controls(self, cnode, 1261 THINKPAD_BAT_PRIMARY); 1262 1263 err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode, 1264 0, CTLTYPE_NODE, "bat1", 1265 SYSCTL_DESCR("battery charge controls (secondary battery)"), 1266 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 1267 if (err) 1268 goto fail; 1269 1270 thinkpad_battery_sysctl_setup_controls(self, cnode, 1271 THINKPAD_BAT_SECONDARY); 1272 } else { 1273 err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode, 1274 0, CTLTYPE_NODE, "bat", 1275 SYSCTL_DESCR("battery charge controls"), 1276 NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL); 1277 if (err) 1278 goto fail; 1279 1280 thinkpad_battery_sysctl_setup_controls(self, cnode, 1281 THINKPAD_BAT_ANY); 1282 } 1283 1284 return; 1285 1286fail: 1287 aprint_error_dev(self, "unable to add sysctl nodes (%d)\n", err); 1288} 1289 1290static bool 1291thinkpad_resume(device_t dv, const pmf_qual_t *qual) 1292{ 1293 thinkpad_softc_t *sc = device_private(dv); 1294 1295 if (sc->sc_powhdl == NULL) 1296 return true; 1297 1298 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true); 1299 1300 return true; 1301} 1302 1303MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power"); 1304 1305#ifdef _MODULE 1306#include "ioconf.c" 1307#endif 1308 1309static int 1310thinkpad_modcmd(modcmd_t cmd, void *aux) 1311{ 1312 int rv = 0; 1313 1314 switch (cmd) { 1315 1316 case MODULE_CMD_INIT: 1317 1318#ifdef _MODULE 1319 rv = config_init_component(cfdriver_ioconf_thinkpad, 1320 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 1321#endif 1322 break; 1323 1324 case MODULE_CMD_FINI: 1325 1326#ifdef _MODULE 1327 rv = config_fini_component(cfdriver_ioconf_thinkpad, 1328 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad); 1329#endif 1330 break; 1331 1332 default: 1333 rv = ENOTTY; 1334 } 1335 1336 return rv; 1337} 1338