acpi_ibm.c revision 178193
1138627Stakawata/*- 2138627Stakawata * Copyright (c) 2004 Takanori Watanabe 3147196Smarkus * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org> 4138627Stakawata * All rights reserved. 5138627Stakawata * 6138627Stakawata * Redistribution and use in source and binary forms, with or without 7138627Stakawata * modification, are permitted provided that the following conditions 8138627Stakawata * are met: 9138627Stakawata * 1. Redistributions of source code must retain the above copyright 10138627Stakawata * notice, this list of conditions and the following disclaimer. 11138627Stakawata * 2. Redistributions in binary form must reproduce the above copyright 12138627Stakawata * notice, this list of conditions and the following disclaimer in the 13138627Stakawata * documentation and/or other materials provided with the distribution. 14138627Stakawata * 15138627Stakawata * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16138627Stakawata * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17138627Stakawata * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18138627Stakawata * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19138627Stakawata * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20138627Stakawata * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21138627Stakawata * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22138627Stakawata * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23138627Stakawata * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24138627Stakawata * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25138627Stakawata * SUCH DAMAGE. 26138627Stakawata */ 27138627Stakawata 28143002Sobrien#include <sys/cdefs.h> 29143002Sobrien__FBSDID("$FreeBSD: head/sys/dev/acpi_support/acpi_ibm.c 178193 2008-04-14 08:00:00Z phk $"); 30143002Sobrien 31147196Smarkus/* 32147196Smarkus * Driver for extra ACPI-controlled gadgets found on IBM ThinkPad laptops. 33147196Smarkus * Inspired by the ibm-acpi and tpb projects which implement these features 34147196Smarkus * on Linux. 35147196Smarkus * 36147196Smarkus * acpi-ibm: <http://ibm-acpi.sourceforge.net/> 37147196Smarkus * tpb: <http://www.nongnu.org/tpb/> 38147196Smarkus */ 39147196Smarkus 40138627Stakawata#include "opt_acpi.h" 41138627Stakawata#include <sys/param.h> 42138627Stakawata#include <sys/kernel.h> 43138627Stakawata#include <sys/bus.h> 44138627Stakawata#include <machine/cpufunc.h> 45150003Sobrien#include <contrib/dev/acpica/acpi.h> 46138627Stakawata#include "acpi_if.h" 47138627Stakawata#include <sys/module.h> 48138627Stakawata#include <dev/acpica/acpivar.h> 49147196Smarkus#include <dev/led/led.h> 50138627Stakawata#include <sys/sysctl.h> 51178193Sphk#include <isa/rtc.h> 52138627Stakawata 53138825Snjl#define _COMPONENT ACPI_OEM 54138774SscottlACPI_MODULE_NAME("IBM") 55138774Sscottl 56147196Smarkus/* Internal methods */ 57147196Smarkus#define ACPI_IBM_METHOD_EVENTS 1 58147196Smarkus#define ACPI_IBM_METHOD_EVENTMASK 2 59147196Smarkus#define ACPI_IBM_METHOD_HOTKEY 3 60147196Smarkus#define ACPI_IBM_METHOD_BRIGHTNESS 4 61147196Smarkus#define ACPI_IBM_METHOD_VOLUME 5 62147196Smarkus#define ACPI_IBM_METHOD_MUTE 6 63147196Smarkus#define ACPI_IBM_METHOD_THINKLIGHT 7 64147196Smarkus#define ACPI_IBM_METHOD_BLUETOOTH 8 65147196Smarkus#define ACPI_IBM_METHOD_WLAN 9 66147196Smarkus#define ACPI_IBM_METHOD_FANSPEED 10 67154326Smarkus#define ACPI_IBM_METHOD_FANLEVEL 11 68154326Smarkus#define ACPI_IBM_METHOD_FANSTATUS 12 69154326Smarkus#define ACPI_IBM_METHOD_THERMAL 13 70138627Stakawata 71147196Smarkus/* Hotkeys/Buttons */ 72147196Smarkus#define IBM_RTC_HOTKEY1 0x64 73147196Smarkus#define IBM_RTC_MASK_HOME (1 << 0) 74147196Smarkus#define IBM_RTC_MASK_SEARCH (1 << 1) 75147196Smarkus#define IBM_RTC_MASK_MAIL (1 << 2) 76147196Smarkus#define IBM_RTC_MASK_WLAN (1 << 5) 77147196Smarkus#define IBM_RTC_HOTKEY2 0x65 78147196Smarkus#define IBM_RTC_MASK_THINKPAD (1 << 3) 79147196Smarkus#define IBM_RTC_MASK_ZOOM (1 << 5) 80147196Smarkus#define IBM_RTC_MASK_VIDEO (1 << 6) 81147196Smarkus#define IBM_RTC_MASK_HIBERNATE (1 << 7) 82147196Smarkus#define IBM_RTC_THINKLIGHT 0x66 83147196Smarkus#define IBM_RTC_MASK_THINKLIGHT (1 << 4) 84147196Smarkus#define IBM_RTC_SCREENEXPAND 0x67 85147196Smarkus#define IBM_RTC_MASK_SCREENEXPAND (1 << 5) 86147196Smarkus#define IBM_RTC_BRIGHTNESS 0x6c 87147196Smarkus#define IBM_RTC_MASK_BRIGHTNESS (1 << 5) 88147196Smarkus#define IBM_RTC_VOLUME 0x6e 89147196Smarkus#define IBM_RTC_MASK_VOLUME (1 << 7) 90138627Stakawata 91147196Smarkus/* Embedded Controller registers */ 92147196Smarkus#define IBM_EC_BRIGHTNESS 0x31 93147196Smarkus#define IBM_EC_MASK_BRI 0x7 94147196Smarkus#define IBM_EC_VOLUME 0x30 95147196Smarkus#define IBM_EC_MASK_VOL 0xf 96147196Smarkus#define IBM_EC_MASK_MUTE (1 << 6) 97147196Smarkus#define IBM_EC_FANSTATUS 0x2F 98154326Smarkus#define IBM_EC_MASK_FANLEVEL 0x3f 99154326Smarkus#define IBM_EC_MASK_FANDISENGAGED (1 << 6) 100147196Smarkus#define IBM_EC_MASK_FANSTATUS (1 << 7) 101147196Smarkus#define IBM_EC_FANSPEED 0x84 102147196Smarkus 103147196Smarkus/* CMOS Commands */ 104147196Smarkus#define IBM_CMOS_VOLUME_DOWN 0 105147196Smarkus#define IBM_CMOS_VOLUME_UP 1 106147196Smarkus#define IBM_CMOS_VOLUME_MUTE 2 107147196Smarkus#define IBM_CMOS_BRIGHTNESS_UP 4 108147196Smarkus#define IBM_CMOS_BRIGHTNESS_DOWN 5 109147196Smarkus 110147196Smarkus/* ACPI methods */ 111147196Smarkus#define IBM_NAME_KEYLIGHT "KBLT" 112147196Smarkus#define IBM_NAME_WLAN_BT_GET "GBDC" 113147196Smarkus#define IBM_NAME_WLAN_BT_SET "SBDC" 114147196Smarkus#define IBM_NAME_MASK_BT (1 << 1) 115147196Smarkus#define IBM_NAME_MASK_WLAN (1 << 2) 116147196Smarkus#define IBM_NAME_THERMAL_GET "TMP7" 117147196Smarkus#define IBM_NAME_THERMAL_UPDT "UPDT" 118147196Smarkus 119147196Smarkus#define IBM_NAME_EVENTS_STATUS_GET "DHKC" 120147196Smarkus#define IBM_NAME_EVENTS_MASK_GET "DHKN" 121147196Smarkus#define IBM_NAME_EVENTS_STATUS_SET "MHKC" 122147196Smarkus#define IBM_NAME_EVENTS_MASK_SET "MHKM" 123147196Smarkus#define IBM_NAME_EVENTS_GET "MHKP" 124147196Smarkus#define IBM_NAME_EVENTS_AVAILMASK "MHKA" 125147196Smarkus 126147196Smarkus#define ABS(x) (((x) < 0)? -(x) : (x)) 127147196Smarkus 128138627Stakawatastruct acpi_ibm_softc { 129147196Smarkus device_t dev; 130147196Smarkus ACPI_HANDLE handle; 131147196Smarkus 132147196Smarkus /* Embedded controller */ 133147196Smarkus device_t ec_dev; 134147196Smarkus ACPI_HANDLE ec_handle; 135147196Smarkus 136147196Smarkus /* CMOS */ 137147196Smarkus ACPI_HANDLE cmos_handle; 138147196Smarkus 139147196Smarkus /* Fan status */ 140147196Smarkus ACPI_HANDLE fan_handle; 141147196Smarkus int fan_levels; 142147196Smarkus 143147196Smarkus /* Keylight commands and states */ 144147196Smarkus ACPI_HANDLE light_handle; 145147196Smarkus int light_cmd_on; 146147196Smarkus int light_cmd_off; 147147196Smarkus int light_val; 148147196Smarkus int light_get_supported; 149147196Smarkus int light_set_supported; 150148710Smarkus 151148710Smarkus /* led(4) interface */ 152147196Smarkus struct cdev *led_dev; 153148710Smarkus int led_busy; 154148710Smarkus int led_state; 155147196Smarkus 156147196Smarkus int wlan_bt_flags; 157147196Smarkus int thermal_updt_supported; 158147196Smarkus 159147196Smarkus unsigned int events_availmask; 160147196Smarkus unsigned int events_initialmask; 161147196Smarkus int events_mask_supported; 162147196Smarkus int events_enable; 163147196Smarkus 164147196Smarkus struct sysctl_ctx_list *sysctl_ctx; 165147196Smarkus struct sysctl_oid *sysctl_tree; 166138627Stakawata}; 167138627Stakawata 168147196Smarkusstatic struct { 169147196Smarkus char *name; 170147196Smarkus int method; 171147196Smarkus char *description; 172147196Smarkus int access; 173147196Smarkus} acpi_ibm_sysctls[] = { 174147196Smarkus { 175147196Smarkus .name = "events", 176147196Smarkus .method = ACPI_IBM_METHOD_EVENTS, 177147196Smarkus .description = "ACPI events enable", 178147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 179147196Smarkus }, 180147196Smarkus { 181147196Smarkus .name = "eventmask", 182147196Smarkus .method = ACPI_IBM_METHOD_EVENTMASK, 183147196Smarkus .description = "ACPI eventmask", 184147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 185147196Smarkus }, 186147196Smarkus { 187147196Smarkus .name = "hotkey", 188147196Smarkus .method = ACPI_IBM_METHOD_HOTKEY, 189147196Smarkus .description = "Key Status", 190147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 191147196Smarkus }, 192147196Smarkus { 193147196Smarkus .name = "lcd_brightness", 194147196Smarkus .method = ACPI_IBM_METHOD_BRIGHTNESS, 195147196Smarkus .description = "LCD Brightness", 196147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 197147196Smarkus }, 198147196Smarkus { 199147196Smarkus .name = "volume", 200147196Smarkus .method = ACPI_IBM_METHOD_VOLUME, 201147196Smarkus .description = "Volume", 202147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 203147196Smarkus }, 204147196Smarkus { 205147196Smarkus .name = "mute", 206147196Smarkus .method = ACPI_IBM_METHOD_MUTE, 207147196Smarkus .description = "Mute", 208147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 209147196Smarkus }, 210147196Smarkus { 211147196Smarkus .name = "thinklight", 212147196Smarkus .method = ACPI_IBM_METHOD_THINKLIGHT, 213147196Smarkus .description = "Thinklight enable", 214147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 215147196Smarkus }, 216147196Smarkus { 217147196Smarkus .name = "bluetooth", 218147196Smarkus .method = ACPI_IBM_METHOD_BLUETOOTH, 219147196Smarkus .description = "Bluetooth enable", 220147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 221147196Smarkus }, 222147196Smarkus { 223147196Smarkus .name = "wlan", 224147196Smarkus .method = ACPI_IBM_METHOD_WLAN, 225147196Smarkus .description = "WLAN enable", 226147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 227147196Smarkus }, 228147196Smarkus { 229147196Smarkus .name = "fan_speed", 230147196Smarkus .method = ACPI_IBM_METHOD_FANSPEED, 231147196Smarkus .description = "Fan speed", 232147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 233147196Smarkus }, 234147196Smarkus { 235154326Smarkus .name = "fan_level", 236154326Smarkus .method = ACPI_IBM_METHOD_FANLEVEL, 237154326Smarkus .description = "Fan level", 238154326Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 239154326Smarkus }, 240154326Smarkus { 241147196Smarkus .name = "fan", 242147196Smarkus .method = ACPI_IBM_METHOD_FANSTATUS, 243147196Smarkus .description = "Fan enable", 244154326Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 245147196Smarkus }, 246147196Smarkus 247147196Smarkus { NULL, 0, NULL, 0 } 248147196Smarkus}; 249147196Smarkus 250147196SmarkusACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); 251147196Smarkus 252138627Stakawatastatic int acpi_ibm_probe(device_t dev); 253138627Stakawatastatic int acpi_ibm_attach(device_t dev); 254138627Stakawatastatic int acpi_ibm_detach(device_t dev); 255138627Stakawata 256148710Smarkusstatic void ibm_led(void *softc, int onoff); 257148710Smarkusstatic void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 258148710Smarkus 259147196Smarkusstatic int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 260147196Smarkusstatic int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 261147196Smarkusstatic int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 262147196Smarkusstatic int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 263147196Smarkus 264147196Smarkusstatic int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 265147196Smarkusstatic int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 266147196Smarkusstatic void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 267147196Smarkus 268138627Stakawatastatic device_method_t acpi_ibm_methods[] = { 269138627Stakawata /* Device interface */ 270138627Stakawata DEVMETHOD(device_probe, acpi_ibm_probe), 271138627Stakawata DEVMETHOD(device_attach, acpi_ibm_attach), 272138627Stakawata DEVMETHOD(device_detach, acpi_ibm_detach), 273138627Stakawata 274138627Stakawata {0, 0} 275138627Stakawata}; 276138627Stakawata 277138627Stakawatastatic driver_t acpi_ibm_driver = { 278138627Stakawata "acpi_ibm", 279138627Stakawata acpi_ibm_methods, 280138627Stakawata sizeof(struct acpi_ibm_softc), 281138627Stakawata}; 282138627Stakawata 283138627Stakawatastatic devclass_t acpi_ibm_devclass; 284138627Stakawata 285138627StakawataDRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 286138627Stakawata 0, 0); 287138627StakawataMODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 288172980Sjhbstatic char *ibm_ids[] = {"IBM0068", NULL}; 289138627Stakawata 290147196Smarkusstatic void 291147196Smarkusibm_led(void *softc, int onoff) 292147196Smarkus{ 293148710Smarkus struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 294148710Smarkus 295148710Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 296148710Smarkus 297148710Smarkus if (sc->led_busy) 298148710Smarkus return; 299148710Smarkus 300148710Smarkus sc->led_busy = 1; 301148710Smarkus sc->led_state = onoff; 302148710Smarkus 303167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 304148710Smarkus} 305148710Smarkus 306148710Smarkusstatic void 307148710Smarkusibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 308148710Smarkus{ 309148710Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 310148710Smarkus 311147196Smarkus ACPI_SERIAL_BEGIN(ibm); 312148710Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 313147196Smarkus ACPI_SERIAL_END(ibm); 314148710Smarkus 315148710Smarkus sc->led_busy = 0; 316147196Smarkus} 317147196Smarkus 318138627Stakawatastatic int 319138627Stakawataacpi_ibm_probe(device_t dev) 320138627Stakawata{ 321147196Smarkus if (acpi_disabled("ibm") || 322147196Smarkus ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 323147196Smarkus device_get_unit(dev) != 0) 324147196Smarkus return (ENXIO); 325138627Stakawata 326147196Smarkus device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 327138627Stakawata 328147196Smarkus return (0); 329138627Stakawata} 330138627Stakawata 331138627Stakawatastatic int 332147196Smarkusacpi_ibm_attach(device_t dev) 333138627Stakawata{ 334147196Smarkus struct acpi_ibm_softc *sc; 335147196Smarkus struct acpi_softc *acpi_sc; 336147196Smarkus devclass_t ec_devclass; 337138627Stakawata 338138627Stakawata ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 339138627Stakawata 340138627Stakawata sc = device_get_softc(dev); 341147196Smarkus sc->dev = dev; 342147196Smarkus sc->handle = acpi_get_handle(dev); 343147196Smarkus 344147196Smarkus acpi_sc = acpi_device_get_parent_softc(dev); 345147196Smarkus 346147196Smarkus /* Look for the first embedded controller */ 347147196Smarkus if (!(ec_devclass = devclass_find ("acpi_ec"))) { 348147196Smarkus if (bootverbose) 349147196Smarkus device_printf(dev, "Couldn't find acpi_ec devclass\n"); 350147196Smarkus return (EINVAL); 351138627Stakawata } 352147196Smarkus if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 353147196Smarkus if (bootverbose) 354147196Smarkus device_printf(dev, "Couldn't find acpi_ec device\n"); 355147196Smarkus return (EINVAL); 356147196Smarkus } 357147196Smarkus sc->ec_handle = acpi_get_handle(sc->ec_dev); 358138627Stakawata 359147196Smarkus ACPI_SERIAL_BEGIN(ibm); 360138627Stakawata 361147196Smarkus /* Get the sysctl tree */ 362147196Smarkus sc->sysctl_ctx = device_get_sysctl_ctx(dev); 363147196Smarkus sc->sysctl_tree = device_get_sysctl_tree(dev); 364138627Stakawata 365147196Smarkus /* Look for event mask and hook up the nodes */ 366147196Smarkus sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 367147196Smarkus IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 368138627Stakawata 369147196Smarkus if (sc->events_mask_supported) { 370147196Smarkus SYSCTL_ADD_INT(sc->sysctl_ctx, 371147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 372147196Smarkus "initialmask", CTLFLAG_RD, 373147196Smarkus &sc->events_initialmask, 0, "Initial eventmask"); 374138627Stakawata 375147196Smarkus /* The availmask is the bitmask of supported events */ 376147196Smarkus if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 377147196Smarkus IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 378147196Smarkus sc->events_availmask = 0xffffffff; 379138627Stakawata 380147196Smarkus SYSCTL_ADD_INT(sc->sysctl_ctx, 381147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 382147196Smarkus "availmask", CTLFLAG_RD, 383147196Smarkus &sc->events_availmask, 0, "Mask of supported events"); 384147196Smarkus } 385138627Stakawata 386147196Smarkus /* Hook up proc nodes */ 387147196Smarkus for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 388147196Smarkus if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 389147196Smarkus continue; 390147196Smarkus 391147196Smarkus SYSCTL_ADD_PROC(sc->sysctl_ctx, 392147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 393147196Smarkus acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, 394147196Smarkus sc, i, acpi_ibm_sysctl, "I", 395147196Smarkus acpi_ibm_sysctls[i].description); 396138627Stakawata } 397147196Smarkus 398147196Smarkus /* Hook up thermal node */ 399147196Smarkus if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 400147196Smarkus SYSCTL_ADD_PROC(sc->sysctl_ctx, 401147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 402147196Smarkus "thermal", CTLTYPE_STRING | CTLFLAG_RD, 403147196Smarkus sc, 0, acpi_ibm_thermal_sysctl, "I", 404147196Smarkus "Thermal zones"); 405147196Smarkus } 406147196Smarkus 407147196Smarkus ACPI_SERIAL_END(ibm); 408147196Smarkus 409147196Smarkus /* Handle notifies */ 410147196Smarkus AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 411147196Smarkus acpi_ibm_notify, dev); 412147196Smarkus 413147196Smarkus /* Hook up light to led(4) */ 414147246Smarkus if (sc->light_set_supported) 415169031Smarkus sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); 416147196Smarkus 417147196Smarkus return (0); 418138627Stakawata} 419138627Stakawata 420138627Stakawatastatic int 421138627Stakawataacpi_ibm_detach(device_t dev) 422138627Stakawata{ 423138774Sscottl ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 424138774Sscottl 425138627Stakawata struct acpi_ibm_softc *sc = device_get_softc(dev); 426138627Stakawata 427147196Smarkus /* Disable events and restore eventmask */ 428147196Smarkus ACPI_SERIAL_BEGIN(ibm); 429147196Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); 430147196Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); 431147196Smarkus ACPI_SERIAL_END(ibm); 432147196Smarkus 433147196Smarkus AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); 434147196Smarkus 435147246Smarkus if (sc->led_dev != NULL) 436147246Smarkus led_destroy(sc->led_dev); 437147246Smarkus 438147196Smarkus return (0); 439138627Stakawata} 440147196Smarkus 441138627Stakawatastatic int 442147196Smarkusacpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 443138627Stakawata{ 444147196Smarkus ACPI_OBJECT arg[2]; 445147196Smarkus ACPI_OBJECT_LIST args; 446147196Smarkus ACPI_STATUS status; 447147196Smarkus 448147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 449147196Smarkus ACPI_SERIAL_ASSERT(ibm); 450147196Smarkus 451147196Smarkus args.Count = 2; 452147196Smarkus args.Pointer = arg; 453147196Smarkus arg[0].Type = ACPI_TYPE_INTEGER; 454147196Smarkus arg[1].Type = ACPI_TYPE_INTEGER; 455147196Smarkus 456147196Smarkus for (int i = 0; i < 32; ++i) { 457147196Smarkus arg[0].Integer.Value = i+1; 458147196Smarkus arg[1].Integer.Value = (((1 << i) & val) != 0); 459147196Smarkus status = AcpiEvaluateObject(sc->handle, 460147196Smarkus IBM_NAME_EVENTS_MASK_SET, &args, NULL); 461147196Smarkus 462147196Smarkus if (ACPI_FAILURE(status)) 463147196Smarkus return (status); 464147196Smarkus } 465147196Smarkus 466147196Smarkus return (0); 467138627Stakawata} 468138627Stakawata 469138627Stakawatastatic int 470147196Smarkusacpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 471138627Stakawata{ 472147196Smarkus struct acpi_ibm_softc *sc; 473147196Smarkus int arg; 474147196Smarkus int error = 0; 475147196Smarkus int function; 476147196Smarkus int method; 477147196Smarkus 478147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 479147196Smarkus 480147196Smarkus sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 481147196Smarkus function = oidp->oid_arg2; 482147196Smarkus method = acpi_ibm_sysctls[function].method; 483147196Smarkus 484147196Smarkus ACPI_SERIAL_BEGIN(ibm); 485147196Smarkus arg = acpi_ibm_sysctl_get(sc, method); 486147196Smarkus error = sysctl_handle_int(oidp, &arg, 0, req); 487147196Smarkus 488147196Smarkus /* Sanity check */ 489147196Smarkus if (error != 0 || req->newptr == NULL) 490147196Smarkus goto out; 491147196Smarkus 492147196Smarkus /* Update */ 493147196Smarkus error = acpi_ibm_sysctl_set(sc, method, arg); 494147196Smarkus 495147196Smarkusout: 496147196Smarkus ACPI_SERIAL_END(ibm); 497147196Smarkus return (error); 498138627Stakawata} 499147196Smarkus 500147196Smarkusstatic int 501147196Smarkusacpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 502138627Stakawata{ 503147196Smarkus ACPI_INTEGER val_ec; 504147196Smarkus int val = 0, key; 505138627Stakawata 506147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 507147196Smarkus ACPI_SERIAL_ASSERT(ibm); 508138627Stakawata 509147196Smarkus switch (method) { 510147196Smarkus case ACPI_IBM_METHOD_EVENTS: 511147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 512147196Smarkus break; 513138627Stakawata 514147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 515147196Smarkus if (sc->events_mask_supported) 516147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 517147196Smarkus break; 518147196Smarkus 519147196Smarkus case ACPI_IBM_METHOD_HOTKEY: 520147196Smarkus /* 521147196Smarkus * Construct the hotkey as a bitmask as illustrated below. 522147196Smarkus * Note that whenever a key was pressed, the respecting bit 523147196Smarkus * toggles and nothing else changes. 524147196Smarkus * +--+--+-+-+-+-+-+-+-+-+-+-+ 525147196Smarkus * |11|10|9|8|7|6|5|4|3|2|1|0| 526147196Smarkus * +--+--+-+-+-+-+-+-+-+-+-+-+ 527147196Smarkus * | | | | | | | | | | | | 528147196Smarkus * | | | | | | | | | | | +- Home Button 529147196Smarkus * | | | | | | | | | | +--- Search Button 530147196Smarkus * | | | | | | | | | +----- Mail Button 531147196Smarkus * | | | | | | | | +------- Thinkpad Button 532147196Smarkus * | | | | | | | +--------- Zoom (Fn + Space) 533147196Smarkus * | | | | | | +----------- WLAN Button 534147196Smarkus * | | | | | +------------- Video Button 535147196Smarkus * | | | | +--------------- Hibernate Button 536147196Smarkus * | | | +----------------- Thinklight Button 537147196Smarkus * | | +------------------- Screen expand (Fn + F8) 538147196Smarkus * | +--------------------- Brightness 539147196Smarkus * +------------------------ Volume/Mute 540147196Smarkus */ 541147196Smarkus key = rtcin(IBM_RTC_HOTKEY1); 542147196Smarkus val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 543147196Smarkus key = rtcin(IBM_RTC_HOTKEY2); 544147196Smarkus val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 545147196Smarkus val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 546147196Smarkus key = rtcin(IBM_RTC_THINKLIGHT); 547147196Smarkus val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 548147196Smarkus key = rtcin(IBM_RTC_SCREENEXPAND); 549147196Smarkus val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 550147196Smarkus key = rtcin(IBM_RTC_BRIGHTNESS); 551147196Smarkus val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 552147196Smarkus key = rtcin(IBM_RTC_VOLUME); 553147196Smarkus val |= (IBM_RTC_MASK_VOLUME & key) << 4; 554147196Smarkus break; 555147196Smarkus 556147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 557147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 558147196Smarkus val = val_ec & IBM_EC_MASK_BRI; 559147196Smarkus break; 560147196Smarkus 561147196Smarkus case ACPI_IBM_METHOD_VOLUME: 562147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 563147246Smarkus val = val_ec & IBM_EC_MASK_VOL; 564147196Smarkus break; 565147196Smarkus 566147196Smarkus case ACPI_IBM_METHOD_MUTE: 567147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 568147196Smarkus val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 569147196Smarkus break; 570147196Smarkus 571147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 572147196Smarkus if (sc->light_get_supported) 573147196Smarkus acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 574147196Smarkus else 575147196Smarkus val = sc->light_val; 576147196Smarkus break; 577147196Smarkus 578147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 579147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 580147196Smarkus sc->wlan_bt_flags = val; 581147196Smarkus val = ((val & IBM_NAME_MASK_BT) != 0); 582147196Smarkus break; 583147196Smarkus 584147196Smarkus case ACPI_IBM_METHOD_WLAN: 585147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 586147196Smarkus sc->wlan_bt_flags = val; 587147196Smarkus val = ((val & IBM_NAME_MASK_WLAN) != 0); 588147196Smarkus break; 589147196Smarkus 590147196Smarkus case ACPI_IBM_METHOD_FANSPEED: 591147196Smarkus if (sc->fan_handle) { 592147196Smarkus if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 593147196Smarkus val = -1; 594138627Stakawata } 595147196Smarkus else { 596147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 597147196Smarkus val = val_ec; 598147196Smarkus } 599147196Smarkus break; 600138627Stakawata 601154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 602154326Smarkus /* 603154326Smarkus * The IBM_EC_FANSTATUS register works as follows: 604154326Smarkus * Bit 0-5 indicate the level at which the fan operates. Only 605154326Smarkus * values between 0 and 7 have an effect. Everything 606154326Smarkus * above 7 is treated the same as level 7 607154326Smarkus * Bit 6 overrides the fan speed limit if set to 1 608154326Smarkus * Bit 7 indicates at which mode the fan operates: 609154326Smarkus * manual (0) or automatic (1) 610154326Smarkus */ 611154326Smarkus if (!sc->fan_handle) { 612154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 613154326Smarkus val = val_ec & IBM_EC_MASK_FANLEVEL; 614154326Smarkus } 615154326Smarkus break; 616154326Smarkus 617147196Smarkus case ACPI_IBM_METHOD_FANSTATUS: 618147196Smarkus if (!sc->fan_handle) { 619147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 620147196Smarkus val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 621138627Stakawata } 622147196Smarkus else 623147196Smarkus val = -1; 624147196Smarkus break; 625138627Stakawata } 626147196Smarkus 627147196Smarkus return (val); 628138627Stakawata} 629138627Stakawata 630138627Stakawatastatic int 631147196Smarkusacpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 632138627Stakawata{ 633147196Smarkus int val, step; 634147196Smarkus ACPI_INTEGER val_ec; 635147196Smarkus ACPI_OBJECT Arg; 636147196Smarkus ACPI_OBJECT_LIST Args; 637147196Smarkus ACPI_STATUS status; 638138627Stakawata 639147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 640147196Smarkus ACPI_SERIAL_ASSERT(ibm); 641138627Stakawata 642147196Smarkus switch (method) { 643147196Smarkus case ACPI_IBM_METHOD_EVENTS: 644147196Smarkus if (arg < 0 || arg > 1) 645147196Smarkus return (EINVAL); 646138627Stakawata 647147196Smarkus status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 648147196Smarkus if (ACPI_FAILURE(status)) 649147196Smarkus return (status); 650147196Smarkus if (sc->events_mask_supported) 651147196Smarkus return acpi_ibm_eventmask_set(sc, sc->events_availmask); 652147196Smarkus break; 653138627Stakawata 654147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 655147196Smarkus if (sc->events_mask_supported) 656147196Smarkus return acpi_ibm_eventmask_set(sc, arg); 657147196Smarkus break; 658138627Stakawata 659147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 660147196Smarkus if (arg < 0 || arg > 7) 661147196Smarkus return (EINVAL); 662138627Stakawata 663147196Smarkus if (sc->cmos_handle) { 664147196Smarkus /* Read the current brightness */ 665147196Smarkus status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 666147196Smarkus if (ACPI_FAILURE(status)) 667147196Smarkus return (status); 668147196Smarkus val = val_ec & IBM_EC_MASK_BRI; 669138627Stakawata 670147196Smarkus Args.Count = 1; 671147196Smarkus Args.Pointer = &Arg; 672147196Smarkus Arg.Type = ACPI_TYPE_INTEGER; 673147196Smarkus Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : IBM_CMOS_BRIGHTNESS_DOWN; 674138627Stakawata 675147196Smarkus step = (arg > val) ? 1 : -1; 676147196Smarkus for (int i = val; i != arg; i += step) { 677147196Smarkus status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 678147196Smarkus if (ACPI_FAILURE(status)) 679147196Smarkus break; 680147196Smarkus } 681147196Smarkus } 682147196Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 683147196Smarkus break; 684138627Stakawata 685147196Smarkus case ACPI_IBM_METHOD_VOLUME: 686147196Smarkus if (arg < 0 || arg > 14) 687147196Smarkus return (EINVAL); 688138627Stakawata 689147246Smarkus status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 690147246Smarkus if (ACPI_FAILURE(status)) 691147246Smarkus return (status); 692147246Smarkus 693147196Smarkus if (sc->cmos_handle) { 694147196Smarkus val = val_ec & IBM_EC_MASK_VOL; 695138627Stakawata 696147196Smarkus Args.Count = 1; 697147196Smarkus Args.Pointer = &Arg; 698147196Smarkus Arg.Type = ACPI_TYPE_INTEGER; 699147196Smarkus Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : IBM_CMOS_VOLUME_DOWN; 700138627Stakawata 701147196Smarkus step = (arg > val) ? 1 : -1; 702147196Smarkus for (int i = val; i != arg; i += step) { 703147196Smarkus status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 704147196Smarkus if (ACPI_FAILURE(status)) 705147196Smarkus break; 706147196Smarkus } 707147196Smarkus } 708147196Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, arg + (val_ec & (~IBM_EC_MASK_VOL)), 1); 709147196Smarkus break; 710138627Stakawata 711147196Smarkus case ACPI_IBM_METHOD_MUTE: 712147196Smarkus if (arg < 0 || arg > 1) 713147196Smarkus return (EINVAL); 714138627Stakawata 715147246Smarkus status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 716147246Smarkus if (ACPI_FAILURE(status)) 717147246Smarkus return (status); 718147246Smarkus 719147196Smarkus if (sc->cmos_handle) { 720147196Smarkus val = val_ec & IBM_EC_MASK_VOL; 721147196Smarkus 722147196Smarkus Args.Count = 1; 723147196Smarkus Args.Pointer = &Arg; 724147196Smarkus Arg.Type = ACPI_TYPE_INTEGER; 725147196Smarkus Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 726147196Smarkus 727147196Smarkus status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 728147196Smarkus if (ACPI_FAILURE(status)) 729147196Smarkus break; 730147196Smarkus } 731147196Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, (arg==1) ? val_ec | IBM_EC_MASK_MUTE : val_ec & (~IBM_EC_MASK_MUTE), 1); 732147196Smarkus break; 733147196Smarkus 734147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 735147196Smarkus if (arg < 0 || arg > 1) 736147196Smarkus return (EINVAL); 737147196Smarkus 738147196Smarkus if (sc->light_set_supported) { 739147196Smarkus Args.Count = 1; 740147196Smarkus Args.Pointer = &Arg; 741147196Smarkus Arg.Type = ACPI_TYPE_INTEGER; 742147196Smarkus Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 743147196Smarkus 744147196Smarkus status = AcpiEvaluateObject(sc->light_handle, NULL, &Args, NULL); 745147196Smarkus if (ACPI_SUCCESS(status)) 746147196Smarkus sc->light_val = arg; 747147196Smarkus return (status); 748147196Smarkus } 749147196Smarkus break; 750147196Smarkus 751147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 752147196Smarkus if (arg < 0 || arg > 1) 753147196Smarkus return (EINVAL); 754147196Smarkus 755147196Smarkus val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 756147196Smarkus return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 757147196Smarkus break; 758154326Smarkus 759154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 760154326Smarkus if (arg < 0 || arg > 7) 761154326Smarkus return (EINVAL); 762154326Smarkus 763154326Smarkus if (!sc->fan_handle) { 764154326Smarkus /* Read the current fanstatus */ 765154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 766154326Smarkus val = val_ec & (~IBM_EC_MASK_FANLEVEL); 767154326Smarkus 768154326Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 769154326Smarkus } 770154326Smarkus break; 771154326Smarkus 772154326Smarkus case ACPI_IBM_METHOD_FANSTATUS: 773154326Smarkus if (arg < 0 || arg > 1) 774154326Smarkus return (EINVAL); 775154326Smarkus 776154326Smarkus if (!sc->fan_handle) { 777154326Smarkus /* Read the current fanstatus */ 778154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 779154326Smarkus 780154326Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 781154326Smarkus (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 782154326Smarkus } 783154326Smarkus break; 784147196Smarkus } 785147196Smarkus 786147196Smarkus return (0); 787138627Stakawata} 788138627Stakawata 789147196Smarkusstatic int 790147196Smarkusacpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 791138627Stakawata{ 792147196Smarkus int dummy; 793147196Smarkus ACPI_OBJECT_TYPE cmos_t; 794147196Smarkus ACPI_HANDLE ledb_handle; 795138627Stakawata 796147196Smarkus switch (method) { 797147196Smarkus case ACPI_IBM_METHOD_EVENTS: 798147196Smarkus /* Events are disabled by default */ 799147196Smarkus return (TRUE); 800138627Stakawata 801147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 802147196Smarkus return (sc->events_mask_supported); 803147196Smarkus 804147196Smarkus case ACPI_IBM_METHOD_HOTKEY: 805147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 806147196Smarkus case ACPI_IBM_METHOD_VOLUME: 807147196Smarkus case ACPI_IBM_METHOD_MUTE: 808147196Smarkus /* EC is required here, which was aready checked before */ 809147196Smarkus return (TRUE); 810147196Smarkus 811147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 812147196Smarkus sc->cmos_handle = NULL; 813169031Smarkus sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 814169031Smarkus sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 815147196Smarkus 816147196Smarkus if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 817147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 818147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 819147196Smarkus ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 820147196Smarkus cmos_t == ACPI_TYPE_METHOD) { 821147196Smarkus sc->light_cmd_on = 0x0c; 822147196Smarkus sc->light_cmd_off = 0x0d; 823147196Smarkus sc->cmos_handle = sc->light_handle; 824147196Smarkus } 825147196Smarkus else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 826147196Smarkus sc->light_cmd_on = 1; 827147196Smarkus sc->light_cmd_off = 0; 828147196Smarkus } 829147196Smarkus else 830147196Smarkus sc->light_handle = NULL; 831147196Smarkus 832147196Smarkus sc->light_set_supported = (sc->light_handle && 833147196Smarkus ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 834147196Smarkus 835169031Smarkus if (sc->light_get_supported) 836169031Smarkus return (TRUE); 837169031Smarkus 838169031Smarkus if (sc->light_set_supported) { 839147196Smarkus sc->light_val = 0; 840147196Smarkus return (TRUE); 841147196Smarkus } 842147196Smarkus 843169031Smarkus return (FALSE); 844169031Smarkus 845147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 846147196Smarkus case ACPI_IBM_METHOD_WLAN: 847147196Smarkus if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 848147196Smarkus return (TRUE); 849147196Smarkus return (FALSE); 850147196Smarkus 851147196Smarkus case ACPI_IBM_METHOD_FANSPEED: 852147196Smarkus /* 853147196Smarkus * Some models report the fan speed in levels from 0-7 854147196Smarkus * Newer models report it contiguously 855147196Smarkus */ 856147196Smarkus sc->fan_levels = 857147196Smarkus (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 858147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 859147196Smarkus return (TRUE); 860147196Smarkus 861154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 862147196Smarkus case ACPI_IBM_METHOD_FANSTATUS: 863147196Smarkus /* 864147196Smarkus * Fan status is only supported on those models, 865147196Smarkus * which report fan RPM contiguously, not in levels 866147196Smarkus */ 867147196Smarkus if (sc->fan_levels) 868147196Smarkus return (FALSE); 869147196Smarkus return (TRUE); 870147196Smarkus 871147196Smarkus case ACPI_IBM_METHOD_THERMAL: 872147196Smarkus if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 873147196Smarkus sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 874147196Smarkus return (TRUE); 875147196Smarkus } 876147196Smarkus return (FALSE); 877147196Smarkus } 878147196Smarkus return (FALSE); 879138627Stakawata} 880138627Stakawata 881138627Stakawatastatic int 882147196Smarkusacpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 883138627Stakawata{ 884147196Smarkus struct acpi_ibm_softc *sc; 885147196Smarkus int error = 0; 886147196Smarkus char temp_cmd[] = "TMP0"; 887147196Smarkus int temp[8]; 888138627Stakawata 889147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 890138627Stakawata 891147196Smarkus sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 892138627Stakawata 893147196Smarkus ACPI_SERIAL_BEGIN(ibm); 894138627Stakawata 895147196Smarkus for (int i = 0; i < 8; ++i) { 896147196Smarkus temp_cmd[3] = '0' + i; 897147196Smarkus 898147196Smarkus /* 899147196Smarkus * The TMPx methods seem to return +/- 128 or 0 900147196Smarkus * when the respecting sensor is not available 901147196Smarkus */ 902147196Smarkus if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 903147196Smarkus &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 904147196Smarkus temp[i] = -1; 905147196Smarkus else if (sc->thermal_updt_supported) 906147196Smarkus /* Temperature is reported in tenth of Kelvin */ 907147196Smarkus temp[i] = (temp[i] - 2732 + 5) / 10; 908147196Smarkus } 909147196Smarkus 910147196Smarkus error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 911147196Smarkus 912147196Smarkus ACPI_SERIAL_END(ibm); 913147196Smarkus return (error); 914138627Stakawata} 915138627Stakawata 916147196Smarkusstatic void 917147196Smarkusacpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 918138627Stakawata{ 919147196Smarkus int event, arg, type; 920147196Smarkus device_t dev = context; 921138627Stakawata struct acpi_ibm_softc *sc = device_get_softc(dev); 922138627Stakawata 923147196Smarkus ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 924138627Stakawata 925147196Smarkus if (notify != 0x80) 926161289Stakawata device_printf(dev, "Unknown notify\n"); 927138627Stakawata 928147196Smarkus for (;;) { 929147196Smarkus acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 930138627Stakawata 931147196Smarkus if (event == 0) 932147196Smarkus break; 933147196Smarkus 934147196Smarkus 935147196Smarkus type = (event >> 12) & 0xf; 936147196Smarkus arg = event & 0xfff; 937147196Smarkus switch (type) { 938147196Smarkus case 1: 939147196Smarkus if (!(sc->events_availmask & (1 << (arg - 1)))) { 940161289Stakawata device_printf(dev, "Unknown key %d\n", arg); 941147196Smarkus break; 942147196Smarkus } 943147196Smarkus 944147196Smarkus /* Notify devd(8) */ 945147196Smarkus acpi_UserNotify("IBM", h, (arg & 0xff)); 946147196Smarkus break; 947147196Smarkus default: 948147196Smarkus break; 949147196Smarkus } 950147196Smarkus } 951138627Stakawata} 952