2 * Copyright (c) 2004 Takanori Watanabe
3 * Copyright (c) 2005 Markus Brueffer <markus@FreeBSD.org>
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * $FreeBSD: src/sys/dev/acpi_support/acpi_ibm.c,v 1.15 2007/10/25 17:30:18 jhb Exp $
28 * $DragonFly: src/sys/dev/acpica5/acpi_thinkpad/acpi_thinkpad.c,v 1.2 2008/10/03 00:47:36 hasso Exp $
32 * Driver for extra ACPI-controlled gadgets found on IBM and Lenovo ThinkPad
33 * laptops. Inspired by the ibm-acpi and tpb projects which implement these
36 * acpi-ibm: <http://ibm-acpi.sourceforge.net/>
37 * tpb: <http://www.nongnu.org/tpb/>
41 #include <sys/param.h>
42 #include <sys/kernel.h>
44 #include <machine/cpufunc.h>
45 #include <sys/module.h>
46 #include <sys/sensors.h>
47 #include <sys/sysctl.h>
49 #include <sys/thread2.h>
50 #include <machine/clock.h>
56 #define _COMPONENT ACPI_OEM
57 ACPI_MODULE_NAME("THINKPAD")
59 /* Internal methods */
60 #define ACPI_THINKPAD_METHOD_EVENTS 1
61 #define ACPI_THINKPAD_METHOD_EVENTMASK 2
62 #define ACPI_THINKPAD_METHOD_HOTKEY 3
63 #define ACPI_THINKPAD_METHOD_BRIGHTNESS 4
64 #define ACPI_THINKPAD_METHOD_VOLUME 5
65 #define ACPI_THINKPAD_METHOD_MUTE 6
66 #define ACPI_THINKPAD_METHOD_THINKLIGHT 7
67 #define ACPI_THINKPAD_METHOD_BLUETOOTH 8
68 #define ACPI_THINKPAD_METHOD_WLAN 9
69 #define ACPI_THINKPAD_METHOD_FANSPEED 10
70 #define ACPI_THINKPAD_METHOD_FANLEVEL 11
71 #define ACPI_THINKPAD_METHOD_FANSTATUS 12
72 #define ACPI_THINKPAD_METHOD_THERMAL 13
75 #define THINKPAD_RTC_HOTKEY1 0x64
76 #define THINKPAD_RTC_MASK_HOME (1 << 0)
77 #define THINKPAD_RTC_MASK_SEARCH (1 << 1)
78 #define THINKPAD_RTC_MASK_MAIL (1 << 2)
79 #define THINKPAD_RTC_MASK_WLAN (1 << 5)
80 #define THINKPAD_RTC_HOTKEY2 0x65
81 #define THINKPAD_RTC_MASK_THINKPAD (1 << 3)
82 #define THINKPAD_RTC_MASK_ZOOM (1 << 5)
83 #define THINKPAD_RTC_MASK_VIDEO (1 << 6)
84 #define THINKPAD_RTC_MASK_HIBERNATE (1 << 7)
85 #define THINKPAD_RTC_THINKLIGHT 0x66
86 #define THINKPAD_RTC_MASK_THINKLIGHT (1 << 4)
87 #define THINKPAD_RTC_SCREENEXPAND 0x67
88 #define THINKPAD_RTC_MASK_SCREENEXPAND (1 << 5)
89 #define THINKPAD_RTC_BRIGHTNESS 0x6c
90 #define THINKPAD_RTC_MASK_BRIGHTNESS (1 << 5)
91 #define THINKPAD_RTC_VOLUME 0x6e
92 #define THINKPAD_RTC_MASK_VOLUME (1 << 7)
94 /* Embedded Controller registers */
95 #define THINKPAD_EC_BRIGHTNESS 0x31
96 #define THINKPAD_EC_MASK_BRI 0x7
97 #define THINKPAD_EC_VOLUME 0x30
98 #define THINKPAD_EC_MASK_VOL 0xf
99 #define THINKPAD_EC_MASK_MUTE (1 << 6)
100 #define THINKPAD_EC_FANSTATUS 0x2F
101 #define THINKPAD_EC_MASK_FANLEVEL 0x3f
102 #define THINKPAD_EC_MASK_FANDISENGAGED (1 << 6)
103 #define THINKPAD_EC_MASK_FANSTATUS (1 << 7)
104 #define THINKPAD_EC_FANSPEED 0x84
107 #define THINKPAD_CMOS_VOLUME_DOWN 0
108 #define THINKPAD_CMOS_VOLUME_UP 1
109 #define THINKPAD_CMOS_VOLUME_MUTE 2
110 #define THINKPAD_CMOS_BRIGHTNESS_UP 4
111 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 5
114 #define THINKPAD_NAME_KEYLIGHT "KBLT"
115 #define THINKPAD_NAME_WLAN_BT_GET "GBDC"
116 #define THINKPAD_NAME_WLAN_BT_SET "SBDC"
117 #define THINKPAD_NAME_MASK_BT (1 << 1)
118 #define THINKPAD_NAME_MASK_WLAN (1 << 2)
119 #define THINKPAD_NAME_THERMAL_GET "TMP7"
120 #define THINKPAD_NAME_THERMAL_UPDT "UPDT"
122 #define THINKPAD_NAME_EVENTS_STATUS_GET "DHKC"
123 #define THINKPAD_NAME_EVENTS_MASK_GET "DHKN"
124 #define THINKPAD_NAME_EVENTS_STATUS_SET "MHKC"
125 #define THINKPAD_NAME_EVENTS_MASK_SET "MHKM"
126 #define THINKPAD_NAME_EVENTS_GET "MHKP"
127 #define THINKPAD_NAME_EVENTS_AVAILMASK "MHKA"
129 #define THINKPAD_NUM_SENSORS 9
130 #define THINKPAD_TEMP_SENSORS 8
132 #define ABS(x) (((x) < 0)? -(x) : (x))
134 struct acpi_thinkpad_softc {
138 /* Embedded controller */
140 ACPI_HANDLE ec_handle;
143 ACPI_HANDLE cmos_handle;
146 ACPI_HANDLE fan_handle;
149 /* Keylight commands and states */
150 ACPI_HANDLE light_handle;
154 int light_get_supported;
155 int light_set_supported;
157 /* led(4) interface */
158 struct cdev *led_dev;
163 int thermal_updt_supported;
165 unsigned int events_availmask;
166 unsigned int events_initialmask;
167 int events_mask_supported;
170 /* sensors(9) related */
171 struct ksensordev sensordev;
172 struct ksensor sensors[THINKPAD_NUM_SENSORS];
174 struct sysctl_ctx_list sysctl_ctx;
175 struct sysctl_oid *sysctl_tree;
183 } acpi_thinkpad_sysctls[] = {
186 .method = ACPI_THINKPAD_METHOD_EVENTS,
187 .description = "ACPI events enable",
188 .access = CTLTYPE_INT | CTLFLAG_RW
192 .method = ACPI_THINKPAD_METHOD_EVENTMASK,
193 .description = "ACPI eventmask",
194 .access = CTLTYPE_INT | CTLFLAG_RW
198 .method = ACPI_THINKPAD_METHOD_HOTKEY,
199 .description = "Key Status",
200 .access = CTLTYPE_INT | CTLFLAG_RD
203 .name = "lcd_brightness",
204 .method = ACPI_THINKPAD_METHOD_BRIGHTNESS,
205 .description = "LCD Brightness",
206 .access = CTLTYPE_INT | CTLFLAG_RW
210 .method = ACPI_THINKPAD_METHOD_VOLUME,
211 .description = "Volume",
212 .access = CTLTYPE_INT | CTLFLAG_RW
216 .method = ACPI_THINKPAD_METHOD_MUTE,
217 .description = "Mute",
218 .access = CTLTYPE_INT | CTLFLAG_RW
221 .name = "thinklight",
222 .method = ACPI_THINKPAD_METHOD_THINKLIGHT,
223 .description = "Thinklight enable",
224 .access = CTLTYPE_INT | CTLFLAG_RW
228 .method = ACPI_THINKPAD_METHOD_BLUETOOTH,
229 .description = "Bluetooth enable",
230 .access = CTLTYPE_INT | CTLFLAG_RW
234 .method = ACPI_THINKPAD_METHOD_WLAN,
235 .description = "WLAN enable",
236 .access = CTLTYPE_INT | CTLFLAG_RD
240 .method = ACPI_THINKPAD_METHOD_FANLEVEL,
241 .description = "Fan level",
242 .access = CTLTYPE_INT | CTLFLAG_RW
246 .method = ACPI_THINKPAD_METHOD_FANSTATUS,
247 .description = "Fan enable",
248 .access = CTLTYPE_INT | CTLFLAG_RW
254 static struct lock tplock;
256 static int acpi_thinkpad_probe(device_t dev);
257 static int acpi_thinkpad_attach(device_t dev);
258 static int acpi_thinkpad_detach(device_t dev);
260 static int acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS);
261 static int acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc,
263 static int acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc,
265 static int acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc,
266 int method, int val);
268 static int acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc,
270 static void acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify,
272 static void acpi_thinkpad_refresh(void *);
274 static device_method_t acpi_thinkpad_methods[] = {
275 /* Device interface */
276 DEVMETHOD(device_probe, acpi_thinkpad_probe),
277 DEVMETHOD(device_attach, acpi_thinkpad_attach),
278 DEVMETHOD(device_detach, acpi_thinkpad_detach),
282 static driver_t acpi_thinkpad_driver = {
284 acpi_thinkpad_methods,
285 sizeof(struct acpi_thinkpad_softc),
288 static devclass_t acpi_thinkpad_devclass;
290 DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver,
291 acpi_thinkpad_devclass, 0, 0);
292 MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1);
293 static char *thinkpad_ids[] = {"IBM0068", NULL};
296 acpi_thinkpad_probe(device_t dev)
298 if (acpi_disabled("thinkpad") ||
299 ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL ||
300 device_get_unit(dev) != 0)
303 device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras");
308 acpi_thinkpad_attach(device_t dev)
310 struct acpi_thinkpad_softc *sc;
311 struct acpi_softc *acpi_sc;
312 devclass_t ec_devclass;
315 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
317 sc = device_get_softc(dev);
319 sc->handle = acpi_get_handle(dev);
321 acpi_sc = acpi_device_get_parent_softc(dev);
323 /* Look for the first embedded controller */
324 if (!(ec_devclass = devclass_find ("acpi_ec"))) {
326 device_printf(dev, "Couldn't find acpi_ec devclass\n");
329 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
331 device_printf(dev, "Couldn't find acpi_ec device\n");
334 sc->ec_handle = acpi_get_handle(sc->ec_dev);
336 lockinit(&tplock, "thinkpad", 0, 0);
337 lockmgr(&tplock, LK_EXCLUSIVE);
339 sysctl_ctx_init(&sc->sysctl_ctx);
340 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
341 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
342 "thinkpad", CTLFLAG_RD, 0, "");
344 /* Look for event mask and hook up the nodes */
345 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
346 THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
348 if (sc->events_mask_supported) {
349 SYSCTL_ADD_INT(&sc->sysctl_ctx,
350 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
351 "initialmask", CTLFLAG_RD,
352 &sc->events_initialmask, 0, "Initial eventmask");
354 /* The availmask is the bitmask of supported events */
355 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
356 THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
357 sc->events_availmask = 0xffffffff;
359 SYSCTL_ADD_INT(&sc->sysctl_ctx,
360 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
361 "availmask", CTLFLAG_RD,
362 &sc->events_availmask, 0, "Mask of supported events");
365 /* Hook up proc nodes */
366 for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) {
367 if (!acpi_thinkpad_sysctl_init(sc,
368 acpi_thinkpad_sysctls[i].method))
371 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
372 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
373 acpi_thinkpad_sysctls[i].name,
374 acpi_thinkpad_sysctls[i].access,
375 sc, i, acpi_thinkpad_sysctl, "I",
376 acpi_thinkpad_sysctls[i].description);
379 lockmgr(&tplock, LK_RELEASE);
381 /* Handle notifies */
382 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
383 acpi_thinkpad_notify, dev);
385 /* Attach sensors(9). */
386 if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) {
387 device_printf(sc->dev, "unable to register update task\n");
391 strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev),
392 sizeof(sc->sensordev.xname));
394 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
395 sc->sensors[i].type = SENSOR_TEMP;
396 sensor_attach(&sc->sensordev, &sc->sensors[i]);
399 sc->sensors[i].type = SENSOR_FANRPM;
400 sensor_attach(&sc->sensordev, &sc->sensors[i]);
402 sensordev_install(&sc->sensordev);
408 acpi_thinkpad_detach(device_t dev)
412 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
414 struct acpi_thinkpad_softc *sc = device_get_softc(dev);
416 /* Disable events and restore eventmask */
417 lockmgr(&tplock, LK_EXCLUSIVE);
418 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0);
419 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK,
420 sc->events_initialmask);
421 lockmgr(&tplock, LK_RELEASE);
423 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
424 acpi_thinkpad_notify);
426 if (sc->sysctl_tree != NULL)
427 sysctl_ctx_free(&sc->sysctl_ctx);
429 sensordev_deinstall(&sc->sensordev);
430 for (i = 0; i < THINKPAD_NUM_SENSORS; i++)
431 sensor_detach(&sc->sensordev, &sc->sensors[i]);
432 sensor_task_unregister(sc);
438 acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val)
442 ACPI_OBJECT_LIST args;
445 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
446 KKASSERT(lockstatus(&tplock, curthread) != 0);
450 arg[0].Type = ACPI_TYPE_INTEGER;
451 arg[1].Type = ACPI_TYPE_INTEGER;
453 for (i = 0; i < 32; ++i) {
454 arg[0].Integer.Value = i+1;
455 arg[1].Integer.Value = (((1 << i) & val) != 0);
456 status = AcpiEvaluateObject(sc->handle,
457 THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL);
459 if (ACPI_FAILURE(status))
467 acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS)
469 struct acpi_thinkpad_softc *sc;
475 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
477 sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1;
478 function = oidp->oid_arg2;
479 method = acpi_thinkpad_sysctls[function].method;
481 lockmgr(&tplock, LK_EXCLUSIVE);
482 arg = acpi_thinkpad_sysctl_get(sc, method);
483 error = sysctl_handle_int(oidp, &arg, 0, req);
486 if (error != 0 || req->newptr == NULL)
490 error = acpi_thinkpad_sysctl_set(sc, method, arg);
493 lockmgr(&tplock, LK_RELEASE);
498 acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method)
503 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
504 KKASSERT(lockstatus(&tplock, curthread) != 0);
507 case ACPI_THINKPAD_METHOD_EVENTS:
508 acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET,
512 case ACPI_THINKPAD_METHOD_EVENTMASK:
513 if (sc->events_mask_supported)
514 acpi_GetInteger(sc->handle,
515 THINKPAD_NAME_EVENTS_MASK_GET, &val);
518 case ACPI_THINKPAD_METHOD_HOTKEY:
520 * Construct the hotkey as a bitmask as illustrated below.
521 * Note that whenever a key was pressed, the respecting bit
522 * toggles and nothing else changes.
523 * +--+--+-+-+-+-+-+-+-+-+-+-+
524 * |11|10|9|8|7|6|5|4|3|2|1|0|
525 * +--+--+-+-+-+-+-+-+-+-+-+-+
526 * | | | | | | | | | | | |
527 * | | | | | | | | | | | +- Home Button
528 * | | | | | | | | | | +--- Search Button
529 * | | | | | | | | | +----- Mail Button
530 * | | | | | | | | +------- Thinkpad Button
531 * | | | | | | | +--------- Zoom (Fn + Space)
532 * | | | | | | +----------- WLAN Button
533 * | | | | | +------------- Video Button
534 * | | | | +--------------- Hibernate Button
535 * | | | +----------------- Thinklight Button
536 * | | +------------------- Screen expand (Fn + F8)
537 * | +--------------------- Brightness
538 * +------------------------ Volume/Mute
540 key = rtcin(THINKPAD_RTC_HOTKEY1);
541 val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH |
542 THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key;
543 key = rtcin(THINKPAD_RTC_HOTKEY2);
544 val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO |
545 THINKPAD_RTC_MASK_HIBERNATE) & key;
546 val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1;
547 key = rtcin(THINKPAD_RTC_THINKLIGHT);
548 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
549 key = rtcin(THINKPAD_RTC_SCREENEXPAND);
550 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
551 key = rtcin(THINKPAD_RTC_BRIGHTNESS);
552 val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5;
553 key = rtcin(THINKPAD_RTC_VOLUME);
554 val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4;
557 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
558 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
559 val = val_ec & THINKPAD_EC_MASK_BRI;
562 case ACPI_THINKPAD_METHOD_VOLUME:
563 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
564 val = val_ec & THINKPAD_EC_MASK_VOL;
567 case ACPI_THINKPAD_METHOD_MUTE:
568 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
569 val = ((val_ec & THINKPAD_EC_MASK_MUTE) ==
570 THINKPAD_EC_MASK_MUTE);
573 case ACPI_THINKPAD_METHOD_THINKLIGHT:
574 if (sc->light_get_supported)
575 acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT,
581 case ACPI_THINKPAD_METHOD_BLUETOOTH:
582 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
583 sc->wlan_bt_flags = val;
584 val = ((val & THINKPAD_NAME_MASK_BT) != 0);
587 case ACPI_THINKPAD_METHOD_WLAN:
588 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
589 sc->wlan_bt_flags = val;
590 val = ((val & THINKPAD_NAME_MASK_WLAN) != 0);
593 case ACPI_THINKPAD_METHOD_FANSPEED:
594 if (sc->fan_handle) {
595 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
600 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED,
606 case ACPI_THINKPAD_METHOD_FANLEVEL:
608 * The THINKPAD_EC_FANSTATUS register works as follows:
609 * Bit 0-5 indicate the level at which the fan operates. Only
610 * values between 0 and 7 have an effect. Everything
611 * above 7 is treated the same as level 7
612 * Bit 6 overrides the fan speed limit if set to 1
613 * Bit 7 indicates at which mode the fan operates:
614 * manual (0) or automatic (1)
616 if (!sc->fan_handle) {
617 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
619 val = val_ec & THINKPAD_EC_MASK_FANLEVEL;
623 case ACPI_THINKPAD_METHOD_FANSTATUS:
624 if (!sc->fan_handle) {
625 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
627 val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) ==
628 THINKPAD_EC_MASK_FANSTATUS;
639 acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg)
644 ACPI_OBJECT_LIST Args;
647 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
648 KKASSERT(lockstatus(&tplock, curthread) != 0);
651 case ACPI_THINKPAD_METHOD_EVENTS:
652 if (arg < 0 || arg > 1)
655 status = acpi_SetInteger(sc->handle,
656 THINKPAD_NAME_EVENTS_STATUS_SET, arg);
657 if (ACPI_FAILURE(status))
659 if (sc->events_mask_supported)
660 return acpi_thinkpad_eventmask_set(sc,
661 sc->events_availmask);
664 case ACPI_THINKPAD_METHOD_EVENTMASK:
665 if (sc->events_mask_supported)
666 return acpi_thinkpad_eventmask_set(sc, arg);
669 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
670 if (arg < 0 || arg > 7)
673 if (sc->cmos_handle) {
674 /* Read the current brightness */
675 status = ACPI_EC_READ(sc->ec_dev,
676 THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
677 if (ACPI_FAILURE(status))
679 val = val_ec & THINKPAD_EC_MASK_BRI;
683 Arg.Type = ACPI_TYPE_INTEGER;
684 Arg.Integer.Value = (arg > val) ?
685 THINKPAD_CMOS_BRIGHTNESS_UP :
686 THINKPAD_CMOS_BRIGHTNESS_DOWN;
688 step = (arg > val) ? 1 : -1;
689 for (i = val; i != arg; i += step) {
690 status = AcpiEvaluateObject(sc->cmos_handle,
692 if (ACPI_FAILURE(status))
696 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS,
700 case ACPI_THINKPAD_METHOD_VOLUME:
701 if (arg < 0 || arg > 14)
704 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
706 if (ACPI_FAILURE(status))
709 if (sc->cmos_handle) {
710 val = val_ec & THINKPAD_EC_MASK_VOL;
714 Arg.Type = ACPI_TYPE_INTEGER;
715 Arg.Integer.Value = (arg > val) ?
716 THINKPAD_CMOS_VOLUME_UP : THINKPAD_CMOS_VOLUME_DOWN;
718 step = (arg > val) ? 1 : -1;
719 for (i = val; i != arg; i += step) {
720 status = AcpiEvaluateObject(sc->cmos_handle,
722 if (ACPI_FAILURE(status))
726 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, arg +
727 (val_ec & (~THINKPAD_EC_MASK_VOL)), 1);
730 case ACPI_THINKPAD_METHOD_MUTE:
731 if (arg < 0 || arg > 1)
734 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
736 if (ACPI_FAILURE(status))
739 if (sc->cmos_handle) {
740 val = val_ec & THINKPAD_EC_MASK_VOL;
744 Arg.Type = ACPI_TYPE_INTEGER;
745 Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE;
747 status = AcpiEvaluateObject(sc->cmos_handle, NULL,
749 if (ACPI_FAILURE(status))
752 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, (arg==1) ?
753 val_ec | THINKPAD_EC_MASK_MUTE :
754 val_ec & (~THINKPAD_EC_MASK_MUTE), 1);
757 case ACPI_THINKPAD_METHOD_THINKLIGHT:
758 if (arg < 0 || arg > 1)
761 if (sc->light_set_supported) {
764 Arg.Type = ACPI_TYPE_INTEGER;
765 Arg.Integer.Value = arg ?
766 sc->light_cmd_on : sc->light_cmd_off;
768 status = AcpiEvaluateObject(sc->light_handle, NULL,
770 if (ACPI_SUCCESS(status))
776 case ACPI_THINKPAD_METHOD_BLUETOOTH:
777 if (arg < 0 || arg > 1)
780 val = (arg == 1) ? sc->wlan_bt_flags |
781 THINKPAD_NAME_MASK_BT :
782 sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT);
783 return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET,
787 case ACPI_THINKPAD_METHOD_FANLEVEL:
788 if (arg < 0 || arg > 7)
791 if (!sc->fan_handle) {
792 /* Read the current fanstatus */
793 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
795 val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL);
797 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
802 case ACPI_THINKPAD_METHOD_FANSTATUS:
803 if (arg < 0 || arg > 1)
806 if (!sc->fan_handle) {
807 /* Read the current fanstatus */
808 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
811 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
812 (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) :
813 (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1);
822 acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method)
825 ACPI_OBJECT_TYPE cmos_t;
826 ACPI_HANDLE ledb_handle;
829 case ACPI_THINKPAD_METHOD_EVENTS:
830 /* Events are disabled by default */
833 case ACPI_THINKPAD_METHOD_EVENTMASK:
834 return (sc->events_mask_supported);
836 case ACPI_THINKPAD_METHOD_HOTKEY:
837 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
838 case ACPI_THINKPAD_METHOD_VOLUME:
839 case ACPI_THINKPAD_METHOD_MUTE:
840 /* EC is required here, which was already checked before */
843 case ACPI_THINKPAD_METHOD_THINKLIGHT:
844 sc->cmos_handle = NULL;
845 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
846 sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val));
848 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS",
849 &sc->light_handle)) ||
850 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS",
851 &sc->light_handle)) ||
852 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS",
853 &sc->light_handle))) &&
854 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
855 cmos_t == ACPI_TYPE_METHOD) {
856 sc->light_cmd_on = 0x0c;
857 sc->light_cmd_off = 0x0d;
858 sc->cmos_handle = sc->light_handle;
860 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT",
861 &sc->light_handle))) {
862 sc->light_cmd_on = 1;
863 sc->light_cmd_off = 0;
866 sc->light_handle = NULL;
868 sc->light_set_supported = (sc->light_handle &&
869 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB",
872 if (sc->light_get_supported)
875 if (sc->light_set_supported) {
882 case ACPI_THINKPAD_METHOD_BLUETOOTH:
883 case ACPI_THINKPAD_METHOD_WLAN:
884 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle,
885 THINKPAD_NAME_WLAN_BT_GET, &dummy)))
889 case ACPI_THINKPAD_METHOD_FANSPEED:
891 * Some models report the fan speed in levels from 0-7
892 * Newer models report it contiguously
894 sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN",
896 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD",
900 case ACPI_THINKPAD_METHOD_FANLEVEL:
901 case ACPI_THINKPAD_METHOD_FANSTATUS:
903 * Fan status is only supported on those models,
904 * which report fan RPM contiguously, not in levels
910 case ACPI_THINKPAD_METHOD_THERMAL:
911 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
912 THINKPAD_NAME_THERMAL_GET, &dummy))) {
913 sc->thermal_updt_supported =
914 ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
915 THINKPAD_NAME_THERMAL_UPDT, &dummy));
924 acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context)
926 int event, arg, type;
927 device_t dev = context;
928 struct acpi_thinkpad_softc *sc = device_get_softc(dev);
930 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
933 device_printf(dev, "Unknown notify\n");
936 acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET,
942 type = (event >> 12) & 0xf;
946 if (!(sc->events_availmask & (1 << (arg - 1)))) {
947 device_printf(dev, "Unknown key %d\n", arg);
952 acpi_UserNotify("THINKPAD", h, (arg & 0xff));
961 acpi_thinkpad_refresh(void *arg)
963 struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg;
964 char temp_cmd[] = "TMP0";
968 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
969 temp_cmd[3] = '0' + i;
972 * The TMPx methods seem to return +/- 128 or 0
973 * when the respecting sensor is not available
975 sc->sensors[i].flags &= ~SENSOR_FINVALID;
976 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
977 &data)) || ABS(data) == 128 || data == 0) {
978 sc->sensors[i].flags |= SENSOR_FINVALID;
981 else if (sc->thermal_updt_supported) {
982 /* Temperature is reported in tenth of Kelvin */
983 sc->sensors[i].value = data * 100000;
985 sc->sensors[i].value = data * 1000000 + 273150000;
988 sc->sensors[i].flags &= ~SENSOR_FINVALID;
989 if (sc->fan_handle) {
990 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
992 sc->sensors[i].flags |= SENSOR_FINVALID;
996 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2);
1000 sc->sensors[i].value = data;