xref: /netbsd-src/sys/dev/acpi/thinkpad_acpi.c (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
1 /* $NetBSD: thinkpad_acpi.c,v 1.49 2021/01/29 15:49:55 thorpej Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Jared D. McNeill <jmcneill@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.49 2021/01/29 15:49:55 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 		    &params, 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", &params, 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", &params, 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", &params, 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