thinkpad_acpi.c revision 1.48.2.1 1 /* $NetBSD: thinkpad_acpi.c,v 1.48.2.1 2021/04/03 22:28:43 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill (at) invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.48.2.1 2021/04/03 22:28:43 thorpej Exp $");
31
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/module.h>
35 #include <sys/systm.h>
36
37 #include <dev/acpi/acpireg.h>
38 #include <dev/acpi/acpivar.h>
39 #include <dev/acpi/acpi_ecvar.h>
40 #include <dev/acpi/acpi_power.h>
41
42 #include <dev/isa/isareg.h>
43
44 #define _COMPONENT ACPI_RESOURCE_COMPONENT
45 ACPI_MODULE_NAME ("thinkpad_acpi")
46
47 #define THINKPAD_NTEMPSENSORS 8
48 #define THINKPAD_NFANSENSORS 1
49 #define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS)
50
51 typedef struct thinkpad_softc {
52 device_t sc_dev;
53 device_t sc_ecdev;
54 struct acpi_devnode *sc_node;
55 ACPI_HANDLE sc_powhdl;
56 ACPI_HANDLE sc_cmoshdl;
57
58 #define TP_PSW_SLEEP 0 /* FnF4 */
59 #define TP_PSW_HIBERNATE 1 /* FnF12 */
60 #define TP_PSW_DISPLAY_CYCLE 2 /* FnF7 */
61 #define TP_PSW_LOCK_SCREEN 3 /* FnF2 */
62 #define TP_PSW_BATTERY_INFO 4 /* FnF3 */
63 #define TP_PSW_EJECT_BUTTON 5 /* FnF9 */
64 #define TP_PSW_ZOOM_BUTTON 6 /* FnSPACE */
65 #define TP_PSW_VENDOR_BUTTON 7 /* ThinkVantage */
66 #define TP_PSW_FNF1_BUTTON 8 /* FnF1 */
67 #define TP_PSW_WIRELESS_BUTTON 9 /* FnF5 */
68 #define TP_PSW_WWAN_BUTTON 10 /* FnF6 */
69 #define TP_PSW_POINTER_BUTTON 11 /* FnF8 */
70 #define TP_PSW_FNF10_BUTTON 12 /* FnF10 */
71 #define TP_PSW_FNF11_BUTTON 13 /* FnF11 */
72 #define TP_PSW_BRIGHTNESS_UP 14
73 #define TP_PSW_BRIGHTNESS_DOWN 15
74 #define TP_PSW_THINKLIGHT 16
75 #define TP_PSW_VOLUME_UP 17
76 #define TP_PSW_VOLUME_DOWN 18
77 #define TP_PSW_VOLUME_MUTE 19
78 #define TP_PSW_LAST 20
79
80 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST];
81 bool sc_smpsw_valid;
82
83 struct sysmon_envsys *sc_sme;
84 envsys_data_t sc_sensor[THINKPAD_NSENSORS];
85
86 int sc_display_state;
87 } thinkpad_softc_t;
88
89 /* Hotkey events */
90 #define THINKPAD_NOTIFY_FnF1 0x001
91 #define THINKPAD_NOTIFY_LockScreen 0x002
92 #define THINKPAD_NOTIFY_BatteryInfo 0x003
93 #define THINKPAD_NOTIFY_SleepButton 0x004
94 #define THINKPAD_NOTIFY_WirelessSwitch 0x005
95 #define THINKPAD_NOTIFY_wWANSwitch 0x006
96 #define THINKPAD_NOTIFY_DisplayCycle 0x007
97 #define THINKPAD_NOTIFY_PointerSwitch 0x008
98 #define THINKPAD_NOTIFY_EjectButton 0x009
99 #define THINKPAD_NOTIFY_FnF10 0x00a /* XXX: Not seen on T61 */
100 #define THINKPAD_NOTIFY_FnF11 0x00b
101 #define THINKPAD_NOTIFY_HibernateButton 0x00c
102 #define THINKPAD_NOTIFY_BrightnessUp 0x010
103 #define THINKPAD_NOTIFY_BrightnessDown 0x011
104 #define THINKPAD_NOTIFY_ThinkLight 0x012
105 #define THINKPAD_NOTIFY_Zoom 0x014
106 #define THINKPAD_NOTIFY_VolumeUp 0x015 /* XXX: Not seen on T61 */
107 #define THINKPAD_NOTIFY_VolumeDown 0x016 /* XXX: Not seen on T61 */
108 #define THINKPAD_NOTIFY_VolumeMute 0x017 /* XXX: Not seen on T61 */
109 #define THINKPAD_NOTIFY_ThinkVantage 0x018
110
111 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04
112 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05
113
114 #define THINKPAD_HKEY_VERSION 0x0100
115
116 #define THINKPAD_DISPLAY_LCD 0x01
117 #define THINKPAD_DISPLAY_CRT 0x02
118 #define THINKPAD_DISPLAY_DVI 0x08
119 #define THINKPAD_DISPLAY_ALL \
120 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI)
121
122 #define THINKPAD_BLUETOOTH_HWPRESENT 0x01
123 #define THINKPAD_BLUETOOTH_RADIOSSW 0x02
124 #define THINKPAD_BLUETOOTH_RESUMECTRL 0x04
125
126 #define THINKPAD_WWAN_HWPRESENT 0x01
127 #define THINKPAD_WWAN_RADIOSSW 0x02
128 #define THINKPAD_WWAN_RESUMECTRL 0x04
129
130 #define THINKPAD_UWB_HWPRESENT 0x01
131 #define THINKPAD_UWB_RADIOSSW 0x02
132
133 #define THINKPAD_RFK_BLUETOOTH 0
134 #define THINKPAD_RFK_WWAN 1
135 #define THINKPAD_RFK_UWB 2
136
137 static int thinkpad_match(device_t, cfdata_t, void *);
138 static void thinkpad_attach(device_t, device_t, void *);
139 static int thinkpad_detach(device_t, int);
140
141 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t);
142 static void thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *);
143 static void thinkpad_get_hotkeys(void *);
144
145 static void thinkpad_sensors_init(thinkpad_softc_t *);
146 static void thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
147 static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
148 static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *);
149
150 static void thinkpad_uwb_toggle(thinkpad_softc_t *);
151 static void thinkpad_wwan_toggle(thinkpad_softc_t *);
152 static void thinkpad_bluetooth_toggle(thinkpad_softc_t *);
153
154 static bool thinkpad_resume(device_t, const pmf_qual_t *);
155 static void thinkpad_brightness_up(device_t);
156 static void thinkpad_brightness_down(device_t);
157 static uint8_t thinkpad_brightness_read(thinkpad_softc_t *sc);
158 static void thinkpad_cmos(thinkpad_softc_t *, uint8_t);
159
160 CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t),
161 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL,
162 DVF_DETACH_SHUTDOWN);
163
164 static const struct device_compatible_entry compat_data[] = {
165 { .compat = "IBM0068" },
166 { .compat = "LEN0068" },
167 DEVICE_COMPAT_EOL
168 };
169
170 static int
171 thinkpad_match(device_t parent, cfdata_t match, void *opaque)
172 {
173 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
174 ACPI_INTEGER ver;
175 int ret;
176
177 ret = acpi_compatible_match(aa, compat_data);
178 if (ret == 0)
179 return 0;
180
181 /* We only support hotkey version 0x0100 */
182 if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV",
183 &ver)))
184 return 0;
185
186 if (ver != THINKPAD_HKEY_VERSION)
187 return 0;
188
189 /* Cool, looks like we're good to go */
190 return ret;
191 }
192
193 static void
194 thinkpad_attach(device_t parent, device_t self, void *opaque)
195 {
196 thinkpad_softc_t *sc = device_private(self);
197 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
198 struct sysmon_pswitch *psw;
199 device_t curdev;
200 deviter_t di;
201 ACPI_STATUS rv;
202 ACPI_INTEGER val;
203 int i;
204
205 sc->sc_dev = self;
206 sc->sc_powhdl = NULL;
207 sc->sc_cmoshdl = NULL;
208 sc->sc_node = aa->aa_node;
209 sc->sc_display_state = THINKPAD_DISPLAY_LCD;
210
211 aprint_naive("\n");
212 aprint_normal("\n");
213
214 sc->sc_ecdev = NULL;
215 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST);
216 curdev != NULL; curdev = deviter_next(&di))
217 if (device_is_a(curdev, "acpiecdt") ||
218 device_is_a(curdev, "acpiec")) {
219 sc->sc_ecdev = curdev;
220 break;
221 }
222 deviter_release(&di);
223
224 if (sc->sc_ecdev)
225 aprint_debug_dev(self, "using EC at %s\n",
226 device_xname(sc->sc_ecdev));
227
228 /* Get the supported event mask */
229 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val);
230 if (ACPI_FAILURE(rv)) {
231 aprint_error_dev(self, "couldn't evaluate MHKA: %s\n",
232 AcpiFormatException(rv));
233 goto fail;
234 }
235
236 /* Enable all supported events */
237 rv = thinkpad_mask_init(sc, val);
238 if (ACPI_FAILURE(rv)) {
239 aprint_error_dev(self, "couldn't set event mask: %s\n",
240 AcpiFormatException(rv));
241 goto fail;
242 }
243
244 (void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler);
245
246 /*
247 * Obtain a handle for CMOS commands. This is used by T61.
248 */
249 (void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl);
250
251 /*
252 * Obtain a handle to the power resource available on many models.
253 * Since pmf(9) is not yet integrated with the ACPI power resource
254 * code, this must be turned on manually upon resume. Otherwise the
255 * system may, for instance, resume from S3 with usb(4) powered down.
256 */
257 (void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl);
258
259 /* Register power switches with sysmon */
260 psw = sc->sc_smpsw;
261 sc->sc_smpsw_valid = true;
262
263 psw[TP_PSW_SLEEP].smpsw_name = device_xname(self);
264 psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
265 #if notyet
266 psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self);
267 mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE;
268 #endif
269 for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++)
270 sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY;
271 psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE;
272 psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN;
273 psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO;
274 psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON;
275 psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON;
276 psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON;
277 #ifndef THINKPAD_NORMAL_HOTKEYS
278 psw[TP_PSW_FNF1_BUTTON].smpsw_name = PSWITCH_HK_FNF1_BUTTON;
279 psw[TP_PSW_WIRELESS_BUTTON].smpsw_name = PSWITCH_HK_WIRELESS_BUTTON;
280 psw[TP_PSW_WWAN_BUTTON].smpsw_name = PSWITCH_HK_WWAN_BUTTON;
281 psw[TP_PSW_POINTER_BUTTON].smpsw_name = PSWITCH_HK_POINTER_BUTTON;
282 psw[TP_PSW_FNF10_BUTTON].smpsw_name = PSWITCH_HK_FNF10_BUTTON;
283 psw[TP_PSW_FNF11_BUTTON].smpsw_name = PSWITCH_HK_FNF11_BUTTON;
284 psw[TP_PSW_BRIGHTNESS_UP].smpsw_name = PSWITCH_HK_BRIGHTNESS_UP;
285 psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name = PSWITCH_HK_BRIGHTNESS_DOWN;
286 psw[TP_PSW_THINKLIGHT].smpsw_name = PSWITCH_HK_THINKLIGHT;
287 psw[TP_PSW_VOLUME_UP].smpsw_name = PSWITCH_HK_VOLUME_UP;
288 psw[TP_PSW_VOLUME_DOWN].smpsw_name = PSWITCH_HK_VOLUME_DOWN;
289 psw[TP_PSW_VOLUME_MUTE].smpsw_name = PSWITCH_HK_VOLUME_MUTE;
290 #endif /* THINKPAD_NORMAL_HOTKEYS */
291
292 for (i = 0; i < TP_PSW_LAST; i++) {
293 /* not supported yet */
294 if (i == TP_PSW_HIBERNATE)
295 continue;
296 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
297 aprint_error_dev(self,
298 "couldn't register with sysmon\n");
299 sc->sc_smpsw_valid = false;
300 break;
301 }
302 }
303
304 /* Register temperature and fan sensors with envsys */
305 thinkpad_sensors_init(sc);
306
307 fail:
308 if (!pmf_device_register(self, NULL, thinkpad_resume))
309 aprint_error_dev(self, "couldn't establish power handler\n");
310 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
311 thinkpad_brightness_up, true))
312 aprint_error_dev(self, "couldn't register event handler\n");
313 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
314 thinkpad_brightness_down, true))
315 aprint_error_dev(self, "couldn't register event handler\n");
316 }
317
318 static int
319 thinkpad_detach(device_t self, int flags)
320 {
321 struct thinkpad_softc *sc = device_private(self);
322 int i;
323
324 acpi_deregister_notify(sc->sc_node);
325
326 for (i = 0; i < TP_PSW_LAST; i++)
327 sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
328
329 if (sc->sc_sme != NULL)
330 sysmon_envsys_unregister(sc->sc_sme);
331
332 pmf_device_deregister(self);
333
334 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP,
335 thinkpad_brightness_up, true);
336
337 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
338 thinkpad_brightness_down, true);
339
340 return 0;
341 }
342
343 static void
344 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
345 {
346 device_t self = opaque;
347 thinkpad_softc_t *sc;
348
349 sc = device_private(self);
350
351 if (notify != 0x80) {
352 aprint_debug_dev(self, "unknown notify 0x%02x\n", notify);
353 return;
354 }
355
356 (void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc);
357 }
358
359 static void
360 thinkpad_get_hotkeys(void *opaque)
361 {
362 thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque;
363 device_t self = sc->sc_dev;
364 ACPI_STATUS rv;
365 ACPI_INTEGER val;
366 int type, event;
367
368 for (;;) {
369 rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val);
370 if (ACPI_FAILURE(rv)) {
371 aprint_error_dev(self, "couldn't evaluate MHKP: %s\n",
372 AcpiFormatException(rv));
373 return;
374 }
375
376 if (val == 0)
377 return;
378
379 type = (val & 0xf000) >> 12;
380 event = val & 0x0fff;
381
382 if (type != 1)
383 /* Only type 1 events are supported for now */
384 continue;
385
386 switch (event) {
387 case THINKPAD_NOTIFY_BrightnessUp:
388 thinkpad_brightness_up(self);
389 #ifndef THINKPAD_NORMAL_HOTKEYS
390 if (sc->sc_smpsw_valid == false)
391 break;
392 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP],
393 PSWITCH_EVENT_PRESSED);
394 #endif
395 break;
396 case THINKPAD_NOTIFY_BrightnessDown:
397 thinkpad_brightness_down(self);
398 #ifndef THINKPAD_NORMAL_HOTKEYS
399 if (sc->sc_smpsw_valid == false)
400 break;
401 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN],
402 PSWITCH_EVENT_PRESSED);
403 #endif
404 break;
405 case THINKPAD_NOTIFY_WirelessSwitch:
406 thinkpad_uwb_toggle(sc);
407 thinkpad_wwan_toggle(sc);
408 thinkpad_bluetooth_toggle(sc);
409 #ifndef THINKPAD_NORMAL_HOTKEYS
410 if (sc->sc_smpsw_valid == false)
411 break;
412 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON],
413 PSWITCH_EVENT_PRESSED);
414 #endif
415 break;
416 case THINKPAD_NOTIFY_wWANSwitch:
417 thinkpad_wwan_toggle(sc);
418 #ifndef THINKPAD_NORMAL_HOTKEYS
419 if (sc->sc_smpsw_valid == false)
420 break;
421 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON],
422 PSWITCH_EVENT_PRESSED);
423 #endif
424 break;
425 case THINKPAD_NOTIFY_SleepButton:
426 if (sc->sc_smpsw_valid == false)
427 break;
428 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP],
429 PSWITCH_EVENT_PRESSED);
430 break;
431 case THINKPAD_NOTIFY_HibernateButton:
432 #if notyet
433 if (sc->sc_smpsw_valid == false)
434 break;
435 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE],
436 PSWITCH_EVENT_PRESSED);
437 #endif
438 break;
439 case THINKPAD_NOTIFY_DisplayCycle:
440 if (sc->sc_smpsw_valid == false)
441 break;
442 sysmon_pswitch_event(
443 &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE],
444 PSWITCH_EVENT_PRESSED);
445 break;
446 case THINKPAD_NOTIFY_LockScreen:
447 if (sc->sc_smpsw_valid == false)
448 break;
449 sysmon_pswitch_event(
450 &sc->sc_smpsw[TP_PSW_LOCK_SCREEN],
451 PSWITCH_EVENT_PRESSED);
452 break;
453 case THINKPAD_NOTIFY_BatteryInfo:
454 if (sc->sc_smpsw_valid == false)
455 break;
456 sysmon_pswitch_event(
457 &sc->sc_smpsw[TP_PSW_BATTERY_INFO],
458 PSWITCH_EVENT_PRESSED);
459 break;
460 case THINKPAD_NOTIFY_EjectButton:
461 if (sc->sc_smpsw_valid == false)
462 break;
463 sysmon_pswitch_event(
464 &sc->sc_smpsw[TP_PSW_EJECT_BUTTON],
465 PSWITCH_EVENT_PRESSED);
466 break;
467 case THINKPAD_NOTIFY_Zoom:
468 if (sc->sc_smpsw_valid == false)
469 break;
470 sysmon_pswitch_event(
471 &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON],
472 PSWITCH_EVENT_PRESSED);
473 break;
474 case THINKPAD_NOTIFY_ThinkVantage:
475 if (sc->sc_smpsw_valid == false)
476 break;
477 sysmon_pswitch_event(
478 &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON],
479 PSWITCH_EVENT_PRESSED);
480 break;
481 #ifndef THINKPAD_NORMAL_HOTKEYS
482 case THINKPAD_NOTIFY_FnF1:
483 if (sc->sc_smpsw_valid == false)
484 break;
485 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON],
486 PSWITCH_EVENT_PRESSED);
487 break;
488 case THINKPAD_NOTIFY_PointerSwitch:
489 if (sc->sc_smpsw_valid == false)
490 break;
491 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON],
492 PSWITCH_EVENT_PRESSED);
493 break;
494 case THINKPAD_NOTIFY_FnF11:
495 if (sc->sc_smpsw_valid == false)
496 break;
497 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON],
498 PSWITCH_EVENT_PRESSED);
499 break;
500 case THINKPAD_NOTIFY_ThinkLight:
501 if (sc->sc_smpsw_valid == false)
502 break;
503 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT],
504 PSWITCH_EVENT_PRESSED);
505 break;
506 /*
507 * For some reason the next four aren't seen on my T61.
508 */
509 case THINKPAD_NOTIFY_FnF10:
510 if (sc->sc_smpsw_valid == false)
511 break;
512 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON],
513 PSWITCH_EVENT_PRESSED);
514 break;
515 case THINKPAD_NOTIFY_VolumeUp:
516 if (sc->sc_smpsw_valid == false)
517 break;
518 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP],
519 PSWITCH_EVENT_PRESSED);
520 break;
521 case THINKPAD_NOTIFY_VolumeDown:
522 if (sc->sc_smpsw_valid == false)
523 break;
524 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN],
525 PSWITCH_EVENT_PRESSED);
526 break;
527 case THINKPAD_NOTIFY_VolumeMute:
528 if (sc->sc_smpsw_valid == false)
529 break;
530 sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE],
531 PSWITCH_EVENT_PRESSED);
532 break;
533 #else
534 case THINKPAD_NOTIFY_FnF1:
535 case THINKPAD_NOTIFY_PointerSwitch:
536 case THINKPAD_NOTIFY_FnF10:
537 case THINKPAD_NOTIFY_FnF11:
538 case THINKPAD_NOTIFY_ThinkLight:
539 case THINKPAD_NOTIFY_VolumeUp:
540 case THINKPAD_NOTIFY_VolumeDown:
541 case THINKPAD_NOTIFY_VolumeMute:
542 /* XXXJDM we should deliver hotkeys as keycodes */
543 break;
544 #endif /* THINKPAD_NORMAL_HOTKEYS */
545 default:
546 aprint_debug_dev(self, "notify event 0x%03x\n", event);
547 break;
548 }
549 }
550 }
551
552 static ACPI_STATUS
553 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask)
554 {
555 ACPI_OBJECT param[2];
556 ACPI_OBJECT_LIST params;
557 ACPI_STATUS rv;
558 int i;
559
560 /* Update hotkey mask */
561 params.Count = 2;
562 params.Pointer = param;
563 param[0].Type = param[1].Type = ACPI_TYPE_INTEGER;
564
565 for (i = 0; i < 32; i++) {
566 param[0].Integer.Value = i + 1;
567 param[1].Integer.Value = ((__BIT(i) & mask) != 0);
568
569 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM",
570 ¶ms, NULL);
571 if (ACPI_FAILURE(rv))
572 return rv;
573 }
574
575 /* Enable hotkey events */
576 rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1);
577 if (ACPI_FAILURE(rv)) {
578 aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n",
579 AcpiFormatException(rv));
580 return rv;
581 }
582
583 /* Claim ownership of brightness control */
584 (void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0);
585
586 return AE_OK;
587 }
588
589 static void
590 thinkpad_sensors_init(thinkpad_softc_t *sc)
591 {
592 int i, j;
593
594 if (sc->sc_ecdev == NULL)
595 return; /* no chance of this working */
596
597 sc->sc_sme = sysmon_envsys_create();
598
599 for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) {
600
601 sc->sc_sensor[i].units = ENVSYS_STEMP;
602 sc->sc_sensor[i].state = ENVSYS_SINVALID;
603 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
604
605 (void)snprintf(sc->sc_sensor[i].desc,
606 sizeof(sc->sc_sensor[i].desc), "temperature %d", i);
607
608 if (sysmon_envsys_sensor_attach(sc->sc_sme,
609 &sc->sc_sensor[i]) != 0)
610 goto fail;
611 }
612
613 for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) {
614
615 sc->sc_sensor[i].units = ENVSYS_SFANRPM;
616 sc->sc_sensor[i].state = ENVSYS_SINVALID;
617 sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
618
619 (void)snprintf(sc->sc_sensor[i].desc,
620 sizeof(sc->sc_sensor[i].desc), "fan speed %d", j);
621
622 if (sysmon_envsys_sensor_attach(sc->sc_sme,
623 &sc->sc_sensor[i]) != 0)
624 goto fail;
625 }
626
627 sc->sc_sme->sme_name = device_xname(sc->sc_dev);
628 sc->sc_sme->sme_cookie = sc;
629 sc->sc_sme->sme_refresh = thinkpad_sensors_refresh;
630
631 if (sysmon_envsys_register(sc->sc_sme) != 0)
632 goto fail;
633
634 return;
635
636 fail:
637 aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n");
638 sysmon_envsys_destroy(sc->sc_sme);
639 sc->sc_sme = NULL;
640 }
641
642 static void
643 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
644 {
645 switch (edata->units) {
646 case ENVSYS_STEMP:
647 thinkpad_temp_refresh(sme, edata);
648 break;
649 case ENVSYS_SFANRPM:
650 thinkpad_fan_refresh(sme, edata);
651 break;
652 default:
653 break;
654 }
655 }
656
657 static void
658 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
659 {
660 thinkpad_softc_t *sc = sme->sme_cookie;
661 char sname[5] = "TMP?";
662 ACPI_INTEGER val;
663 ACPI_STATUS rv;
664 int temp;
665
666 sname[3] = '0' + edata->sensor;
667 rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val);
668 if (ACPI_FAILURE(rv)) {
669 edata->state = ENVSYS_SINVALID;
670 return;
671 }
672 temp = (int)val;
673 if (temp > 127 || temp < -127) {
674 edata->state = ENVSYS_SINVALID;
675 return;
676 }
677
678 edata->value_cur = temp * 1000000 + 273150000;
679 edata->state = ENVSYS_SVALID;
680 }
681
682 static void
683 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
684 {
685 thinkpad_softc_t *sc = sme->sme_cookie;
686 ACPI_INTEGER lo;
687 ACPI_INTEGER hi;
688 ACPI_STATUS rv;
689 int rpm;
690
691 /*
692 * Read the low byte first to avoid a firmware bug.
693 */
694 rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1);
695 if (ACPI_FAILURE(rv)) {
696 edata->state = ENVSYS_SINVALID;
697 return;
698 }
699 rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1);
700 if (ACPI_FAILURE(rv)) {
701 edata->state = ENVSYS_SINVALID;
702 return;
703 }
704
705 /*
706 * Extract the low bytes from buffers
707 */
708 lo = ((uint8_t *)&lo)[0];
709 hi = ((uint8_t *)&hi)[0];
710
711 rpm = ((((int)hi) << 8) | ((int)lo));
712 if (rpm < 0) {
713 edata->state = ENVSYS_SINVALID;
714 return;
715 }
716
717 edata->value_cur = rpm;
718 edata->state = ENVSYS_SVALID;
719 }
720
721 static void
722 thinkpad_bluetooth_toggle(thinkpad_softc_t *sc)
723 {
724 ACPI_BUFFER buf;
725 ACPI_OBJECT retobj;
726 ACPI_OBJECT param[1];
727 ACPI_OBJECT_LIST params;
728 ACPI_STATUS rv;
729
730 /* Ignore return value, as the hardware may not support bluetooth */
731 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL);
732 if (!ACPI_FAILURE(rv))
733 return;
734
735 buf.Pointer = &retobj;
736 buf.Length = sizeof(retobj);
737
738 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GBDC", NULL, &buf);
739 if (ACPI_FAILURE(rv))
740 return;
741
742 params.Count = 1;
743 params.Pointer = param;
744 param[0].Type = ACPI_TYPE_INTEGER;
745 param[0].Integer.Value =
746 (retobj.Integer.Value & THINKPAD_BLUETOOTH_RADIOSSW) == 0
747 ? THINKPAD_BLUETOOTH_RADIOSSW | THINKPAD_BLUETOOTH_RESUMECTRL
748 : 0;
749
750 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SBDC", ¶ms, NULL);
751 }
752
753 static void
754 thinkpad_wwan_toggle(thinkpad_softc_t *sc)
755 {
756 ACPI_BUFFER buf;
757 ACPI_OBJECT retobj;
758 ACPI_OBJECT param[1];
759 ACPI_OBJECT_LIST params;
760 ACPI_STATUS rv;
761
762 buf.Pointer = &retobj;
763 buf.Length = sizeof(retobj);
764
765 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, &buf);
766 if (ACPI_FAILURE(rv))
767 return;
768
769 params.Count = 1;
770 params.Pointer = param;
771 param[0].Type = ACPI_TYPE_INTEGER;
772 param[0].Integer.Value =
773 (retobj.Integer.Value & THINKPAD_WWAN_RADIOSSW) == 0
774 ? THINKPAD_WWAN_RADIOSSW | THINKPAD_WWAN_RESUMECTRL
775 : 0;
776
777 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SWAN", ¶ms, NULL);
778 }
779
780 static void
781 thinkpad_uwb_toggle(thinkpad_softc_t *sc)
782 {
783 ACPI_BUFFER buf;
784 ACPI_OBJECT retobj;
785 ACPI_OBJECT param[1];
786 ACPI_OBJECT_LIST params;
787 ACPI_STATUS rv;
788
789 buf.Pointer = &retobj;
790 buf.Length = sizeof(retobj);
791
792 rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GUWB", NULL, &buf);
793 if (ACPI_FAILURE(rv))
794 return;
795
796 params.Count = 1;
797 params.Pointer = param;
798 param[0].Type = ACPI_TYPE_INTEGER;
799 param[0].Integer.Value =
800 (retobj.Integer.Value & THINKPAD_UWB_RADIOSSW) == 0
801 ? THINKPAD_UWB_RADIOSSW
802 : 0;
803
804 (void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SUWB", ¶ms, NULL);
805 }
806
807 static uint8_t
808 thinkpad_brightness_read(thinkpad_softc_t *sc)
809 {
810 uint32_t val = 0;
811
812 AcpiOsWritePort(IO_RTC, 0x6c, 8);
813 AcpiOsReadPort(IO_RTC + 1, &val, 8);
814
815 return val & 7;
816 }
817
818 static void
819 thinkpad_brightness_up(device_t self)
820 {
821 thinkpad_softc_t *sc = device_private(self);
822
823 if (thinkpad_brightness_read(sc) == 7)
824 return;
825
826 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP);
827 }
828
829 static void
830 thinkpad_brightness_down(device_t self)
831 {
832 thinkpad_softc_t *sc = device_private(self);
833
834 if (thinkpad_brightness_read(sc) == 0)
835 return;
836
837 thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN);
838 }
839
840 static void
841 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd)
842 {
843 ACPI_STATUS rv;
844
845 if (sc->sc_cmoshdl == NULL)
846 return;
847
848 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd);
849
850 if (ACPI_FAILURE(rv))
851 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n",
852 AcpiFormatException(rv));
853 }
854
855 static bool
856 thinkpad_resume(device_t dv, const pmf_qual_t *qual)
857 {
858 thinkpad_softc_t *sc = device_private(dv);
859
860 if (sc->sc_powhdl == NULL)
861 return true;
862
863 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true);
864
865 return true;
866 }
867
868 MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power");
869
870 #ifdef _MODULE
871 #include "ioconf.c"
872 #endif
873
874 static int
875 thinkpad_modcmd(modcmd_t cmd, void *aux)
876 {
877 int rv = 0;
878
879 switch (cmd) {
880
881 case MODULE_CMD_INIT:
882
883 #ifdef _MODULE
884 rv = config_init_component(cfdriver_ioconf_thinkpad,
885 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
886 #endif
887 break;
888
889 case MODULE_CMD_FINI:
890
891 #ifdef _MODULE
892 rv = config_fini_component(cfdriver_ioconf_thinkpad,
893 cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
894 #endif
895 break;
896
897 default:
898 rv = ENOTTY;
899 }
900
901 return rv;
902 }
903