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>
57 #define _COMPONENT ACPI_OEM
58 ACPI_MODULE_NAME("THINKPAD")
60 /* Internal methods */
61 #define ACPI_THINKPAD_METHOD_EVENTS 1
62 #define ACPI_THINKPAD_METHOD_EVENTMASK 2
63 #define ACPI_THINKPAD_METHOD_HOTKEY 3
64 #define ACPI_THINKPAD_METHOD_BRIGHTNESS 4
65 #define ACPI_THINKPAD_METHOD_VOLUME 5
66 #define ACPI_THINKPAD_METHOD_MUTE 6
67 #define ACPI_THINKPAD_METHOD_THINKLIGHT 7
68 #define ACPI_THINKPAD_METHOD_BLUETOOTH 8
69 #define ACPI_THINKPAD_METHOD_WLAN 9
70 #define ACPI_THINKPAD_METHOD_FANSPEED 10
71 #define ACPI_THINKPAD_METHOD_FANLEVEL 11
72 #define ACPI_THINKPAD_METHOD_FANSTATUS 12
73 #define ACPI_THINKPAD_METHOD_THERMAL 13
76 #define THINKPAD_RTC_HOTKEY1 0x64
77 #define THINKPAD_RTC_MASK_HOME (1 << 0)
78 #define THINKPAD_RTC_MASK_SEARCH (1 << 1)
79 #define THINKPAD_RTC_MASK_MAIL (1 << 2)
80 #define THINKPAD_RTC_MASK_WLAN (1 << 5)
81 #define THINKPAD_RTC_HOTKEY2 0x65
82 #define THINKPAD_RTC_MASK_THINKPAD (1 << 3)
83 #define THINKPAD_RTC_MASK_ZOOM (1 << 5)
84 #define THINKPAD_RTC_MASK_VIDEO (1 << 6)
85 #define THINKPAD_RTC_MASK_HIBERNATE (1 << 7)
86 #define THINKPAD_RTC_THINKLIGHT 0x66
87 #define THINKPAD_RTC_MASK_THINKLIGHT (1 << 4)
88 #define THINKPAD_RTC_SCREENEXPAND 0x67
89 #define THINKPAD_RTC_MASK_SCREENEXPAND (1 << 5)
90 #define THINKPAD_RTC_BRIGHTNESS 0x6c
91 #define THINKPAD_RTC_MASK_BRIGHTNESS (1 << 5)
92 #define THINKPAD_RTC_VOLUME 0x6e
93 #define THINKPAD_RTC_MASK_VOLUME (1 << 7)
95 /* Embedded Controller registers */
96 #define THINKPAD_EC_BRIGHTNESS 0x31
97 #define THINKPAD_EC_MASK_BRI 0x7
98 #define THINKPAD_EC_VOLUME 0x30
99 #define THINKPAD_EC_MASK_VOL 0xf
100 #define THINKPAD_EC_MASK_MUTE (1 << 6)
101 #define THINKPAD_EC_FANSTATUS 0x2F
102 #define THINKPAD_EC_MASK_FANLEVEL 0x3f
103 #define THINKPAD_EC_MASK_FANDISENGAGED (1 << 6)
104 #define THINKPAD_EC_MASK_FANSTATUS (1 << 7)
105 #define THINKPAD_EC_FANSPEED 0x84
108 #define THINKPAD_CMOS_VOLUME_DOWN 0
109 #define THINKPAD_CMOS_VOLUME_UP 1
110 #define THINKPAD_CMOS_VOLUME_MUTE 2
111 #define THINKPAD_CMOS_BRIGHTNESS_UP 4
112 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 5
115 #define THINKPAD_NAME_KEYLIGHT "KBLT"
116 #define THINKPAD_NAME_WLAN_BT_GET "GBDC"
117 #define THINKPAD_NAME_WLAN_BT_SET "SBDC"
118 #define THINKPAD_NAME_MASK_BT (1 << 1)
119 #define THINKPAD_NAME_MASK_WLAN (1 << 2)
120 #define THINKPAD_NAME_THERMAL_GET "TMP7"
121 #define THINKPAD_NAME_THERMAL_UPDT "UPDT"
123 #define THINKPAD_NAME_EVENTS_STATUS_GET "DHKC"
124 #define THINKPAD_NAME_EVENTS_MASK_GET "DHKN"
125 #define THINKPAD_NAME_EVENTS_STATUS_SET "MHKC"
126 #define THINKPAD_NAME_EVENTS_MASK_SET "MHKM"
127 #define THINKPAD_NAME_EVENTS_GET "MHKP"
128 #define THINKPAD_NAME_EVENTS_AVAILMASK "MHKA"
130 #define THINKPAD_NUM_SENSORS 9
131 #define THINKPAD_TEMP_SENSORS 8
133 #define ABS(x) (((x) < 0)? -(x) : (x))
135 struct acpi_thinkpad_softc {
139 /* Embedded controller */
141 ACPI_HANDLE ec_handle;
144 ACPI_HANDLE cmos_handle;
147 ACPI_HANDLE fan_handle;
150 /* Keylight commands and states */
151 ACPI_HANDLE light_handle;
155 int light_get_supported;
156 int light_set_supported;
158 /* led(4) interface */
159 struct cdev *led_dev;
164 int thermal_updt_supported;
166 unsigned int events_availmask;
167 unsigned int events_initialmask;
168 int events_mask_supported;
171 /* sensors(9) related */
172 struct ksensordev sensordev;
173 struct ksensor sensors[THINKPAD_NUM_SENSORS];
175 struct sysctl_ctx_list sysctl_ctx;
176 struct sysctl_oid *sysctl_tree;
184 } acpi_thinkpad_sysctls[] = {
187 .method = ACPI_THINKPAD_METHOD_EVENTS,
188 .description = "ACPI events enable",
189 .access = CTLTYPE_INT | CTLFLAG_RW
193 .method = ACPI_THINKPAD_METHOD_EVENTMASK,
194 .description = "ACPI eventmask",
195 .access = CTLTYPE_INT | CTLFLAG_RW
199 .method = ACPI_THINKPAD_METHOD_HOTKEY,
200 .description = "Key Status",
201 .access = CTLTYPE_INT | CTLFLAG_RD
204 .name = "lcd_brightness",
205 .method = ACPI_THINKPAD_METHOD_BRIGHTNESS,
206 .description = "LCD Brightness",
207 .access = CTLTYPE_INT | CTLFLAG_RW
211 .method = ACPI_THINKPAD_METHOD_VOLUME,
212 .description = "Volume",
213 .access = CTLTYPE_INT | CTLFLAG_RW
217 .method = ACPI_THINKPAD_METHOD_MUTE,
218 .description = "Mute",
219 .access = CTLTYPE_INT | CTLFLAG_RW
222 .name = "thinklight",
223 .method = ACPI_THINKPAD_METHOD_THINKLIGHT,
224 .description = "Thinklight enable",
225 .access = CTLTYPE_INT | CTLFLAG_RW
229 .method = ACPI_THINKPAD_METHOD_BLUETOOTH,
230 .description = "Bluetooth enable",
231 .access = CTLTYPE_INT | CTLFLAG_RW
235 .method = ACPI_THINKPAD_METHOD_WLAN,
236 .description = "WLAN enable",
237 .access = CTLTYPE_INT | CTLFLAG_RD
241 .method = ACPI_THINKPAD_METHOD_FANLEVEL,
242 .description = "Fan level",
243 .access = CTLTYPE_INT | CTLFLAG_RW
247 .method = ACPI_THINKPAD_METHOD_FANSTATUS,
248 .description = "Fan enable",
249 .access = CTLTYPE_INT | CTLFLAG_RW
255 static struct lock tplock;
257 static int acpi_thinkpad_probe(device_t dev);
258 static int acpi_thinkpad_attach(device_t dev);
259 static int acpi_thinkpad_detach(device_t dev);
261 static int acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS);
262 static int acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc,
264 static int acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc,
266 static int acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc,
267 int method, int val);
269 static int acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc,
271 static void acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify,
273 static void acpi_thinkpad_refresh(void *);
275 static device_method_t acpi_thinkpad_methods[] = {
276 /* Device interface */
277 DEVMETHOD(device_probe, acpi_thinkpad_probe),
278 DEVMETHOD(device_attach, acpi_thinkpad_attach),
279 DEVMETHOD(device_detach, acpi_thinkpad_detach),
283 static driver_t acpi_thinkpad_driver = {
285 acpi_thinkpad_methods,
286 sizeof(struct acpi_thinkpad_softc),
289 static devclass_t acpi_thinkpad_devclass;
291 DRIVER_MODULE(acpi_thinkpad, acpi, acpi_thinkpad_driver,
292 acpi_thinkpad_devclass, 0, 0);
293 MODULE_DEPEND(acpi_thinkpad, acpi, 1, 1, 1);
294 static char *thinkpad_ids[] = {"IBM0068", NULL};
297 acpi_thinkpad_probe(device_t dev)
299 if (acpi_disabled("thinkpad") ||
300 ACPI_ID_PROBE(device_get_parent(dev), dev, thinkpad_ids) == NULL ||
301 device_get_unit(dev) != 0)
304 device_set_desc(dev, "IBM/Lenovo ThinkPad ACPI Extras");
309 acpi_thinkpad_attach(device_t dev)
311 struct acpi_thinkpad_softc *sc;
312 struct acpi_softc *acpi_sc;
313 devclass_t ec_devclass;
316 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
318 sc = device_get_softc(dev);
320 sc->handle = acpi_get_handle(dev);
322 acpi_sc = acpi_device_get_parent_softc(dev);
324 /* Look for the first embedded controller */
325 if (!(ec_devclass = devclass_find ("acpi_ec"))) {
327 device_printf(dev, "Couldn't find acpi_ec devclass\n");
330 if (!(sc->ec_dev = devclass_get_device(ec_devclass, 0))) {
332 device_printf(dev, "Couldn't find acpi_ec device\n");
335 sc->ec_handle = acpi_get_handle(sc->ec_dev);
337 lockinit(&tplock, "thinkpad", 0, 0);
338 lockmgr(&tplock, LK_EXCLUSIVE);
340 sysctl_ctx_init(&sc->sysctl_ctx);
341 sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
342 SYSCTL_CHILDREN(acpi_sc->acpi_sysctl_tree), OID_AUTO,
343 "thinkpad", CTLFLAG_RD, 0, "");
345 /* Look for event mask and hook up the nodes */
346 sc->events_mask_supported = ACPI_SUCCESS(acpi_GetInteger(sc->handle,
347 THINKPAD_NAME_EVENTS_MASK_GET, &sc->events_initialmask));
349 if (sc->events_mask_supported) {
350 SYSCTL_ADD_INT(&sc->sysctl_ctx,
351 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
352 "initialmask", CTLFLAG_RD,
353 &sc->events_initialmask, 0, "Initial eventmask");
355 /* The availmask is the bitmask of supported events */
356 if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
357 THINKPAD_NAME_EVENTS_AVAILMASK, &sc->events_availmask)))
358 sc->events_availmask = 0xffffffff;
360 SYSCTL_ADD_INT(&sc->sysctl_ctx,
361 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
362 "availmask", CTLFLAG_RD,
363 &sc->events_availmask, 0, "Mask of supported events");
366 /* Hook up proc nodes */
367 for (i = 0; acpi_thinkpad_sysctls[i].name != NULL; i++) {
368 if (!acpi_thinkpad_sysctl_init(sc,
369 acpi_thinkpad_sysctls[i].method))
372 SYSCTL_ADD_PROC(&sc->sysctl_ctx,
373 SYSCTL_CHILDREN(sc->sysctl_tree), OID_AUTO,
374 acpi_thinkpad_sysctls[i].name,
375 acpi_thinkpad_sysctls[i].access,
376 sc, i, acpi_thinkpad_sysctl, "I",
377 acpi_thinkpad_sysctls[i].description);
380 lockmgr(&tplock, LK_RELEASE);
382 /* Handle notifies */
383 AcpiInstallNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
384 acpi_thinkpad_notify, dev);
386 /* Attach sensors(9). */
387 if (sensor_task_register(sc, acpi_thinkpad_refresh, 5)) {
388 device_printf(sc->dev, "unable to register update task\n");
392 strlcpy(sc->sensordev.xname, device_get_nameunit(sc->dev),
393 sizeof(sc->sensordev.xname));
395 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
396 sc->sensors[i].type = SENSOR_TEMP;
397 sensor_attach(&sc->sensordev, &sc->sensors[i]);
400 sc->sensors[i].type = SENSOR_FANRPM;
401 sensor_attach(&sc->sensordev, &sc->sensors[i]);
403 sensordev_install(&sc->sensordev);
409 acpi_thinkpad_detach(device_t dev)
413 ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
415 struct acpi_thinkpad_softc *sc = device_get_softc(dev);
417 /* Disable events and restore eventmask */
418 lockmgr(&tplock, LK_EXCLUSIVE);
419 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTS, 0);
420 acpi_thinkpad_sysctl_set(sc, ACPI_THINKPAD_METHOD_EVENTMASK,
421 sc->events_initialmask);
422 lockmgr(&tplock, LK_RELEASE);
424 AcpiRemoveNotifyHandler(sc->handle, ACPI_DEVICE_NOTIFY,
425 acpi_thinkpad_notify);
427 if (sc->sysctl_tree != NULL)
428 sysctl_ctx_free(&sc->sysctl_ctx);
430 sensordev_deinstall(&sc->sensordev);
431 for (i = 0; i < THINKPAD_NUM_SENSORS; i++)
432 sensor_detach(&sc->sensordev, &sc->sensors[i]);
433 sensor_task_unregister(sc);
439 acpi_thinkpad_eventmask_set(struct acpi_thinkpad_softc *sc, int val)
443 ACPI_OBJECT_LIST args;
446 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
447 KKASSERT(lockstatus(&tplock, curthread) != 0);
451 arg[0].Type = ACPI_TYPE_INTEGER;
452 arg[1].Type = ACPI_TYPE_INTEGER;
454 for (i = 0; i < 32; ++i) {
455 arg[0].Integer.Value = i+1;
456 arg[1].Integer.Value = (((1 << i) & val) != 0);
457 status = AcpiEvaluateObject(sc->handle,
458 THINKPAD_NAME_EVENTS_MASK_SET, &args, NULL);
460 if (ACPI_FAILURE(status))
468 acpi_thinkpad_sysctl(SYSCTL_HANDLER_ARGS)
470 struct acpi_thinkpad_softc *sc;
476 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
478 sc = (struct acpi_thinkpad_softc *)oidp->oid_arg1;
479 function = oidp->oid_arg2;
480 method = acpi_thinkpad_sysctls[function].method;
482 lockmgr(&tplock, LK_EXCLUSIVE);
483 arg = acpi_thinkpad_sysctl_get(sc, method);
484 error = sysctl_handle_int(oidp, &arg, 0, req);
487 if (error != 0 || req->newptr == NULL)
491 error = acpi_thinkpad_sysctl_set(sc, method, arg);
494 lockmgr(&tplock, LK_RELEASE);
499 acpi_thinkpad_sysctl_get(struct acpi_thinkpad_softc *sc, int method)
504 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
505 KKASSERT(lockstatus(&tplock, curthread) != 0);
508 case ACPI_THINKPAD_METHOD_EVENTS:
509 acpi_GetInteger(sc->handle, THINKPAD_NAME_EVENTS_STATUS_GET,
513 case ACPI_THINKPAD_METHOD_EVENTMASK:
514 if (sc->events_mask_supported)
515 acpi_GetInteger(sc->handle,
516 THINKPAD_NAME_EVENTS_MASK_GET, &val);
519 case ACPI_THINKPAD_METHOD_HOTKEY:
521 * Construct the hotkey as a bitmask as illustrated below.
522 * Note that whenever a key was pressed, the respecting bit
523 * toggles and nothing else changes.
524 * +--+--+-+-+-+-+-+-+-+-+-+-+
525 * |11|10|9|8|7|6|5|4|3|2|1|0|
526 * +--+--+-+-+-+-+-+-+-+-+-+-+
527 * | | | | | | | | | | | |
528 * | | | | | | | | | | | +- Home Button
529 * | | | | | | | | | | +--- Search Button
530 * | | | | | | | | | +----- Mail Button
531 * | | | | | | | | +------- Thinkpad Button
532 * | | | | | | | +--------- Zoom (Fn + Space)
533 * | | | | | | +----------- WLAN Button
534 * | | | | | +------------- Video Button
535 * | | | | +--------------- Hibernate Button
536 * | | | +----------------- Thinklight Button
537 * | | +------------------- Screen expand (Fn + F8)
538 * | +--------------------- Brightness
539 * +------------------------ Volume/Mute
541 key = rtcin(THINKPAD_RTC_HOTKEY1);
542 val = (THINKPAD_RTC_MASK_HOME | THINKPAD_RTC_MASK_SEARCH |
543 THINKPAD_RTC_MASK_MAIL | THINKPAD_RTC_MASK_WLAN) & key;
544 key = rtcin(THINKPAD_RTC_HOTKEY2);
545 val |= (THINKPAD_RTC_MASK_THINKPAD | THINKPAD_RTC_MASK_VIDEO |
546 THINKPAD_RTC_MASK_HIBERNATE) & key;
547 val |= (THINKPAD_RTC_MASK_ZOOM & key) >> 1;
548 key = rtcin(THINKPAD_RTC_THINKLIGHT);
549 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
550 key = rtcin(THINKPAD_RTC_SCREENEXPAND);
551 val |= (THINKPAD_RTC_MASK_THINKLIGHT & key) << 4;
552 key = rtcin(THINKPAD_RTC_BRIGHTNESS);
553 val |= (THINKPAD_RTC_MASK_BRIGHTNESS & key) << 5;
554 key = rtcin(THINKPAD_RTC_VOLUME);
555 val |= (THINKPAD_RTC_MASK_VOLUME & key) << 4;
558 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
559 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
560 val = val_ec & THINKPAD_EC_MASK_BRI;
563 case ACPI_THINKPAD_METHOD_VOLUME:
564 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
565 val = val_ec & THINKPAD_EC_MASK_VOL;
568 case ACPI_THINKPAD_METHOD_MUTE:
569 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME, &val_ec, 1);
570 val = ((val_ec & THINKPAD_EC_MASK_MUTE) ==
571 THINKPAD_EC_MASK_MUTE);
574 case ACPI_THINKPAD_METHOD_THINKLIGHT:
575 if (sc->light_get_supported)
576 acpi_GetInteger(sc->ec_handle, THINKPAD_NAME_KEYLIGHT,
582 case ACPI_THINKPAD_METHOD_BLUETOOTH:
583 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
584 sc->wlan_bt_flags = val;
585 val = ((val & THINKPAD_NAME_MASK_BT) != 0);
588 case ACPI_THINKPAD_METHOD_WLAN:
589 acpi_GetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_GET, &val);
590 sc->wlan_bt_flags = val;
591 val = ((val & THINKPAD_NAME_MASK_WLAN) != 0);
594 case ACPI_THINKPAD_METHOD_FANSPEED:
595 if (sc->fan_handle) {
596 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
601 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED,
607 case ACPI_THINKPAD_METHOD_FANLEVEL:
609 * The THINKPAD_EC_FANSTATUS register works as follows:
610 * Bit 0-5 indicate the level at which the fan operates. Only
611 * values between 0 and 7 have an effect. Everything
612 * above 7 is treated the same as level 7
613 * Bit 6 overrides the fan speed limit if set to 1
614 * Bit 7 indicates at which mode the fan operates:
615 * manual (0) or automatic (1)
617 if (!sc->fan_handle) {
618 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
620 val = val_ec & THINKPAD_EC_MASK_FANLEVEL;
624 case ACPI_THINKPAD_METHOD_FANSTATUS:
625 if (!sc->fan_handle) {
626 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
628 val = (val_ec & THINKPAD_EC_MASK_FANSTATUS) ==
629 THINKPAD_EC_MASK_FANSTATUS;
640 acpi_thinkpad_sysctl_set(struct acpi_thinkpad_softc *sc, int method, int arg)
645 ACPI_OBJECT_LIST Args;
648 ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
649 KKASSERT(lockstatus(&tplock, curthread) != 0);
652 case ACPI_THINKPAD_METHOD_EVENTS:
653 if (arg < 0 || arg > 1)
656 status = acpi_SetInteger(sc->handle,
657 THINKPAD_NAME_EVENTS_STATUS_SET, arg);
658 if (ACPI_FAILURE(status))
660 if (sc->events_mask_supported)
661 return acpi_thinkpad_eventmask_set(sc,
662 sc->events_availmask);
665 case ACPI_THINKPAD_METHOD_EVENTMASK:
666 if (sc->events_mask_supported)
667 return acpi_thinkpad_eventmask_set(sc, arg);
670 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
671 if (arg < 0 || arg > 7)
674 if (sc->cmos_handle) {
675 /* Read the current brightness */
676 status = ACPI_EC_READ(sc->ec_dev,
677 THINKPAD_EC_BRIGHTNESS, &val_ec, 1);
678 if (ACPI_FAILURE(status))
680 val = val_ec & THINKPAD_EC_MASK_BRI;
684 Arg.Type = ACPI_TYPE_INTEGER;
685 Arg.Integer.Value = (arg > val) ?
686 THINKPAD_CMOS_BRIGHTNESS_UP :
687 THINKPAD_CMOS_BRIGHTNESS_DOWN;
689 step = (arg > val) ? 1 : -1;
690 for (i = val; i != arg; i += step) {
691 status = AcpiEvaluateObject(sc->cmos_handle,
693 if (ACPI_FAILURE(status))
697 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_BRIGHTNESS,
701 case ACPI_THINKPAD_METHOD_VOLUME:
702 if (arg < 0 || arg > 14)
705 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
707 if (ACPI_FAILURE(status))
710 if (sc->cmos_handle) {
711 val = val_ec & THINKPAD_EC_MASK_VOL;
715 Arg.Type = ACPI_TYPE_INTEGER;
716 Arg.Integer.Value = (arg > val) ?
717 THINKPAD_CMOS_VOLUME_UP : THINKPAD_CMOS_VOLUME_DOWN;
719 step = (arg > val) ? 1 : -1;
720 for (i = val; i != arg; i += step) {
721 status = AcpiEvaluateObject(sc->cmos_handle,
723 if (ACPI_FAILURE(status))
727 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, arg +
728 (val_ec & (~THINKPAD_EC_MASK_VOL)), 1);
731 case ACPI_THINKPAD_METHOD_MUTE:
732 if (arg < 0 || arg > 1)
735 status = ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_VOLUME,
737 if (ACPI_FAILURE(status))
740 if (sc->cmos_handle) {
741 val = val_ec & THINKPAD_EC_MASK_VOL;
745 Arg.Type = ACPI_TYPE_INTEGER;
746 Arg.Integer.Value = THINKPAD_CMOS_VOLUME_MUTE;
748 status = AcpiEvaluateObject(sc->cmos_handle, NULL,
750 if (ACPI_FAILURE(status))
753 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_VOLUME, (arg==1) ?
754 val_ec | THINKPAD_EC_MASK_MUTE :
755 val_ec & (~THINKPAD_EC_MASK_MUTE), 1);
758 case ACPI_THINKPAD_METHOD_THINKLIGHT:
759 if (arg < 0 || arg > 1)
762 if (sc->light_set_supported) {
765 Arg.Type = ACPI_TYPE_INTEGER;
766 Arg.Integer.Value = arg ?
767 sc->light_cmd_on : sc->light_cmd_off;
769 status = AcpiEvaluateObject(sc->light_handle, NULL,
771 if (ACPI_SUCCESS(status))
777 case ACPI_THINKPAD_METHOD_BLUETOOTH:
778 if (arg < 0 || arg > 1)
781 val = (arg == 1) ? sc->wlan_bt_flags |
782 THINKPAD_NAME_MASK_BT :
783 sc->wlan_bt_flags & (~THINKPAD_NAME_MASK_BT);
784 return acpi_SetInteger(sc->handle, THINKPAD_NAME_WLAN_BT_SET,
788 case ACPI_THINKPAD_METHOD_FANLEVEL:
789 if (arg < 0 || arg > 7)
792 if (!sc->fan_handle) {
793 /* Read the current fanstatus */
794 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
796 val = val_ec & (~THINKPAD_EC_MASK_FANLEVEL);
798 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
803 case ACPI_THINKPAD_METHOD_FANSTATUS:
804 if (arg < 0 || arg > 1)
807 if (!sc->fan_handle) {
808 /* Read the current fanstatus */
809 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSTATUS,
812 return ACPI_EC_WRITE(sc->ec_dev, THINKPAD_EC_FANSTATUS,
813 (arg == 1) ? (val_ec | THINKPAD_EC_MASK_FANSTATUS) :
814 (val_ec & (~THINKPAD_EC_MASK_FANSTATUS)), 1);
823 acpi_thinkpad_sysctl_init(struct acpi_thinkpad_softc *sc, int method)
826 ACPI_OBJECT_TYPE cmos_t;
827 ACPI_HANDLE ledb_handle;
830 case ACPI_THINKPAD_METHOD_EVENTS:
831 /* Events are disabled by default */
834 case ACPI_THINKPAD_METHOD_EVENTMASK:
835 return (sc->events_mask_supported);
837 case ACPI_THINKPAD_METHOD_HOTKEY:
838 case ACPI_THINKPAD_METHOD_BRIGHTNESS:
839 case ACPI_THINKPAD_METHOD_VOLUME:
840 case ACPI_THINKPAD_METHOD_MUTE:
841 /* EC is required here, which was already checked before */
844 case ACPI_THINKPAD_METHOD_THINKLIGHT:
845 sc->cmos_handle = NULL;
846 sc->light_get_supported = ACPI_SUCCESS(acpi_GetInteger(
847 sc->ec_handle, THINKPAD_NAME_KEYLIGHT, &sc->light_val));
849 if ((ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\UCMS",
850 &sc->light_handle)) ||
851 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMOS",
852 &sc->light_handle)) ||
853 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\CMS",
854 &sc->light_handle))) &&
855 ACPI_SUCCESS(AcpiGetType(sc->light_handle, &cmos_t)) &&
856 cmos_t == ACPI_TYPE_METHOD) {
857 sc->light_cmd_on = 0x0c;
858 sc->light_cmd_off = 0x0d;
859 sc->cmos_handle = sc->light_handle;
861 else if (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\LGHT",
862 &sc->light_handle))) {
863 sc->light_cmd_on = 1;
864 sc->light_cmd_off = 0;
867 sc->light_handle = NULL;
869 sc->light_set_supported = (sc->light_handle &&
870 ACPI_FAILURE(AcpiGetHandle(sc->ec_handle, "LEDB",
873 if (sc->light_get_supported)
876 if (sc->light_set_supported) {
883 case ACPI_THINKPAD_METHOD_BLUETOOTH:
884 case ACPI_THINKPAD_METHOD_WLAN:
885 if (ACPI_SUCCESS(acpi_GetInteger(sc->handle,
886 THINKPAD_NAME_WLAN_BT_GET, &dummy)))
890 case ACPI_THINKPAD_METHOD_FANSPEED:
892 * Some models report the fan speed in levels from 0-7
893 * Newer models report it contiguously
895 sc->fan_levels = (ACPI_SUCCESS(AcpiGetHandle(sc->handle, "GFAN",
897 ACPI_SUCCESS(AcpiGetHandle(sc->handle, "\\FSPD",
901 case ACPI_THINKPAD_METHOD_FANLEVEL:
902 case ACPI_THINKPAD_METHOD_FANSTATUS:
904 * Fan status is only supported on those models,
905 * which report fan RPM contiguously, not in levels
911 case ACPI_THINKPAD_METHOD_THERMAL:
912 if (ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
913 THINKPAD_NAME_THERMAL_GET, &dummy))) {
914 sc->thermal_updt_supported =
915 ACPI_SUCCESS(acpi_GetInteger(sc->ec_handle,
916 THINKPAD_NAME_THERMAL_UPDT, &dummy));
925 acpi_thinkpad_notify(ACPI_HANDLE h, UINT32 notify, void *context)
927 int event, arg, type;
928 device_t dev = context;
929 struct acpi_thinkpad_softc *sc = device_get_softc(dev);
931 ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, notify);
934 device_printf(dev, "Unknown notify\n");
937 acpi_GetInteger(acpi_get_handle(dev), THINKPAD_NAME_EVENTS_GET,
943 type = (event >> 12) & 0xf;
947 if (!(sc->events_availmask & (1 << (arg - 1)))) {
948 device_printf(dev, "Unknown key %d\n", arg);
953 acpi_UserNotify("THINKPAD", h, (arg & 0xff));
962 acpi_thinkpad_refresh(void *arg)
964 struct acpi_thinkpad_softc *sc = (struct acpi_thinkpad_softc *)arg;
967 for (i = 0; i < THINKPAD_TEMP_SENSORS; i++) {
968 char temp_cmd[] = "TMP0";
970 temp_cmd[3] = '0' + i;
972 * The TMPx methods seem to return +/- 128 or 0
973 * when the respecting sensor is not available
975 if (ACPI_FAILURE(acpi_GetInteger(sc->ec_handle, temp_cmd,
976 &data)) || ABS(data) == 128 || data == 0) {
977 sc->sensors[i].flags |= SENSOR_FINVALID;
980 if (sc->thermal_updt_supported)
981 /* Temperature is reported in tenth of Kelvin */
982 sc->sensors[i].value = data * 100000 - 50000;
984 sc->sensors[i].value = data * 1000000 + 273150000;
985 sc->sensors[i].flags &= ~SENSOR_FINVALID;
988 if (sc->fan_handle) {
989 if (ACPI_FAILURE(acpi_GetInteger(sc->fan_handle,
991 sc->sensors[i].flags |= SENSOR_FINVALID;
992 sc->sensors[i].value = data;
993 sc->sensors[i].flags &= ~SENSOR_FINVALID;
997 ACPI_EC_READ(sc->ec_dev, THINKPAD_EC_FANSPEED, &speed, 2);
998 sc->sensors[i].value = speed;
999 sc->sensors[i].flags &= ~SENSOR_FINVALID;