acpi_ibm.c revision 237493
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 237493 2012-06-23 18:43:54Z iwasaki $"); 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> 45193530Sjkim 46193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 47193530Sjkim#include <contrib/dev/acpica/include/accommon.h> 48193530Sjkim 49138627Stakawata#include "acpi_if.h" 50138627Stakawata#include <sys/module.h> 51138627Stakawata#include <dev/acpica/acpivar.h> 52147196Smarkus#include <dev/led/led.h> 53237493Siwasaki#include <sys/power.h> 54237493Siwasaki#include <sys/sbuf.h> 55138627Stakawata#include <sys/sysctl.h> 56178193Sphk#include <isa/rtc.h> 57138627Stakawata 58138825Snjl#define _COMPONENT ACPI_OEM 59138774SscottlACPI_MODULE_NAME("IBM") 60138774Sscottl 61147196Smarkus/* Internal methods */ 62147196Smarkus#define ACPI_IBM_METHOD_EVENTS 1 63147196Smarkus#define ACPI_IBM_METHOD_EVENTMASK 2 64147196Smarkus#define ACPI_IBM_METHOD_HOTKEY 3 65147196Smarkus#define ACPI_IBM_METHOD_BRIGHTNESS 4 66147196Smarkus#define ACPI_IBM_METHOD_VOLUME 5 67147196Smarkus#define ACPI_IBM_METHOD_MUTE 6 68147196Smarkus#define ACPI_IBM_METHOD_THINKLIGHT 7 69147196Smarkus#define ACPI_IBM_METHOD_BLUETOOTH 8 70147196Smarkus#define ACPI_IBM_METHOD_WLAN 9 71147196Smarkus#define ACPI_IBM_METHOD_FANSPEED 10 72154326Smarkus#define ACPI_IBM_METHOD_FANLEVEL 11 73154326Smarkus#define ACPI_IBM_METHOD_FANSTATUS 12 74154326Smarkus#define ACPI_IBM_METHOD_THERMAL 13 75237493Siwasaki#define ACPI_IBM_METHOD_HANDLEREVENTS 14 76138627Stakawata 77147196Smarkus/* Hotkeys/Buttons */ 78147196Smarkus#define IBM_RTC_HOTKEY1 0x64 79147196Smarkus#define IBM_RTC_MASK_HOME (1 << 0) 80147196Smarkus#define IBM_RTC_MASK_SEARCH (1 << 1) 81147196Smarkus#define IBM_RTC_MASK_MAIL (1 << 2) 82147196Smarkus#define IBM_RTC_MASK_WLAN (1 << 5) 83147196Smarkus#define IBM_RTC_HOTKEY2 0x65 84147196Smarkus#define IBM_RTC_MASK_THINKPAD (1 << 3) 85147196Smarkus#define IBM_RTC_MASK_ZOOM (1 << 5) 86147196Smarkus#define IBM_RTC_MASK_VIDEO (1 << 6) 87147196Smarkus#define IBM_RTC_MASK_HIBERNATE (1 << 7) 88147196Smarkus#define IBM_RTC_THINKLIGHT 0x66 89147196Smarkus#define IBM_RTC_MASK_THINKLIGHT (1 << 4) 90147196Smarkus#define IBM_RTC_SCREENEXPAND 0x67 91147196Smarkus#define IBM_RTC_MASK_SCREENEXPAND (1 << 5) 92147196Smarkus#define IBM_RTC_BRIGHTNESS 0x6c 93147196Smarkus#define IBM_RTC_MASK_BRIGHTNESS (1 << 5) 94147196Smarkus#define IBM_RTC_VOLUME 0x6e 95147196Smarkus#define IBM_RTC_MASK_VOLUME (1 << 7) 96138627Stakawata 97147196Smarkus/* Embedded Controller registers */ 98147196Smarkus#define IBM_EC_BRIGHTNESS 0x31 99147196Smarkus#define IBM_EC_MASK_BRI 0x7 100147196Smarkus#define IBM_EC_VOLUME 0x30 101147196Smarkus#define IBM_EC_MASK_VOL 0xf 102147196Smarkus#define IBM_EC_MASK_MUTE (1 << 6) 103147196Smarkus#define IBM_EC_FANSTATUS 0x2F 104154326Smarkus#define IBM_EC_MASK_FANLEVEL 0x3f 105154326Smarkus#define IBM_EC_MASK_FANDISENGAGED (1 << 6) 106147196Smarkus#define IBM_EC_MASK_FANSTATUS (1 << 7) 107147196Smarkus#define IBM_EC_FANSPEED 0x84 108147196Smarkus 109147196Smarkus/* CMOS Commands */ 110147196Smarkus#define IBM_CMOS_VOLUME_DOWN 0 111147196Smarkus#define IBM_CMOS_VOLUME_UP 1 112147196Smarkus#define IBM_CMOS_VOLUME_MUTE 2 113147196Smarkus#define IBM_CMOS_BRIGHTNESS_UP 4 114147196Smarkus#define IBM_CMOS_BRIGHTNESS_DOWN 5 115147196Smarkus 116147196Smarkus/* ACPI methods */ 117147196Smarkus#define IBM_NAME_KEYLIGHT "KBLT" 118147196Smarkus#define IBM_NAME_WLAN_BT_GET "GBDC" 119147196Smarkus#define IBM_NAME_WLAN_BT_SET "SBDC" 120147196Smarkus#define IBM_NAME_MASK_BT (1 << 1) 121147196Smarkus#define IBM_NAME_MASK_WLAN (1 << 2) 122147196Smarkus#define IBM_NAME_THERMAL_GET "TMP7" 123147196Smarkus#define IBM_NAME_THERMAL_UPDT "UPDT" 124147196Smarkus 125147196Smarkus#define IBM_NAME_EVENTS_STATUS_GET "DHKC" 126147196Smarkus#define IBM_NAME_EVENTS_MASK_GET "DHKN" 127147196Smarkus#define IBM_NAME_EVENTS_STATUS_SET "MHKC" 128147196Smarkus#define IBM_NAME_EVENTS_MASK_SET "MHKM" 129147196Smarkus#define IBM_NAME_EVENTS_GET "MHKP" 130147196Smarkus#define IBM_NAME_EVENTS_AVAILMASK "MHKA" 131147196Smarkus 132237493Siwasaki/* Event Code */ 133237493Siwasaki#define IBM_EVENT_LCD_BACKLIGHT 0x03 134237493Siwasaki#define IBM_EVENT_SUSPEND_TO_RAM 0x04 135237493Siwasaki#define IBM_EVENT_BLUETOOTH 0x05 136237493Siwasaki#define IBM_EVENT_SCREEN_EXPAND 0x07 137237493Siwasaki#define IBM_EVENT_SUSPEND_TO_DISK 0x0c 138237493Siwasaki#define IBM_EVENT_BRIGHTNESS_UP 0x10 139237493Siwasaki#define IBM_EVENT_BRIGHTNESS_DOWN 0x11 140237493Siwasaki#define IBM_EVENT_THINKLIGHT 0x12 141237493Siwasaki#define IBM_EVENT_ZOOM 0x14 142237493Siwasaki#define IBM_EVENT_VOLUME_UP 0x15 143237493Siwasaki#define IBM_EVENT_VOLUME_DOWN 0x16 144237493Siwasaki#define IBM_EVENT_MUTE 0x17 145237493Siwasaki#define IBM_EVENT_ACCESS_IBM_BUTTON 0x18 146237493Siwasaki 147147196Smarkus#define ABS(x) (((x) < 0)? -(x) : (x)) 148147196Smarkus 149138627Stakawatastruct acpi_ibm_softc { 150147196Smarkus device_t dev; 151147196Smarkus ACPI_HANDLE handle; 152147196Smarkus 153147196Smarkus /* Embedded controller */ 154147196Smarkus device_t ec_dev; 155147196Smarkus ACPI_HANDLE ec_handle; 156147196Smarkus 157147196Smarkus /* CMOS */ 158147196Smarkus ACPI_HANDLE cmos_handle; 159147196Smarkus 160147196Smarkus /* Fan status */ 161147196Smarkus ACPI_HANDLE fan_handle; 162147196Smarkus int fan_levels; 163147196Smarkus 164147196Smarkus /* Keylight commands and states */ 165147196Smarkus ACPI_HANDLE light_handle; 166147196Smarkus int light_cmd_on; 167147196Smarkus int light_cmd_off; 168147196Smarkus int light_val; 169147196Smarkus int light_get_supported; 170147196Smarkus int light_set_supported; 171148710Smarkus 172148710Smarkus /* led(4) interface */ 173147196Smarkus struct cdev *led_dev; 174148710Smarkus int led_busy; 175148710Smarkus int led_state; 176147196Smarkus 177147196Smarkus int wlan_bt_flags; 178147196Smarkus int thermal_updt_supported; 179147196Smarkus 180147196Smarkus unsigned int events_availmask; 181147196Smarkus unsigned int events_initialmask; 182147196Smarkus int events_mask_supported; 183147196Smarkus int events_enable; 184147196Smarkus 185237493Siwasaki unsigned int handler_events; 186237493Siwasaki 187147196Smarkus struct sysctl_ctx_list *sysctl_ctx; 188147196Smarkus struct sysctl_oid *sysctl_tree; 189138627Stakawata}; 190138627Stakawata 191147196Smarkusstatic struct { 192147196Smarkus char *name; 193147196Smarkus int method; 194147196Smarkus char *description; 195147196Smarkus int access; 196147196Smarkus} acpi_ibm_sysctls[] = { 197147196Smarkus { 198147196Smarkus .name = "events", 199147196Smarkus .method = ACPI_IBM_METHOD_EVENTS, 200147196Smarkus .description = "ACPI events enable", 201147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 202147196Smarkus }, 203147196Smarkus { 204147196Smarkus .name = "eventmask", 205147196Smarkus .method = ACPI_IBM_METHOD_EVENTMASK, 206147196Smarkus .description = "ACPI eventmask", 207147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 208147196Smarkus }, 209147196Smarkus { 210147196Smarkus .name = "hotkey", 211147196Smarkus .method = ACPI_IBM_METHOD_HOTKEY, 212147196Smarkus .description = "Key Status", 213147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 214147196Smarkus }, 215147196Smarkus { 216147196Smarkus .name = "lcd_brightness", 217147196Smarkus .method = ACPI_IBM_METHOD_BRIGHTNESS, 218147196Smarkus .description = "LCD Brightness", 219147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 220147196Smarkus }, 221147196Smarkus { 222147196Smarkus .name = "volume", 223147196Smarkus .method = ACPI_IBM_METHOD_VOLUME, 224147196Smarkus .description = "Volume", 225147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 226147196Smarkus }, 227147196Smarkus { 228147196Smarkus .name = "mute", 229147196Smarkus .method = ACPI_IBM_METHOD_MUTE, 230147196Smarkus .description = "Mute", 231147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 232147196Smarkus }, 233147196Smarkus { 234147196Smarkus .name = "thinklight", 235147196Smarkus .method = ACPI_IBM_METHOD_THINKLIGHT, 236147196Smarkus .description = "Thinklight enable", 237147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 238147196Smarkus }, 239147196Smarkus { 240147196Smarkus .name = "bluetooth", 241147196Smarkus .method = ACPI_IBM_METHOD_BLUETOOTH, 242147196Smarkus .description = "Bluetooth enable", 243147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 244147196Smarkus }, 245147196Smarkus { 246147196Smarkus .name = "wlan", 247147196Smarkus .method = ACPI_IBM_METHOD_WLAN, 248147196Smarkus .description = "WLAN enable", 249147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 250147196Smarkus }, 251147196Smarkus { 252147196Smarkus .name = "fan_speed", 253147196Smarkus .method = ACPI_IBM_METHOD_FANSPEED, 254147196Smarkus .description = "Fan speed", 255147196Smarkus .access = CTLTYPE_INT | CTLFLAG_RD 256147196Smarkus }, 257147196Smarkus { 258154326Smarkus .name = "fan_level", 259154326Smarkus .method = ACPI_IBM_METHOD_FANLEVEL, 260154326Smarkus .description = "Fan level", 261154326Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 262154326Smarkus }, 263154326Smarkus { 264147196Smarkus .name = "fan", 265147196Smarkus .method = ACPI_IBM_METHOD_FANSTATUS, 266147196Smarkus .description = "Fan enable", 267154326Smarkus .access = CTLTYPE_INT | CTLFLAG_RW 268147196Smarkus }, 269147196Smarkus 270147196Smarkus { NULL, 0, NULL, 0 } 271147196Smarkus}; 272147196Smarkus 273147196SmarkusACPI_SERIAL_DECL(ibm, "ACPI IBM extras"); 274147196Smarkus 275138627Stakawatastatic int acpi_ibm_probe(device_t dev); 276138627Stakawatastatic int acpi_ibm_attach(device_t dev); 277138627Stakawatastatic int acpi_ibm_detach(device_t dev); 278201605Siwasakistatic int acpi_ibm_resume(device_t dev); 279138627Stakawata 280148710Smarkusstatic void ibm_led(void *softc, int onoff); 281148710Smarkusstatic void ibm_led_task(struct acpi_ibm_softc *sc, int pending __unused); 282148710Smarkus 283147196Smarkusstatic int acpi_ibm_sysctl(SYSCTL_HANDLER_ARGS); 284147196Smarkusstatic int acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method); 285147196Smarkusstatic int acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method); 286147196Smarkusstatic int acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int val); 287147196Smarkus 288147196Smarkusstatic int acpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val); 289147196Smarkusstatic int acpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS); 290237493Siwasakistatic int acpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS); 291147196Smarkusstatic void acpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context); 292147196Smarkus 293237493Siwasakistatic int acpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg); 294237493Siwasakistatic int acpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg); 295237493Siwasakistatic int acpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg); 296237493Siwasakistatic int acpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg); 297237493Siwasakistatic int acpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg); 298237493Siwasaki 299138627Stakawatastatic device_method_t acpi_ibm_methods[] = { 300138627Stakawata /* Device interface */ 301138627Stakawata DEVMETHOD(device_probe, acpi_ibm_probe), 302138627Stakawata DEVMETHOD(device_attach, acpi_ibm_attach), 303138627Stakawata DEVMETHOD(device_detach, acpi_ibm_detach), 304201605Siwasaki DEVMETHOD(device_resume, acpi_ibm_resume), 305138627Stakawata 306138627Stakawata {0, 0} 307138627Stakawata}; 308138627Stakawata 309138627Stakawatastatic driver_t acpi_ibm_driver = { 310138627Stakawata "acpi_ibm", 311138627Stakawata acpi_ibm_methods, 312138627Stakawata sizeof(struct acpi_ibm_softc), 313138627Stakawata}; 314138627Stakawata 315138627Stakawatastatic devclass_t acpi_ibm_devclass; 316138627Stakawata 317138627StakawataDRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 318138627Stakawata 0, 0); 319138627StakawataMODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); 320197350Sjhbstatic char *ibm_ids[] = {"IBM0068", NULL}; 321138627Stakawata 322147196Smarkusstatic void 323147196Smarkusibm_led(void *softc, int onoff) 324147196Smarkus{ 325148710Smarkus struct acpi_ibm_softc* sc = (struct acpi_ibm_softc*) softc; 326148710Smarkus 327148710Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 328148710Smarkus 329148710Smarkus if (sc->led_busy) 330148710Smarkus return; 331148710Smarkus 332148710Smarkus sc->led_busy = 1; 333148710Smarkus sc->led_state = onoff; 334148710Smarkus 335167814Sjkim AcpiOsExecute(OSL_NOTIFY_HANDLER, (void *)ibm_led_task, sc); 336148710Smarkus} 337148710Smarkus 338148710Smarkusstatic void 339148710Smarkusibm_led_task(struct acpi_ibm_softc *sc, int pending __unused) 340148710Smarkus{ 341148710Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 342148710Smarkus 343147196Smarkus ACPI_SERIAL_BEGIN(ibm); 344148710Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_THINKLIGHT, sc->led_state); 345147196Smarkus ACPI_SERIAL_END(ibm); 346148710Smarkus 347148710Smarkus sc->led_busy = 0; 348147196Smarkus} 349147196Smarkus 350138627Stakawatastatic int 351138627Stakawataacpi_ibm_probe(device_t dev) 352138627Stakawata{ 353147196Smarkus if (acpi_disabled("ibm") || 354147196Smarkus ACPI_ID_PROBE(device_get_parent(dev), dev, ibm_ids) == NULL || 355147196Smarkus device_get_unit(dev) != 0) 356147196Smarkus return (ENXIO); 357138627Stakawata 358147196Smarkus device_set_desc(dev, "IBM ThinkPad ACPI Extras"); 359138627Stakawata 360147196Smarkus return (0); 361138627Stakawata} 362138627Stakawata 363138627Stakawatastatic int 364147196Smarkusacpi_ibm_attach(device_t dev) 365138627Stakawata{ 366147196Smarkus struct acpi_ibm_softc *sc; 367147196Smarkus devclass_t ec_devclass; 368138627Stakawata 369138627Stakawata ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 370138627Stakawata 371138627Stakawata sc = device_get_softc(dev); 372147196Smarkus sc->dev = dev; 373147196Smarkus sc->handle = acpi_get_handle(dev); 374147196Smarkus 375147196Smarkus /* Look for the first embedded controller */ 376147196Smarkus if (!(ec_devclass = devclass_find ("acpi_ec"))) { 377147196Smarkus if (bootverbose) 378147196Smarkus device_printf(dev, "Couldn't find acpi_ec devclass\n"); 379147196Smarkus return (EINVAL); 380138627Stakawata } 381147196Smarkus if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) { 382147196Smarkus if (bootverbose) 383147196Smarkus device_printf(dev, "Couldn't find acpi_ec device\n"); 384147196Smarkus return (EINVAL); 385147196Smarkus } 386147196Smarkus sc->ec_handle = acpi_get_handle(sc->ec_dev); 387138627Stakawata 388147196Smarkus /* Get the sysctl tree */ 389147196Smarkus sc->sysctl_ctx = device_get_sysctl_ctx(dev); 390147196Smarkus sc->sysctl_tree = device_get_sysctl_tree(dev); 391138627Stakawata 392147196Smarkus /* Look for event mask and hook up the nodes */ 393147196Smarkus sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle, 394147196Smarkus IBM_NAME_EVENTS_MASK_GET, &sc->events_initialmask)); 395138627Stakawata 396147196Smarkus if (sc->events_mask_supported) { 397217323Smdf SYSCTL_ADD_UINT(sc->sysctl_ctx, 398147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 399147196Smarkus "initialmask", CTLFLAG_RD, 400147196Smarkus &sc->events_initialmask, 0, "Initial eventmask"); 401138627Stakawata 402147196Smarkus /* The availmask is the bitmask of supported events */ 403147196Smarkus if (ACPI_FAILURE(acpi_GetInteger(sc->handle, 404147196Smarkus IBM_NAME_EVENTS_AVAILMASK, &sc->events_availmask))) 405147196Smarkus sc->events_availmask = 0xffffffff; 406138627Stakawata 407217323Smdf SYSCTL_ADD_UINT(sc->sysctl_ctx, 408147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 409147196Smarkus "availmask", CTLFLAG_RD, 410147196Smarkus &sc->events_availmask, 0, "Mask of supported events"); 411147196Smarkus } 412138627Stakawata 413147196Smarkus /* Hook up proc nodes */ 414147196Smarkus for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 415147196Smarkus if (!acpi_ibm_sysctl_init(sc, acpi_ibm_sysctls[i].method)) 416147196Smarkus continue; 417147196Smarkus 418147196Smarkus SYSCTL_ADD_PROC(sc->sysctl_ctx, 419147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 420147196Smarkus acpi_ibm_sysctls[i].name, acpi_ibm_sysctls[i].access, 421147196Smarkus sc, i, acpi_ibm_sysctl, "I", 422147196Smarkus acpi_ibm_sysctls[i].description); 423138627Stakawata } 424147196Smarkus 425147196Smarkus /* Hook up thermal node */ 426147196Smarkus if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_THERMAL)) { 427147196Smarkus SYSCTL_ADD_PROC(sc->sysctl_ctx, 428147196Smarkus SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 429217566Smdf "thermal", CTLTYPE_INT | CTLFLAG_RD, 430147196Smarkus sc, 0, acpi_ibm_thermal_sysctl, "I", 431147196Smarkus "Thermal zones"); 432147196Smarkus } 433147196Smarkus 434237493Siwasaki /* Hook up handlerevents node */ 435237493Siwasaki if (acpi_ibm_sysctl_init(sc, ACPI_IBM_METHOD_HANDLEREVENTS)) { 436237493Siwasaki SYSCTL_ADD_PROC(sc->sysctl_ctx, 437237493Siwasaki SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO, 438237493Siwasaki "handlerevents", CTLTYPE_STRING | CTLFLAG_RW, 439237493Siwasaki sc, 0, acpi_ibm_handlerevents_sysctl, "I", 440237493Siwasaki "devd(8) events handled by acpi_ibm"); 441237493Siwasaki } 442237493Siwasaki 443147196Smarkus /* Handle notifies */ 444147196Smarkus AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, 445147196Smarkus acpi_ibm_notify, dev); 446147196Smarkus 447147196Smarkus /* Hook up light to led(4) */ 448147246Smarkus if (sc->light_set_supported) 449169031Smarkus sc->led_dev = led_create_state(ibm_led, sc, "thinklight", sc->light_val); 450147196Smarkus 451147196Smarkus return (0); 452138627Stakawata} 453138627Stakawata 454138627Stakawatastatic int 455138627Stakawataacpi_ibm_detach(device_t dev) 456138627Stakawata{ 457138774Sscottl ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 458138774Sscottl 459138627Stakawata struct acpi_ibm_softc *sc = device_get_softc(dev); 460138627Stakawata 461147196Smarkus /* Disable events and restore eventmask */ 462147196Smarkus ACPI_SERIAL_BEGIN(ibm); 463147196Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTS, 0); 464147196Smarkus acpi_ibm_sysctl_set(sc, ACPI_IBM_METHOD_EVENTMASK, sc->events_initialmask); 465147196Smarkus ACPI_SERIAL_END(ibm); 466147196Smarkus 467147196Smarkus AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY, acpi_ibm_notify); 468147196Smarkus 469147246Smarkus if (sc->led_dev != NULL) 470147246Smarkus led_destroy(sc->led_dev); 471147246Smarkus 472147196Smarkus return (0); 473138627Stakawata} 474147196Smarkus 475138627Stakawatastatic int 476201605Siwasakiacpi_ibm_resume(device_t dev) 477201605Siwasaki{ 478201605Siwasaki struct acpi_ibm_softc *sc = device_get_softc(dev); 479201605Siwasaki 480201605Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__); 481201605Siwasaki 482201605Siwasaki ACPI_SERIAL_BEGIN(ibm); 483201605Siwasaki for (int i = 0; acpi_ibm_sysctls[i].name != NULL; i++) { 484201605Siwasaki int val; 485201605Siwasaki 486201605Siwasaki if ((acpi_ibm_sysctls[i].access & CTLFLAG_RD) == 0) { 487201605Siwasaki continue; 488201605Siwasaki } 489201605Siwasaki 490201605Siwasaki val = acpi_ibm_sysctl_get(sc, i); 491201605Siwasaki 492201605Siwasaki if ((acpi_ibm_sysctls[i].access & CTLFLAG_WR) == 0) { 493201605Siwasaki continue; 494201605Siwasaki } 495201605Siwasaki 496201605Siwasaki acpi_ibm_sysctl_set(sc, i, val); 497201605Siwasaki } 498201605Siwasaki ACPI_SERIAL_END(ibm); 499201605Siwasaki 500201605Siwasaki return (0); 501201605Siwasaki} 502201605Siwasaki 503201605Siwasakistatic int 504147196Smarkusacpi_ibm_eventmask_set(struct acpi_ibm_softc *sc, int val) 505138627Stakawata{ 506147196Smarkus ACPI_OBJECT arg[2]; 507147196Smarkus ACPI_OBJECT_LIST args; 508147196Smarkus ACPI_STATUS status; 509147196Smarkus 510147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 511147196Smarkus ACPI_SERIAL_ASSERT(ibm); 512147196Smarkus 513147196Smarkus args.Count = 2; 514147196Smarkus args.Pointer = arg; 515147196Smarkus arg[0].Type = ACPI_TYPE_INTEGER; 516147196Smarkus arg[1].Type = ACPI_TYPE_INTEGER; 517147196Smarkus 518147196Smarkus for (int i = 0; i < 32; ++i) { 519147196Smarkus arg[0].Integer.Value = i+1; 520147196Smarkus arg[1].Integer.Value = (((1 << i) & val) != 0); 521147196Smarkus status = AcpiEvaluateObject(sc->handle, 522147196Smarkus IBM_NAME_EVENTS_MASK_SET, &args, NULL); 523147196Smarkus 524147196Smarkus if (ACPI_FAILURE(status)) 525147196Smarkus return (status); 526147196Smarkus } 527147196Smarkus 528147196Smarkus return (0); 529138627Stakawata} 530138627Stakawata 531138627Stakawatastatic int 532147196Smarkusacpi_ibm_sysctl(SYSCTL_HANDLER_ARGS) 533138627Stakawata{ 534147196Smarkus struct acpi_ibm_softc *sc; 535147196Smarkus int arg; 536147196Smarkus int error = 0; 537147196Smarkus int function; 538147196Smarkus int method; 539147196Smarkus 540147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 541147196Smarkus 542147196Smarkus sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 543147196Smarkus function = oidp->oid_arg2; 544147196Smarkus method = acpi_ibm_sysctls[function].method; 545147196Smarkus 546147196Smarkus ACPI_SERIAL_BEGIN(ibm); 547147196Smarkus arg = acpi_ibm_sysctl_get(sc, method); 548147196Smarkus error = sysctl_handle_int(oidp, &arg, 0, req); 549147196Smarkus 550147196Smarkus /* Sanity check */ 551147196Smarkus if (error != 0 || req->newptr == NULL) 552147196Smarkus goto out; 553147196Smarkus 554147196Smarkus /* Update */ 555147196Smarkus error = acpi_ibm_sysctl_set(sc, method, arg); 556147196Smarkus 557147196Smarkusout: 558147196Smarkus ACPI_SERIAL_END(ibm); 559147196Smarkus return (error); 560138627Stakawata} 561147196Smarkus 562147196Smarkusstatic int 563147196Smarkusacpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) 564138627Stakawata{ 565202771Sjkim UINT64 val_ec; 566147196Smarkus int val = 0, key; 567138627Stakawata 568147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 569147196Smarkus ACPI_SERIAL_ASSERT(ibm); 570138627Stakawata 571147196Smarkus switch (method) { 572147196Smarkus case ACPI_IBM_METHOD_EVENTS: 573147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_GET, &val); 574147196Smarkus break; 575138627Stakawata 576147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 577147196Smarkus if (sc->events_mask_supported) 578147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_EVENTS_MASK_GET, &val); 579147196Smarkus break; 580147196Smarkus 581147196Smarkus case ACPI_IBM_METHOD_HOTKEY: 582147196Smarkus /* 583147196Smarkus * Construct the hotkey as a bitmask as illustrated below. 584147196Smarkus * Note that whenever a key was pressed, the respecting bit 585147196Smarkus * toggles and nothing else changes. 586147196Smarkus * +--+--+-+-+-+-+-+-+-+-+-+-+ 587147196Smarkus * |11|10|9|8|7|6|5|4|3|2|1|0| 588147196Smarkus * +--+--+-+-+-+-+-+-+-+-+-+-+ 589147196Smarkus * | | | | | | | | | | | | 590147196Smarkus * | | | | | | | | | | | +- Home Button 591147196Smarkus * | | | | | | | | | | +--- Search Button 592147196Smarkus * | | | | | | | | | +----- Mail Button 593147196Smarkus * | | | | | | | | +------- Thinkpad Button 594147196Smarkus * | | | | | | | +--------- Zoom (Fn + Space) 595147196Smarkus * | | | | | | +----------- WLAN Button 596147196Smarkus * | | | | | +------------- Video Button 597147196Smarkus * | | | | +--------------- Hibernate Button 598147196Smarkus * | | | +----------------- Thinklight Button 599147196Smarkus * | | +------------------- Screen expand (Fn + F8) 600147196Smarkus * | +--------------------- Brightness 601147196Smarkus * +------------------------ Volume/Mute 602147196Smarkus */ 603147196Smarkus key = rtcin(IBM_RTC_HOTKEY1); 604147196Smarkus val = (IBM_RTC_MASK_HOME | IBM_RTC_MASK_SEARCH | IBM_RTC_MASK_MAIL | IBM_RTC_MASK_WLAN) & key; 605147196Smarkus key = rtcin(IBM_RTC_HOTKEY2); 606147196Smarkus val |= (IBM_RTC_MASK_THINKPAD | IBM_RTC_MASK_VIDEO | IBM_RTC_MASK_HIBERNATE) & key; 607147196Smarkus val |= (IBM_RTC_MASK_ZOOM & key) >> 1; 608147196Smarkus key = rtcin(IBM_RTC_THINKLIGHT); 609147196Smarkus val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 610147196Smarkus key = rtcin(IBM_RTC_SCREENEXPAND); 611147196Smarkus val |= (IBM_RTC_MASK_THINKLIGHT & key) << 4; 612147196Smarkus key = rtcin(IBM_RTC_BRIGHTNESS); 613147196Smarkus val |= (IBM_RTC_MASK_BRIGHTNESS & key) << 5; 614147196Smarkus key = rtcin(IBM_RTC_VOLUME); 615147196Smarkus val |= (IBM_RTC_MASK_VOLUME & key) << 4; 616147196Smarkus break; 617147196Smarkus 618147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 619147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 620147196Smarkus val = val_ec & IBM_EC_MASK_BRI; 621147196Smarkus break; 622147196Smarkus 623147196Smarkus case ACPI_IBM_METHOD_VOLUME: 624147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 625147246Smarkus val = val_ec & IBM_EC_MASK_VOL; 626147196Smarkus break; 627147196Smarkus 628147196Smarkus case ACPI_IBM_METHOD_MUTE: 629147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 630147196Smarkus val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 631147196Smarkus break; 632147196Smarkus 633147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 634147196Smarkus if (sc->light_get_supported) 635147196Smarkus acpi_GetInteger(sc->ec_handle, IBM_NAME_KEYLIGHT, &val); 636147196Smarkus else 637147196Smarkus val = sc->light_val; 638147196Smarkus break; 639147196Smarkus 640147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 641147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 642147196Smarkus sc->wlan_bt_flags = val; 643147196Smarkus val = ((val & IBM_NAME_MASK_BT) != 0); 644147196Smarkus break; 645147196Smarkus 646147196Smarkus case ACPI_IBM_METHOD_WLAN: 647147196Smarkus acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &val); 648147196Smarkus sc->wlan_bt_flags = val; 649147196Smarkus val = ((val & IBM_NAME_MASK_WLAN) != 0); 650147196Smarkus break; 651147196Smarkus 652147196Smarkus case ACPI_IBM_METHOD_FANSPEED: 653147196Smarkus if (sc->fan_handle) { 654147196Smarkus if(ACPI_FAILURE(acpi_GetInteger(sc->fan_handle, NULL, &val))) 655147196Smarkus val = -1; 656138627Stakawata } 657147196Smarkus else { 658147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSPEED, &val_ec, 2); 659147196Smarkus val = val_ec; 660147196Smarkus } 661147196Smarkus break; 662138627Stakawata 663154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 664154326Smarkus /* 665154326Smarkus * The IBM_EC_FANSTATUS register works as follows: 666154326Smarkus * Bit 0-5 indicate the level at which the fan operates. Only 667154326Smarkus * values between 0 and 7 have an effect. Everything 668154326Smarkus * above 7 is treated the same as level 7 669154326Smarkus * Bit 6 overrides the fan speed limit if set to 1 670154326Smarkus * Bit 7 indicates at which mode the fan operates: 671154326Smarkus * manual (0) or automatic (1) 672154326Smarkus */ 673154326Smarkus if (!sc->fan_handle) { 674154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 675154326Smarkus val = val_ec & IBM_EC_MASK_FANLEVEL; 676154326Smarkus } 677154326Smarkus break; 678154326Smarkus 679147196Smarkus case ACPI_IBM_METHOD_FANSTATUS: 680147196Smarkus if (!sc->fan_handle) { 681147196Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 682147196Smarkus val = (val_ec & IBM_EC_MASK_FANSTATUS) == IBM_EC_MASK_FANSTATUS; 683138627Stakawata } 684147196Smarkus else 685147196Smarkus val = -1; 686147196Smarkus break; 687138627Stakawata } 688147196Smarkus 689147196Smarkus return (val); 690138627Stakawata} 691138627Stakawata 692138627Stakawatastatic int 693147196Smarkusacpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) 694138627Stakawata{ 695237493Siwasaki int val; 696202771Sjkim UINT64 val_ec; 697147196Smarkus ACPI_STATUS status; 698138627Stakawata 699147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 700147196Smarkus ACPI_SERIAL_ASSERT(ibm); 701138627Stakawata 702147196Smarkus switch (method) { 703147196Smarkus case ACPI_IBM_METHOD_EVENTS: 704147196Smarkus if (arg < 0 || arg > 1) 705147196Smarkus return (EINVAL); 706138627Stakawata 707147196Smarkus status = acpi_SetInteger(sc->handle, IBM_NAME_EVENTS_STATUS_SET, arg); 708147196Smarkus if (ACPI_FAILURE(status)) 709147196Smarkus return (status); 710147196Smarkus if (sc->events_mask_supported) 711147196Smarkus return acpi_ibm_eventmask_set(sc, sc->events_availmask); 712147196Smarkus break; 713138627Stakawata 714147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 715147196Smarkus if (sc->events_mask_supported) 716147196Smarkus return acpi_ibm_eventmask_set(sc, arg); 717147196Smarkus break; 718138627Stakawata 719147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 720237493Siwasaki return acpi_ibm_brightness_set(sc, arg); 721147196Smarkus break; 722138627Stakawata 723147196Smarkus case ACPI_IBM_METHOD_VOLUME: 724237493Siwasaki return acpi_ibm_volume_set(sc, arg); 725147196Smarkus break; 726138627Stakawata 727147196Smarkus case ACPI_IBM_METHOD_MUTE: 728237493Siwasaki return acpi_ibm_mute_set(sc, arg); 729147196Smarkus break; 730147196Smarkus 731147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 732237493Siwasaki return acpi_ibm_thinklight_set(sc, arg); 733147196Smarkus break; 734147196Smarkus 735147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 736237493Siwasaki return acpi_ibm_bluetooth_set(sc, arg); 737147196Smarkus break; 738154326Smarkus 739154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 740154326Smarkus if (arg < 0 || arg > 7) 741154326Smarkus return (EINVAL); 742154326Smarkus 743154326Smarkus if (!sc->fan_handle) { 744154326Smarkus /* Read the current fanstatus */ 745154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 746154326Smarkus val = val_ec & (~IBM_EC_MASK_FANLEVEL); 747154326Smarkus 748154326Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, val | arg, 1); 749154326Smarkus } 750154326Smarkus break; 751154326Smarkus 752154326Smarkus case ACPI_IBM_METHOD_FANSTATUS: 753154326Smarkus if (arg < 0 || arg > 1) 754154326Smarkus return (EINVAL); 755154326Smarkus 756154326Smarkus if (!sc->fan_handle) { 757154326Smarkus /* Read the current fanstatus */ 758154326Smarkus ACPI_EC_READ(sc->ec_dev, IBM_EC_FANSTATUS, &val_ec, 1); 759154326Smarkus 760154326Smarkus return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_FANSTATUS, 761154326Smarkus (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); 762154326Smarkus } 763154326Smarkus break; 764147196Smarkus } 765147196Smarkus 766147196Smarkus return (0); 767138627Stakawata} 768138627Stakawata 769147196Smarkusstatic int 770147196Smarkusacpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) 771138627Stakawata{ 772147196Smarkus int dummy; 773147196Smarkus ACPI_OBJECT_TYPE cmos_t; 774147196Smarkus ACPI_HANDLE ledb_handle; 775138627Stakawata 776147196Smarkus switch (method) { 777147196Smarkus case ACPI_IBM_METHOD_EVENTS: 778147196Smarkus /* Events are disabled by default */ 779147196Smarkus return (TRUE); 780138627Stakawata 781147196Smarkus case ACPI_IBM_METHOD_EVENTMASK: 782147196Smarkus return (sc->events_mask_supported); 783147196Smarkus 784147196Smarkus case ACPI_IBM_METHOD_HOTKEY: 785147196Smarkus case ACPI_IBM_METHOD_BRIGHTNESS: 786147196Smarkus case ACPI_IBM_METHOD_VOLUME: 787147196Smarkus case ACPI_IBM_METHOD_MUTE: 788147196Smarkus /* EC is required here, which was aready checked before */ 789147196Smarkus return (TRUE); 790147196Smarkus 791147196Smarkus case ACPI_IBM_METHOD_THINKLIGHT: 792147196Smarkus sc->cmos_handle = NULL; 793169031Smarkus sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger( 794169031Smarkus sc->ec_handle, IBM_NAME_KEYLIGHT, &sc->light_val)); 795147196Smarkus 796147196Smarkus if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS", &sc->light_handle)) || 797147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS", &sc->light_handle)) || 798147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS", &sc->light_handle))) && 799147196Smarkus ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) && 800147196Smarkus cmos_t == ACPI_TYPE_METHOD) { 801147196Smarkus sc->light_cmd_on = 0x0c; 802147196Smarkus sc->light_cmd_off = 0x0d; 803147196Smarkus sc->cmos_handle = sc->light_handle; 804147196Smarkus } 805147196Smarkus else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT", &sc->light_handle))) { 806147196Smarkus sc->light_cmd_on = 1; 807147196Smarkus sc->light_cmd_off = 0; 808147196Smarkus } 809147196Smarkus else 810147196Smarkus sc->light_handle = NULL; 811147196Smarkus 812147196Smarkus sc->light_set_supported = (sc->light_handle && 813147196Smarkus ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB", &ledb_handle))); 814147196Smarkus 815169031Smarkus if (sc->light_get_supported) 816169031Smarkus return (TRUE); 817169031Smarkus 818169031Smarkus if (sc->light_set_supported) { 819147196Smarkus sc->light_val = 0; 820147196Smarkus return (TRUE); 821147196Smarkus } 822147196Smarkus 823169031Smarkus return (FALSE); 824169031Smarkus 825147196Smarkus case ACPI_IBM_METHOD_BLUETOOTH: 826147196Smarkus case ACPI_IBM_METHOD_WLAN: 827147196Smarkus if (ACPI_SUCCESS(acpi_GetInteger(sc->handle, IBM_NAME_WLAN_BT_GET, &dummy))) 828147196Smarkus return (TRUE); 829147196Smarkus return (FALSE); 830147196Smarkus 831147196Smarkus case ACPI_IBM_METHOD_FANSPEED: 832147196Smarkus /* 833147196Smarkus * Some models report the fan speed in levels from 0-7 834147196Smarkus * Newer models report it contiguously 835147196Smarkus */ 836147196Smarkus sc->fan_levels = 837147196Smarkus (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN", &sc->fan_handle)) || 838147196Smarkus ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD", &sc->fan_handle))); 839147196Smarkus return (TRUE); 840147196Smarkus 841154326Smarkus case ACPI_IBM_METHOD_FANLEVEL: 842147196Smarkus case ACPI_IBM_METHOD_FANSTATUS: 843147196Smarkus /* 844147196Smarkus * Fan status is only supported on those models, 845147196Smarkus * which report fan RPM contiguously, not in levels 846147196Smarkus */ 847147196Smarkus if (sc->fan_levels) 848147196Smarkus return (FALSE); 849147196Smarkus return (TRUE); 850147196Smarkus 851147196Smarkus case ACPI_IBM_METHOD_THERMAL: 852147196Smarkus if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_GET, &dummy))) { 853147196Smarkus sc->thermal_updt_supported = ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle, IBM_NAME_THERMAL_UPDT, &dummy)); 854147196Smarkus return (TRUE); 855147196Smarkus } 856147196Smarkus return (FALSE); 857237493Siwasaki 858237493Siwasaki case ACPI_IBM_METHOD_HANDLEREVENTS: 859237493Siwasaki return (TRUE); 860147196Smarkus } 861147196Smarkus return (FALSE); 862138627Stakawata} 863138627Stakawata 864138627Stakawatastatic int 865147196Smarkusacpi_ibm_thermal_sysctl(SYSCTL_HANDLER_ARGS) 866138627Stakawata{ 867147196Smarkus struct acpi_ibm_softc *sc; 868147196Smarkus int error = 0; 869147196Smarkus char temp_cmd[] = "TMP0"; 870147196Smarkus int temp[8]; 871138627Stakawata 872147196Smarkus ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 873138627Stakawata 874147196Smarkus sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 875138627Stakawata 876147196Smarkus ACPI_SERIAL_BEGIN(ibm); 877138627Stakawata 878147196Smarkus for (int i = 0; i < 8; ++i) { 879147196Smarkus temp_cmd[3] = '0' + i; 880147196Smarkus 881147196Smarkus /* 882147196Smarkus * The TMPx methods seem to return +/- 128 or 0 883147196Smarkus * when the respecting sensor is not available 884147196Smarkus */ 885147196Smarkus if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd, 886147196Smarkus &temp[i])) || ABS(temp[i]) == 128 || temp[i] == 0) 887147196Smarkus temp[i] = -1; 888147196Smarkus else if (sc->thermal_updt_supported) 889147196Smarkus /* Temperature is reported in tenth of Kelvin */ 890147196Smarkus temp[i] = (temp[i] - 2732 + 5) / 10; 891147196Smarkus } 892147196Smarkus 893147196Smarkus error = sysctl_handle_opaque(oidp, &temp, 8*sizeof(int), req); 894147196Smarkus 895147196Smarkus ACPI_SERIAL_END(ibm); 896147196Smarkus return (error); 897138627Stakawata} 898138627Stakawata 899237493Siwasakistatic int 900237493Siwasakiacpi_ibm_handlerevents_sysctl(SYSCTL_HANDLER_ARGS) 901237493Siwasaki{ 902237493Siwasaki struct acpi_ibm_softc *sc; 903237493Siwasaki int error = 0; 904237493Siwasaki struct sbuf sb; 905237493Siwasaki char *cp, *ep; 906237493Siwasaki int l, val; 907237493Siwasaki unsigned int handler_events; 908237493Siwasaki 909237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 910237493Siwasaki 911237493Siwasaki sc = (struct acpi_ibm_softc *)oidp->oid_arg1; 912237493Siwasaki 913237493Siwasaki if (sbuf_new(&sb, NULL, 128, SBUF_AUTOEXTEND) == NULL) 914237493Siwasaki return (ENOMEM); 915237493Siwasaki 916237493Siwasaki ACPI_SERIAL_BEGIN(ibm); 917237493Siwasaki 918237493Siwasaki /* Get old values if this is a get request. */ 919237493Siwasaki if (req->newptr == NULL) { 920237493Siwasaki for (int i = 0; i < 8 * sizeof(sc->handler_events); i++) 921237493Siwasaki if (sc->handler_events & (1 << i)) 922237493Siwasaki sbuf_printf(&sb, "0x%02x ", i + 1); 923237493Siwasaki if (sbuf_len(&sb) == 0) 924237493Siwasaki sbuf_printf(&sb, "NONE"); 925237493Siwasaki } 926237493Siwasaki 927237493Siwasaki sbuf_trim(&sb); 928237493Siwasaki sbuf_finish(&sb); 929237493Siwasaki 930237493Siwasaki /* Copy out the old values to the user. */ 931237493Siwasaki error = SYSCTL_OUT(req, sbuf_data(&sb), sbuf_len(&sb)); 932237493Siwasaki sbuf_delete(&sb); 933237493Siwasaki 934237493Siwasaki if (error != 0 || req->newptr == NULL) 935237493Siwasaki goto out; 936237493Siwasaki 937237493Siwasaki /* If the user is setting a string, parse it. */ 938237493Siwasaki handler_events = 0; 939237493Siwasaki cp = (char *)req->newptr; 940237493Siwasaki while (*cp) { 941237493Siwasaki if (isspace(*cp)) { 942237493Siwasaki cp++; 943237493Siwasaki continue; 944237493Siwasaki } 945237493Siwasaki 946237493Siwasaki ep = cp; 947237493Siwasaki 948237493Siwasaki while (*ep && !isspace(*ep)) 949237493Siwasaki ep++; 950237493Siwasaki 951237493Siwasaki l = ep - cp; 952237493Siwasaki if (l == 0) 953237493Siwasaki break; 954237493Siwasaki 955237493Siwasaki if (strncmp(cp, "NONE", 4) == 0) { 956237493Siwasaki cp = ep; 957237493Siwasaki continue; 958237493Siwasaki } 959237493Siwasaki 960237493Siwasaki if (l >= 3 && cp[0] == '0' && (cp[1] == 'X' || cp[1] == 'x')) 961237493Siwasaki val = strtoul(cp, &ep, 16); 962237493Siwasaki else 963237493Siwasaki val = strtoul(cp, &ep, 10); 964237493Siwasaki 965237493Siwasaki if (val == 0 || ep == cp || val >= 8 * sizeof(handler_events)) { 966237493Siwasaki cp[l] = '\0'; 967237493Siwasaki device_printf(sc->dev, "invalid event code: %s\n", cp); 968237493Siwasaki error = EINVAL; 969237493Siwasaki goto out; 970237493Siwasaki } 971237493Siwasaki 972237493Siwasaki handler_events |= 1 << (val - 1); 973237493Siwasaki 974237493Siwasaki cp = ep; 975237493Siwasaki } 976237493Siwasaki 977237493Siwasaki sc->handler_events = handler_events; 978237493Siwasakiout: 979237493Siwasaki ACPI_SERIAL_END(ibm); 980237493Siwasaki return (error); 981237493Siwasaki} 982237493Siwasaki 983237493Siwasakistatic int 984237493Siwasakiacpi_ibm_brightness_set(struct acpi_ibm_softc *sc, int arg) 985237493Siwasaki{ 986237493Siwasaki int val, step; 987237493Siwasaki UINT64 val_ec; 988237493Siwasaki ACPI_OBJECT Arg; 989237493Siwasaki ACPI_OBJECT_LIST Args; 990237493Siwasaki ACPI_STATUS status; 991237493Siwasaki 992237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 993237493Siwasaki ACPI_SERIAL_ASSERT(ibm); 994237493Siwasaki 995237493Siwasaki if (arg < 0 || arg > 7) 996237493Siwasaki return (EINVAL); 997237493Siwasaki 998237493Siwasaki /* Read the current brightness */ 999237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, &val_ec, 1); 1000237493Siwasaki if (ACPI_FAILURE(status)) 1001237493Siwasaki return (status); 1002237493Siwasaki 1003237493Siwasaki if (sc->cmos_handle) { 1004237493Siwasaki val = val_ec & IBM_EC_MASK_BRI; 1005237493Siwasaki 1006237493Siwasaki Args.Count = 1; 1007237493Siwasaki Args.Pointer = &Arg; 1008237493Siwasaki Arg.Type = ACPI_TYPE_INTEGER; 1009237493Siwasaki Arg.Integer.Value = (arg > val) ? IBM_CMOS_BRIGHTNESS_UP : 1010237493Siwasaki IBM_CMOS_BRIGHTNESS_DOWN; 1011237493Siwasaki 1012237493Siwasaki step = (arg > val) ? 1 : -1; 1013237493Siwasaki for (int i = val; i != arg; i += step) { 1014237493Siwasaki status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1015237493Siwasaki &Args, NULL); 1016237493Siwasaki if (ACPI_FAILURE(status)) { 1017237493Siwasaki /* Record the last value */ 1018237493Siwasaki if (i != val) { 1019237493Siwasaki ACPI_EC_WRITE(sc->ec_dev, 1020237493Siwasaki IBM_EC_BRIGHTNESS, i - step, 1); 1021237493Siwasaki } 1022237493Siwasaki return (status); 1023237493Siwasaki } 1024237493Siwasaki } 1025237493Siwasaki } 1026237493Siwasaki 1027237493Siwasaki return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_BRIGHTNESS, arg, 1); 1028237493Siwasaki} 1029237493Siwasaki 1030237493Siwasakistatic int 1031237493Siwasakiacpi_ibm_bluetooth_set(struct acpi_ibm_softc *sc, int arg) 1032237493Siwasaki{ 1033237493Siwasaki int val; 1034237493Siwasaki 1035237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1036237493Siwasaki ACPI_SERIAL_ASSERT(ibm); 1037237493Siwasaki 1038237493Siwasaki if (arg < 0 || arg > 1) 1039237493Siwasaki return (EINVAL); 1040237493Siwasaki 1041237493Siwasaki val = (arg == 1) ? sc->wlan_bt_flags | IBM_NAME_MASK_BT : 1042237493Siwasaki sc->wlan_bt_flags & (~IBM_NAME_MASK_BT); 1043237493Siwasaki return acpi_SetInteger(sc->handle, IBM_NAME_WLAN_BT_SET, val); 1044237493Siwasaki} 1045237493Siwasaki 1046237493Siwasakistatic int 1047237493Siwasakiacpi_ibm_thinklight_set(struct acpi_ibm_softc *sc, int arg) 1048237493Siwasaki{ 1049237493Siwasaki ACPI_OBJECT Arg; 1050237493Siwasaki ACPI_OBJECT_LIST Args; 1051237493Siwasaki ACPI_STATUS status; 1052237493Siwasaki 1053237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1054237493Siwasaki ACPI_SERIAL_ASSERT(ibm); 1055237493Siwasaki 1056237493Siwasaki if (arg < 0 || arg > 1) 1057237493Siwasaki return (EINVAL); 1058237493Siwasaki 1059237493Siwasaki if (sc->light_set_supported) { 1060237493Siwasaki Args.Count = 1; 1061237493Siwasaki Args.Pointer = &Arg; 1062237493Siwasaki Arg.Type = ACPI_TYPE_INTEGER; 1063237493Siwasaki Arg.Integer.Value = arg ? sc->light_cmd_on : sc->light_cmd_off; 1064237493Siwasaki 1065237493Siwasaki status = AcpiEvaluateObject(sc->light_handle, NULL, 1066237493Siwasaki &Args, NULL); 1067237493Siwasaki if (ACPI_SUCCESS(status)) 1068237493Siwasaki sc->light_val = arg; 1069237493Siwasaki return (status); 1070237493Siwasaki } 1071237493Siwasaki 1072237493Siwasaki return (0); 1073237493Siwasaki} 1074237493Siwasaki 1075237493Siwasakistatic int 1076237493Siwasakiacpi_ibm_volume_set(struct acpi_ibm_softc *sc, int arg) 1077237493Siwasaki{ 1078237493Siwasaki int val, step; 1079237493Siwasaki UINT64 val_ec; 1080237493Siwasaki ACPI_OBJECT Arg; 1081237493Siwasaki ACPI_OBJECT_LIST Args; 1082237493Siwasaki ACPI_STATUS status; 1083237493Siwasaki 1084237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1085237493Siwasaki ACPI_SERIAL_ASSERT(ibm); 1086237493Siwasaki 1087237493Siwasaki if (arg < 0 || arg > 14) 1088237493Siwasaki return (EINVAL); 1089237493Siwasaki 1090237493Siwasaki /* Read the current volume */ 1091237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1092237493Siwasaki if (ACPI_FAILURE(status)) 1093237493Siwasaki return (status); 1094237493Siwasaki 1095237493Siwasaki if (sc->cmos_handle) { 1096237493Siwasaki val = val_ec & IBM_EC_MASK_VOL; 1097237493Siwasaki 1098237493Siwasaki Args.Count = 1; 1099237493Siwasaki Args.Pointer = &Arg; 1100237493Siwasaki Arg.Type = ACPI_TYPE_INTEGER; 1101237493Siwasaki Arg.Integer.Value = (arg > val) ? IBM_CMOS_VOLUME_UP : 1102237493Siwasaki IBM_CMOS_VOLUME_DOWN; 1103237493Siwasaki 1104237493Siwasaki step = (arg > val) ? 1 : -1; 1105237493Siwasaki for (int i = val; i != arg; i += step) { 1106237493Siwasaki status = AcpiEvaluateObject(sc->cmos_handle, NULL, 1107237493Siwasaki &Args, NULL); 1108237493Siwasaki if (ACPI_FAILURE(status)) { 1109237493Siwasaki /* Record the last value */ 1110237493Siwasaki if (i != val) { 1111237493Siwasaki val_ec = i - step + 1112237493Siwasaki (val_ec & (~IBM_EC_MASK_VOL)); 1113237493Siwasaki ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, 1114237493Siwasaki val_ec, 1); 1115237493Siwasaki } 1116237493Siwasaki return (status); 1117237493Siwasaki } 1118237493Siwasaki } 1119237493Siwasaki } 1120237493Siwasaki 1121237493Siwasaki val_ec = arg + (val_ec & (~IBM_EC_MASK_VOL)); 1122237493Siwasaki return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1123237493Siwasaki} 1124237493Siwasaki 1125237493Siwasakistatic int 1126237493Siwasakiacpi_ibm_mute_set(struct acpi_ibm_softc *sc, int arg) 1127237493Siwasaki{ 1128237493Siwasaki UINT64 val_ec; 1129237493Siwasaki ACPI_OBJECT Arg; 1130237493Siwasaki ACPI_OBJECT_LIST Args; 1131237493Siwasaki ACPI_STATUS status; 1132237493Siwasaki 1133237493Siwasaki ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 1134237493Siwasaki ACPI_SERIAL_ASSERT(ibm); 1135237493Siwasaki 1136237493Siwasaki if (arg < 0 || arg > 1) 1137237493Siwasaki return (EINVAL); 1138237493Siwasaki 1139237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1140237493Siwasaki if (ACPI_FAILURE(status)) 1141237493Siwasaki return (status); 1142237493Siwasaki 1143237493Siwasaki if (sc->cmos_handle) { 1144237493Siwasaki Args.Count = 1; 1145237493Siwasaki Args.Pointer = &Arg; 1146237493Siwasaki Arg.Type = ACPI_TYPE_INTEGER; 1147237493Siwasaki Arg.Integer.Value = IBM_CMOS_VOLUME_MUTE; 1148237493Siwasaki 1149237493Siwasaki status = AcpiEvaluateObject(sc->cmos_handle, NULL, &Args, NULL); 1150237493Siwasaki if (ACPI_FAILURE(status)) 1151237493Siwasaki return (status); 1152237493Siwasaki } 1153237493Siwasaki 1154237493Siwasaki val_ec = (arg == 1) ? val_ec | IBM_EC_MASK_MUTE : 1155237493Siwasaki val_ec & (~IBM_EC_MASK_MUTE); 1156237493Siwasaki return ACPI_EC_WRITE(sc->ec_dev, IBM_EC_VOLUME, val_ec, 1); 1157237493Siwasaki} 1158237493Siwasaki 1159147196Smarkusstatic void 1160237493Siwasakiacpi_ibm_eventhandler(struct acpi_ibm_softc *sc, int arg) 1161237493Siwasaki{ 1162237493Siwasaki int val; 1163237493Siwasaki UINT64 val_ec; 1164237493Siwasaki ACPI_STATUS status; 1165237493Siwasaki 1166237493Siwasaki ACPI_SERIAL_BEGIN(ibm); 1167237493Siwasaki switch (arg) { 1168237493Siwasaki case IBM_EVENT_SUSPEND_TO_RAM: 1169237493Siwasaki power_pm_suspend(POWER_SLEEP_STATE_SUSPEND); 1170237493Siwasaki break; 1171237493Siwasaki 1172237493Siwasaki case IBM_EVENT_BLUETOOTH: 1173237493Siwasaki acpi_ibm_bluetooth_set(sc, (sc->wlan_bt_flags == 0)); 1174237493Siwasaki break; 1175237493Siwasaki 1176237493Siwasaki case IBM_EVENT_BRIGHTNESS_UP: 1177237493Siwasaki case IBM_EVENT_BRIGHTNESS_DOWN: 1178237493Siwasaki /* Read the current brightness */ 1179237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_BRIGHTNESS, 1180237493Siwasaki &val_ec, 1); 1181237493Siwasaki if (ACPI_FAILURE(status)) 1182237493Siwasaki return; 1183237493Siwasaki 1184237493Siwasaki val = val_ec & IBM_EC_MASK_BRI; 1185237493Siwasaki val = (arg == IBM_EVENT_BRIGHTNESS_UP) ? val + 1 : val - 1; 1186237493Siwasaki acpi_ibm_brightness_set(sc, val); 1187237493Siwasaki break; 1188237493Siwasaki 1189237493Siwasaki case IBM_EVENT_THINKLIGHT: 1190237493Siwasaki acpi_ibm_thinklight_set(sc, (sc->light_val == 0)); 1191237493Siwasaki break; 1192237493Siwasaki 1193237493Siwasaki case IBM_EVENT_VOLUME_UP: 1194237493Siwasaki case IBM_EVENT_VOLUME_DOWN: 1195237493Siwasaki /* Read the current volume */ 1196237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1197237493Siwasaki if (ACPI_FAILURE(status)) 1198237493Siwasaki return; 1199237493Siwasaki 1200237493Siwasaki val = val_ec & IBM_EC_MASK_VOL; 1201237493Siwasaki val = (arg == IBM_EVENT_VOLUME_UP) ? val + 1 : val - 1; 1202237493Siwasaki acpi_ibm_volume_set(sc, val); 1203237493Siwasaki break; 1204237493Siwasaki 1205237493Siwasaki case IBM_EVENT_MUTE: 1206237493Siwasaki /* Read the current value */ 1207237493Siwasaki status = ACPI_EC_READ(sc->ec_dev, IBM_EC_VOLUME, &val_ec, 1); 1208237493Siwasaki if (ACPI_FAILURE(status)) 1209237493Siwasaki return; 1210237493Siwasaki 1211237493Siwasaki val = ((val_ec & IBM_EC_MASK_MUTE) == IBM_EC_MASK_MUTE); 1212237493Siwasaki acpi_ibm_mute_set(sc, (val == 0)); 1213237493Siwasaki break; 1214237493Siwasaki 1215237493Siwasaki default: 1216237493Siwasaki break; 1217237493Siwasaki } 1218237493Siwasaki ACPI_SERIAL_END(ibm); 1219237493Siwasaki} 1220237493Siwasaki 1221237493Siwasakistatic void 1222147196Smarkusacpi_ibm_notify(ACPI_HANDLE h, UINT32 notify, void *context) 1223138627Stakawata{ 1224147196Smarkus int event, arg, type; 1225147196Smarkus device_t dev = context; 1226138627Stakawata struct acpi_ibm_softc *sc = device_get_softc(dev); 1227138627Stakawata 1228147196Smarkus ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify); 1229138627Stakawata 1230147196Smarkus if (notify != 0x80) 1231161289Stakawata device_printf(dev, "Unknown notify\n"); 1232138627Stakawata 1233147196Smarkus for (;;) { 1234147196Smarkus acpi_GetInteger(acpi_get_handle(dev), IBM_NAME_EVENTS_GET, &event); 1235138627Stakawata 1236147196Smarkus if (event == 0) 1237147196Smarkus break; 1238147196Smarkus 1239147196Smarkus 1240147196Smarkus type = (event >> 12) & 0xf; 1241147196Smarkus arg = event & 0xfff; 1242147196Smarkus switch (type) { 1243147196Smarkus case 1: 1244147196Smarkus if (!(sc->events_availmask & (1 << (arg - 1)))) { 1245161289Stakawata device_printf(dev, "Unknown key %d\n", arg); 1246147196Smarkus break; 1247147196Smarkus } 1248147196Smarkus 1249237493Siwasaki /* Execute event handler */ 1250237493Siwasaki if (sc->handler_events & (1 << (arg - 1))) 1251237493Siwasaki acpi_ibm_eventhandler(sc, (arg & 0xff)); 1252237493Siwasaki 1253147196Smarkus /* Notify devd(8) */ 1254147196Smarkus acpi_UserNotify("IBM", h, (arg & 0xff)); 1255147196Smarkus break; 1256147196Smarkus default: 1257147196Smarkus break; 1258147196Smarkus } 1259147196Smarkus } 1260138627Stakawata} 1261