xref: /netbsd-src/sys/dev/acpi/thinkpad_acpi.c (revision deb6f0161a9109e7de9b519dc8dfb9478668dcdd)
1 /* $NetBSD: thinkpad_acpi.c,v 1.46 2016/04/03 10:36:00 mlelstv 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.46 2016/04/03 10:36:00 mlelstv 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 char * const thinkpad_ids[] = {
165 	"IBM0068",
166 	"LEN0068",
167 	NULL
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 
176 	if (aa->aa_node->ad_type != ACPI_TYPE_DEVICE)
177 		return 0;
178 
179 	if (!acpi_match_hid(aa->aa_node->ad_devinfo, thinkpad_ids))
180 		return 0;
181 
182 	/* We only support hotkey version 0x0100 */
183 	if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV",
184 	    &ver)))
185 		return 0;
186 
187 	if (ver != THINKPAD_HKEY_VERSION)
188 		return 0;
189 
190 	/* Cool, looks like we're good to go */
191 	return 1;
192 }
193 
194 static void
195 thinkpad_attach(device_t parent, device_t self, void *opaque)
196 {
197 	thinkpad_softc_t *sc = device_private(self);
198 	struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
199 	struct sysmon_pswitch *psw;
200 	device_t curdev;
201 	deviter_t di;
202 	ACPI_STATUS rv;
203 	ACPI_INTEGER val;
204 	int i;
205 
206 	sc->sc_dev = self;
207 	sc->sc_powhdl = NULL;
208 	sc->sc_cmoshdl = NULL;
209 	sc->sc_node = aa->aa_node;
210 	sc->sc_display_state = THINKPAD_DISPLAY_LCD;
211 
212 	aprint_naive("\n");
213 	aprint_normal("\n");
214 
215 	sc->sc_ecdev = NULL;
216 	for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST);
217 	     curdev != NULL; curdev = deviter_next(&di))
218 		if (device_is_a(curdev, "acpiecdt") ||
219 		    device_is_a(curdev, "acpiec")) {
220 			sc->sc_ecdev = curdev;
221 			break;
222 		}
223 	deviter_release(&di);
224 
225 	if (sc->sc_ecdev)
226 		aprint_debug_dev(self, "using EC at %s\n",
227 		    device_xname(sc->sc_ecdev));
228 
229 	/* Get the supported event mask */
230 	rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val);
231 	if (ACPI_FAILURE(rv)) {
232 		aprint_error_dev(self, "couldn't evaluate MHKA: %s\n",
233 		    AcpiFormatException(rv));
234 		goto fail;
235 	}
236 
237 	/* Enable all supported events */
238 	rv = thinkpad_mask_init(sc, val);
239 	if (ACPI_FAILURE(rv)) {
240 		aprint_error_dev(self, "couldn't set event mask: %s\n",
241 		    AcpiFormatException(rv));
242 		goto fail;
243 	}
244 
245 	(void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler);
246 
247 	/*
248 	 * Obtain a handle for CMOS commands. This is used by T61.
249 	 */
250 	(void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl);
251 
252 	/*
253 	 * Obtain a handle to the power resource available on many models.
254 	 * Since pmf(9) is not yet integrated with the ACPI power resource
255 	 * code, this must be turned on manually upon resume. Otherwise the
256 	 * system may, for instance, resume from S3 with usb(4) powered down.
257 	 */
258 	(void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl);
259 
260 	/* Register power switches with sysmon */
261 	psw = sc->sc_smpsw;
262 	sc->sc_smpsw_valid = true;
263 
264 	psw[TP_PSW_SLEEP].smpsw_name = device_xname(self);
265 	psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
266 #if notyet
267 	psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self);
268 	mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE;
269 #endif
270 	for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++)
271 		sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY;
272 	psw[TP_PSW_DISPLAY_CYCLE].smpsw_name = PSWITCH_HK_DISPLAY_CYCLE;
273 	psw[TP_PSW_LOCK_SCREEN].smpsw_name = PSWITCH_HK_LOCK_SCREEN;
274 	psw[TP_PSW_BATTERY_INFO].smpsw_name = PSWITCH_HK_BATTERY_INFO;
275 	psw[TP_PSW_EJECT_BUTTON].smpsw_name = PSWITCH_HK_EJECT_BUTTON;
276 	psw[TP_PSW_ZOOM_BUTTON].smpsw_name = PSWITCH_HK_ZOOM_BUTTON;
277 	psw[TP_PSW_VENDOR_BUTTON].smpsw_name = PSWITCH_HK_VENDOR_BUTTON;
278 #ifndef THINKPAD_NORMAL_HOTKEYS
279 	psw[TP_PSW_FNF1_BUTTON].smpsw_name     = PSWITCH_HK_FNF1_BUTTON;
280 	psw[TP_PSW_WIRELESS_BUTTON].smpsw_name = PSWITCH_HK_WIRELESS_BUTTON;
281 	psw[TP_PSW_WWAN_BUTTON].smpsw_name     = PSWITCH_HK_WWAN_BUTTON;
282 	psw[TP_PSW_POINTER_BUTTON].smpsw_name  = PSWITCH_HK_POINTER_BUTTON;
283 	psw[TP_PSW_FNF10_BUTTON].smpsw_name    = PSWITCH_HK_FNF10_BUTTON;
284 	psw[TP_PSW_FNF11_BUTTON].smpsw_name    = PSWITCH_HK_FNF11_BUTTON;
285 	psw[TP_PSW_BRIGHTNESS_UP].smpsw_name   = PSWITCH_HK_BRIGHTNESS_UP;
286 	psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name = PSWITCH_HK_BRIGHTNESS_DOWN;
287 	psw[TP_PSW_THINKLIGHT].smpsw_name      = PSWITCH_HK_THINKLIGHT;
288 	psw[TP_PSW_VOLUME_UP].smpsw_name       = PSWITCH_HK_VOLUME_UP;
289 	psw[TP_PSW_VOLUME_DOWN].smpsw_name     = PSWITCH_HK_VOLUME_DOWN;
290 	psw[TP_PSW_VOLUME_MUTE].smpsw_name     = PSWITCH_HK_VOLUME_MUTE;
291 #endif /* THINKPAD_NORMAL_HOTKEYS */
292 
293 	for (i = 0; i < TP_PSW_LAST; i++) {
294 		/* not supported yet */
295 		if (i == TP_PSW_HIBERNATE)
296 			continue;
297 		if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
298 			aprint_error_dev(self,
299 			    "couldn't register with sysmon\n");
300 			sc->sc_smpsw_valid = false;
301 			break;
302 		}
303 	}
304 
305 	/* Register temperature and fan sensors with envsys */
306 	thinkpad_sensors_init(sc);
307 
308 fail:
309 	if (!pmf_device_register(self, NULL, thinkpad_resume))
310 		aprint_error_dev(self, "couldn't establish power handler\n");
311 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
312 	    thinkpad_brightness_up, true))
313 		aprint_error_dev(self, "couldn't register event handler\n");
314 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
315 	    thinkpad_brightness_down, true))
316 		aprint_error_dev(self, "couldn't register event handler\n");
317 }
318 
319 static int
320 thinkpad_detach(device_t self, int flags)
321 {
322 	struct thinkpad_softc *sc = device_private(self);
323 	int i;
324 
325 	acpi_deregister_notify(sc->sc_node);
326 
327 	for (i = 0; i < TP_PSW_LAST; i++)
328 		sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
329 
330 	if (sc->sc_sme != NULL)
331 		sysmon_envsys_unregister(sc->sc_sme);
332 
333 	pmf_device_deregister(self);
334 
335 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP,
336 	    thinkpad_brightness_up, true);
337 
338 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
339 	    thinkpad_brightness_down, true);
340 
341 	return 0;
342 }
343 
344 static void
345 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
346 {
347 	device_t self = opaque;
348 	thinkpad_softc_t *sc;
349 
350 	sc = device_private(self);
351 
352 	if (notify != 0x80) {
353 		aprint_debug_dev(self, "unknown notify 0x%02x\n", notify);
354 		return;
355 	}
356 
357 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc);
358 }
359 
360 static void
361 thinkpad_get_hotkeys(void *opaque)
362 {
363 	thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque;
364 	device_t self = sc->sc_dev;
365 	ACPI_STATUS rv;
366 	ACPI_INTEGER val;
367 	int type, event;
368 
369 	for (;;) {
370 		rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val);
371 		if (ACPI_FAILURE(rv)) {
372 			aprint_error_dev(self, "couldn't evaluate MHKP: %s\n",
373 			    AcpiFormatException(rv));
374 			return;
375 		}
376 
377 		if (val == 0)
378 			return;
379 
380 		type = (val & 0xf000) >> 12;
381 		event = val & 0x0fff;
382 
383 		if (type != 1)
384 			/* Only type 1 events are supported for now */
385 			continue;
386 
387 		switch (event) {
388 		case THINKPAD_NOTIFY_BrightnessUp:
389 			thinkpad_brightness_up(self);
390 #ifndef THINKPAD_NORMAL_HOTKEYS
391 			if (sc->sc_smpsw_valid == false)
392 				break;
393 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP],
394 			    PSWITCH_EVENT_PRESSED);
395 #endif
396 			break;
397 		case THINKPAD_NOTIFY_BrightnessDown:
398 			thinkpad_brightness_down(self);
399 #ifndef THINKPAD_NORMAL_HOTKEYS
400 			if (sc->sc_smpsw_valid == false)
401 				break;
402 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN],
403 			    PSWITCH_EVENT_PRESSED);
404 #endif
405 			break;
406 		case THINKPAD_NOTIFY_WirelessSwitch:
407 			thinkpad_uwb_toggle(sc);
408 			thinkpad_wwan_toggle(sc);
409 			thinkpad_bluetooth_toggle(sc);
410 #ifndef THINKPAD_NORMAL_HOTKEYS
411 			if (sc->sc_smpsw_valid == false)
412 				break;
413 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON],
414 			    PSWITCH_EVENT_PRESSED);
415 #endif
416 			break;
417 		case THINKPAD_NOTIFY_wWANSwitch:
418 			thinkpad_wwan_toggle(sc);
419 #ifndef THINKPAD_NORMAL_HOTKEYS
420 			if (sc->sc_smpsw_valid == false)
421 				break;
422 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON],
423 			    PSWITCH_EVENT_PRESSED);
424 #endif
425 			break;
426 		case THINKPAD_NOTIFY_SleepButton:
427 			if (sc->sc_smpsw_valid == false)
428 				break;
429 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP],
430 			    PSWITCH_EVENT_PRESSED);
431 			break;
432 		case THINKPAD_NOTIFY_HibernateButton:
433 #if notyet
434 			if (sc->sc_smpsw_valid == false)
435 				break;
436 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE],
437 			    PSWITCH_EVENT_PRESSED);
438 #endif
439 			break;
440 		case THINKPAD_NOTIFY_DisplayCycle:
441 			if (sc->sc_smpsw_valid == false)
442 				break;
443 			sysmon_pswitch_event(
444 			    &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE],
445 			    PSWITCH_EVENT_PRESSED);
446 			break;
447 		case THINKPAD_NOTIFY_LockScreen:
448 			if (sc->sc_smpsw_valid == false)
449 				break;
450 			sysmon_pswitch_event(
451 			    &sc->sc_smpsw[TP_PSW_LOCK_SCREEN],
452 			    PSWITCH_EVENT_PRESSED);
453 			break;
454 		case THINKPAD_NOTIFY_BatteryInfo:
455 			if (sc->sc_smpsw_valid == false)
456 				break;
457 			sysmon_pswitch_event(
458 			    &sc->sc_smpsw[TP_PSW_BATTERY_INFO],
459 			    PSWITCH_EVENT_PRESSED);
460 			break;
461 		case THINKPAD_NOTIFY_EjectButton:
462 			if (sc->sc_smpsw_valid == false)
463 				break;
464 			sysmon_pswitch_event(
465 			    &sc->sc_smpsw[TP_PSW_EJECT_BUTTON],
466 			    PSWITCH_EVENT_PRESSED);
467 			break;
468 		case THINKPAD_NOTIFY_Zoom:
469 			if (sc->sc_smpsw_valid == false)
470 				break;
471 			sysmon_pswitch_event(
472 			    &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON],
473 			    PSWITCH_EVENT_PRESSED);
474 			break;
475 		case THINKPAD_NOTIFY_ThinkVantage:
476 			if (sc->sc_smpsw_valid == false)
477 				break;
478 			sysmon_pswitch_event(
479 			    &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON],
480 			    PSWITCH_EVENT_PRESSED);
481 			break;
482 #ifndef THINKPAD_NORMAL_HOTKEYS
483 		case THINKPAD_NOTIFY_FnF1:
484 			if (sc->sc_smpsw_valid == false)
485 				break;
486 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON],
487 			    PSWITCH_EVENT_PRESSED);
488 			break;
489 		case THINKPAD_NOTIFY_PointerSwitch:
490 			if (sc->sc_smpsw_valid == false)
491 				break;
492 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON],
493 			    PSWITCH_EVENT_PRESSED);
494 			break;
495 		case THINKPAD_NOTIFY_FnF11:
496 			if (sc->sc_smpsw_valid == false)
497 				break;
498 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON],
499 			    PSWITCH_EVENT_PRESSED);
500 			break;
501 		case THINKPAD_NOTIFY_ThinkLight:
502 			if (sc->sc_smpsw_valid == false)
503 				break;
504 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT],
505 			    PSWITCH_EVENT_PRESSED);
506 			break;
507 		/*
508 		 * For some reason the next four aren't seen on my T61.
509 		 */
510 		case THINKPAD_NOTIFY_FnF10:
511 			if (sc->sc_smpsw_valid == false)
512 				break;
513 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON],
514 			    PSWITCH_EVENT_PRESSED);
515 			break;
516 		case THINKPAD_NOTIFY_VolumeUp:
517 			if (sc->sc_smpsw_valid == false)
518 				break;
519 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP],
520 			    PSWITCH_EVENT_PRESSED);
521 			break;
522 		case THINKPAD_NOTIFY_VolumeDown:
523 			if (sc->sc_smpsw_valid == false)
524 				break;
525 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN],
526 			    PSWITCH_EVENT_PRESSED);
527 			break;
528 		case THINKPAD_NOTIFY_VolumeMute:
529 			if (sc->sc_smpsw_valid == false)
530 				break;
531 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE],
532 			    PSWITCH_EVENT_PRESSED);
533 			break;
534 #else
535 		case THINKPAD_NOTIFY_FnF1:
536 		case THINKPAD_NOTIFY_PointerSwitch:
537 		case THINKPAD_NOTIFY_FnF10:
538 		case THINKPAD_NOTIFY_FnF11:
539 		case THINKPAD_NOTIFY_ThinkLight:
540 		case THINKPAD_NOTIFY_VolumeUp:
541 		case THINKPAD_NOTIFY_VolumeDown:
542 		case THINKPAD_NOTIFY_VolumeMute:
543 			/* XXXJDM we should deliver hotkeys as keycodes */
544 			break;
545 #endif /* THINKPAD_NORMAL_HOTKEYS */
546 		default:
547 			aprint_debug_dev(self, "notify event 0x%03x\n", event);
548 			break;
549 		}
550 	}
551 }
552 
553 static ACPI_STATUS
554 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask)
555 {
556 	ACPI_OBJECT param[2];
557 	ACPI_OBJECT_LIST params;
558 	ACPI_STATUS rv;
559 	int i;
560 
561 	/* Update hotkey mask */
562 	params.Count = 2;
563 	params.Pointer = param;
564 	param[0].Type = param[1].Type = ACPI_TYPE_INTEGER;
565 
566 	for (i = 0; i < 32; i++) {
567 		param[0].Integer.Value = i + 1;
568 		param[1].Integer.Value = (((1 << i) & mask) != 0);
569 
570 		rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM",
571 		    &params, NULL);
572 		if (ACPI_FAILURE(rv))
573 			return rv;
574 	}
575 
576 	/* Enable hotkey events */
577 	rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1);
578 	if (ACPI_FAILURE(rv)) {
579 		aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n",
580 		    AcpiFormatException(rv));
581 		return rv;
582 	}
583 
584 	/* Claim ownership of brightness control */
585 	(void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0);
586 
587 	return AE_OK;
588 }
589 
590 static void
591 thinkpad_sensors_init(thinkpad_softc_t *sc)
592 {
593 	int i, j;
594 
595 	if (sc->sc_ecdev == NULL)
596 		return;	/* no chance of this working */
597 
598 	sc->sc_sme = sysmon_envsys_create();
599 
600 	for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) {
601 
602 		sc->sc_sensor[i].units = ENVSYS_STEMP;
603 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
604 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
605 
606 		(void)snprintf(sc->sc_sensor[i].desc,
607 		    sizeof(sc->sc_sensor[i].desc), "temperature %d", i);
608 
609 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
610 			&sc->sc_sensor[i]) != 0)
611 			goto fail;
612 	}
613 
614 	for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) {
615 
616 		sc->sc_sensor[i].units = ENVSYS_SFANRPM;
617 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
618 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
619 
620 		(void)snprintf(sc->sc_sensor[i].desc,
621 		    sizeof(sc->sc_sensor[i].desc), "fan speed %d", j);
622 
623 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
624 			&sc->sc_sensor[i]) != 0)
625 			goto fail;
626 	}
627 
628 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
629 	sc->sc_sme->sme_cookie = sc;
630 	sc->sc_sme->sme_refresh = thinkpad_sensors_refresh;
631 
632 	if (sysmon_envsys_register(sc->sc_sme) != 0)
633 		goto fail;
634 
635 	return;
636 
637 fail:
638 	aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n");
639 	sysmon_envsys_destroy(sc->sc_sme);
640 	sc->sc_sme = NULL;
641 }
642 
643 static void
644 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
645 {
646 	switch (edata->units) {
647 	case ENVSYS_STEMP:
648 		thinkpad_temp_refresh(sme, edata);
649 		break;
650 	case ENVSYS_SFANRPM:
651 		thinkpad_fan_refresh(sme, edata);
652 		break;
653 	default:
654 		break;
655 	}
656 }
657 
658 static void
659 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
660 {
661 	thinkpad_softc_t *sc = sme->sme_cookie;
662 	char sname[5] = "TMP?";
663 	ACPI_INTEGER val;
664 	ACPI_STATUS rv;
665 	int temp;
666 
667 	sname[3] = '0' + edata->sensor;
668 	rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val);
669 	if (ACPI_FAILURE(rv)) {
670 		edata->state = ENVSYS_SINVALID;
671 		return;
672 	}
673 	temp = (int)val;
674 	if (temp > 127 || temp < -127) {
675 		edata->state = ENVSYS_SINVALID;
676 		return;
677 	}
678 
679 	edata->value_cur = temp * 1000000 + 273150000;
680 	edata->state = ENVSYS_SVALID;
681 }
682 
683 static void
684 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
685 {
686 	thinkpad_softc_t *sc = sme->sme_cookie;
687 	ACPI_INTEGER lo;
688 	ACPI_INTEGER hi;
689 	ACPI_STATUS rv;
690 	int rpm;
691 
692 	/*
693 	 * Read the low byte first to avoid a firmware bug.
694 	 */
695 	rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1);
696 	if (ACPI_FAILURE(rv)) {
697 		edata->state = ENVSYS_SINVALID;
698 		return;
699 	}
700 	rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1);
701 	if (ACPI_FAILURE(rv)) {
702 		edata->state = ENVSYS_SINVALID;
703 		return;
704 	}
705 	rpm = ((((int)hi) << 8) | ((int)lo));
706 	if (rpm < 0) {
707 		edata->state = ENVSYS_SINVALID;
708 		return;
709 	}
710 
711 	edata->value_cur = rpm;
712 	edata->state = ENVSYS_SVALID;
713 }
714 
715 static void
716 thinkpad_bluetooth_toggle(thinkpad_softc_t *sc)
717 {
718 	ACPI_BUFFER buf;
719 	ACPI_OBJECT retobj;
720 	ACPI_OBJECT param[1];
721 	ACPI_OBJECT_LIST params;
722 	ACPI_STATUS rv;
723 
724 	/* Ignore return value, as the hardware may not support bluetooth */
725 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL);
726 	if (!ACPI_FAILURE(rv))
727 		return;
728 
729 	buf.Pointer = &retobj;
730 	buf.Length = sizeof(retobj);
731 
732 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GBDC", NULL, &buf);
733 	if (ACPI_FAILURE(rv))
734 		return;
735 
736 	params.Count = 1;
737 	params.Pointer = param;
738 	param[0].Type = ACPI_TYPE_INTEGER;
739 	param[0].Integer.Value =
740 		(retobj.Integer.Value & THINKPAD_BLUETOOTH_RADIOSSW) == 0
741 		? THINKPAD_BLUETOOTH_RADIOSSW | THINKPAD_BLUETOOTH_RESUMECTRL
742 		: 0;
743 
744 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SBDC", &params, NULL);
745 }
746 
747 static void
748 thinkpad_wwan_toggle(thinkpad_softc_t *sc)
749 {
750 	ACPI_BUFFER buf;
751 	ACPI_OBJECT retobj;
752 	ACPI_OBJECT param[1];
753 	ACPI_OBJECT_LIST params;
754 	ACPI_STATUS rv;
755 
756 	buf.Pointer = &retobj;
757 	buf.Length = sizeof(retobj);
758 
759 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, &buf);
760 	if (ACPI_FAILURE(rv))
761 		return;
762 
763 	params.Count = 1;
764 	params.Pointer = param;
765 	param[0].Type = ACPI_TYPE_INTEGER;
766 	param[0].Integer.Value =
767 		(retobj.Integer.Value & THINKPAD_WWAN_RADIOSSW) == 0
768 		? THINKPAD_WWAN_RADIOSSW | THINKPAD_WWAN_RESUMECTRL
769 		: 0;
770 
771 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SWAN", &params, NULL);
772 }
773 
774 static void
775 thinkpad_uwb_toggle(thinkpad_softc_t *sc)
776 {
777 	ACPI_BUFFER buf;
778 	ACPI_OBJECT retobj;
779 	ACPI_OBJECT param[1];
780 	ACPI_OBJECT_LIST params;
781 	ACPI_STATUS rv;
782 
783 	buf.Pointer = &retobj;
784 	buf.Length = sizeof(retobj);
785 
786 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GUWB", NULL, &buf);
787 	if (ACPI_FAILURE(rv))
788 		return;
789 
790 	params.Count = 1;
791 	params.Pointer = param;
792 	param[0].Type = ACPI_TYPE_INTEGER;
793 	param[0].Integer.Value =
794 		(retobj.Integer.Value & THINKPAD_UWB_RADIOSSW) == 0
795 		? THINKPAD_UWB_RADIOSSW
796 		: 0;
797 
798 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SUWB", &params, NULL);
799 }
800 
801 static uint8_t
802 thinkpad_brightness_read(thinkpad_softc_t *sc)
803 {
804 	uint32_t val = 0;
805 
806 	AcpiOsWritePort(IO_RTC, 0x6c, 8);
807 	AcpiOsReadPort(IO_RTC + 1, &val, 8);
808 
809 	return val & 7;
810 }
811 
812 static void
813 thinkpad_brightness_up(device_t self)
814 {
815 	thinkpad_softc_t *sc = device_private(self);
816 
817 	if (thinkpad_brightness_read(sc) == 7)
818 		return;
819 
820 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP);
821 }
822 
823 static void
824 thinkpad_brightness_down(device_t self)
825 {
826 	thinkpad_softc_t *sc = device_private(self);
827 
828 	if (thinkpad_brightness_read(sc) == 0)
829 		return;
830 
831 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN);
832 }
833 
834 static void
835 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd)
836 {
837 	ACPI_STATUS rv;
838 
839 	if (sc->sc_cmoshdl == NULL)
840 		return;
841 
842 	rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd);
843 
844 	if (ACPI_FAILURE(rv))
845 		aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n",
846 		    AcpiFormatException(rv));
847 }
848 
849 static bool
850 thinkpad_resume(device_t dv, const pmf_qual_t *qual)
851 {
852 	thinkpad_softc_t *sc = device_private(dv);
853 
854 	if (sc->sc_powhdl == NULL)
855 		return true;
856 
857 	(void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true);
858 
859 	return true;
860 }
861 
862 MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power");
863 
864 #ifdef _MODULE
865 #include "ioconf.c"
866 #endif
867 
868 static int
869 thinkpad_modcmd(modcmd_t cmd, void *aux)
870 {
871 	int rv = 0;
872 
873 	switch (cmd) {
874 
875 	case MODULE_CMD_INIT:
876 
877 #ifdef _MODULE
878 		rv = config_init_component(cfdriver_ioconf_thinkpad,
879 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
880 #endif
881 		break;
882 
883 	case MODULE_CMD_FINI:
884 
885 #ifdef _MODULE
886 		rv = config_fini_component(cfdriver_ioconf_thinkpad,
887 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
888 #endif
889 		break;
890 
891 	default:
892 		rv = ENOTTY;
893 	}
894 
895 	return rv;
896 }
897