xref: /netbsd-src/sys/dev/acpi/thinkpad_acpi.c (revision b9306226a65541a8cd2d3395799b819f30f735e3)
1 /* $NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos 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.57 2024/04/27 14:50:18 christos Exp $");
31 
32 #include <sys/param.h>
33 #include <sys/device.h>
34 #include <sys/module.h>
35 #include <sys/sdt.h>
36 #include <sys/systm.h>
37 #include <sys/sysctl.h>
38 
39 #include <dev/acpi/acpireg.h>
40 #include <dev/acpi/acpivar.h>
41 #include <dev/acpi/acpi_ecvar.h>
42 #include <dev/acpi/acpi_power.h>
43 
44 #include <dev/isa/isareg.h>
45 
46 #define _COMPONENT		ACPI_RESOURCE_COMPONENT
47 ACPI_MODULE_NAME		("thinkpad_acpi")
48 
49 #define	THINKPAD_NTEMPSENSORS	8
50 #define	THINKPAD_NFANSENSORS	1
51 #define	THINKPAD_NSENSORS	(THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS)
52 
53 typedef struct tp_sysctl_param {
54 	device_t		sp_dev;
55 	int			sp_bat;
56 } tp_sysctl_param_t;
57 
58 typedef union tp_batctl {
59 	int			have_any;
60 	struct {
61 	    int			charge_start:1;
62 	    int			charge_stop:1;
63 	    int			charge_inhibit:1;
64 	    int			force_discharge:1;
65 	    int			individual_control:1;
66 	}			have;
67 } tp_batctl_t;
68 
69 typedef struct thinkpad_softc {
70 	device_t		sc_dev;
71 	device_t		sc_ecdev;
72 	struct acpi_devnode	*sc_node;
73 	struct sysctllog	*sc_log;
74 	ACPI_HANDLE		sc_powhdl;
75 	ACPI_HANDLE		sc_cmoshdl;
76 	ACPI_INTEGER		sc_ver;
77 
78 #define	TP_PSW_SLEEP		0	/* FnF4 */
79 #define	TP_PSW_HIBERNATE	1	/* FnF12 */
80 #define	TP_PSW_DISPLAY_CYCLE	2	/* FnF7 */
81 #define	TP_PSW_LOCK_SCREEN	3	/* FnF2 */
82 #define	TP_PSW_BATTERY_INFO	4	/* FnF3 */
83 #define	TP_PSW_EJECT_BUTTON	5	/* FnF9 */
84 #define	TP_PSW_ZOOM_BUTTON	6	/* FnSPACE */
85 #define	TP_PSW_VENDOR_BUTTON	7	/* ThinkVantage */
86 #define	TP_PSW_FNF1_BUTTON	8	/* FnF1 */
87 #define	TP_PSW_WIRELESS_BUTTON	9	/* FnF5 */
88 #define	TP_PSW_WWAN_BUTTON	10	/* FnF6 */
89 #define	TP_PSW_POINTER_BUTTON	11	/* FnF8 */
90 #define	TP_PSW_FNF10_BUTTON	12	/* FnF10 */
91 #define	TP_PSW_FNF11_BUTTON	13	/* FnF11 */
92 #define	TP_PSW_BRIGHTNESS_UP	14
93 #define	TP_PSW_BRIGHTNESS_DOWN	15
94 #define	TP_PSW_THINKLIGHT	16
95 #define	TP_PSW_VOLUME_UP	17
96 #define	TP_PSW_VOLUME_DOWN	18
97 #define	TP_PSW_VOLUME_MUTE	19
98 #define	TP_PSW_STAR_BUTTON	20
99 #define	TP_PSW_SCISSORS_BUTTON	21
100 #define	TP_PSW_BLUETOOTH_BUTTON	22
101 #define	TP_PSW_KEYBOARD_BUTTON	23
102 #define	TP_PSW_LAST		24
103 
104 	struct sysmon_pswitch	sc_smpsw[TP_PSW_LAST];
105 	bool			sc_smpsw_valid;
106 
107 	struct sysmon_envsys	*sc_sme;
108 	envsys_data_t		sc_sensor[THINKPAD_NSENSORS];
109 
110 	int			sc_display_state;
111 
112 #define THINKPAD_BAT_ANY	0
113 #define THINKPAD_BAT_PRIMARY	1
114 #define THINKPAD_BAT_SECONDARY	2
115 #define THINKPAD_BAT_LAST	3
116 
117 	tp_batctl_t		sc_batctl;
118 	tp_sysctl_param_t	sc_scparam[THINKPAD_BAT_LAST];
119 } thinkpad_softc_t;
120 
121 /* Hotkey events */
122 #define	THINKPAD_NOTIFY_FnF1		0x001
123 #define	THINKPAD_NOTIFY_LockScreen	0x002
124 #define	THINKPAD_NOTIFY_BatteryInfo	0x003
125 #define	THINKPAD_NOTIFY_SleepButton	0x004
126 #define	THINKPAD_NOTIFY_WirelessSwitch	0x005
127 #define	THINKPAD_NOTIFY_wWANSwitch	0x006
128 #define	THINKPAD_NOTIFY_DisplayCycle	0x007
129 #define	THINKPAD_NOTIFY_PointerSwitch	0x008
130 #define	THINKPAD_NOTIFY_EjectButton	0x009
131 #define	THINKPAD_NOTIFY_FnF10		0x00a	/* XXX: Not seen on T61 */
132 #define	THINKPAD_NOTIFY_FnF11		0x00b
133 #define	THINKPAD_NOTIFY_HibernateButton	0x00c
134 #define	THINKPAD_NOTIFY_BrightnessUp	0x010
135 #define	THINKPAD_NOTIFY_BrightnessDown	0x011
136 #define	THINKPAD_NOTIFY_ThinkLight	0x012
137 #define	THINKPAD_NOTIFY_Zoom		0x014
138 #define	THINKPAD_NOTIFY_VolumeUp	0x015	/* XXX: Not seen on T61 */
139 #define	THINKPAD_NOTIFY_VolumeDown	0x016	/* XXX: Not seen on T61 */
140 #define	THINKPAD_NOTIFY_VolumeMute	0x017	/* XXX: Not seen on T61 */
141 #define	THINKPAD_NOTIFY_ThinkVantage	0x018
142 #define	THINKPAD_NOTIFY_Star		0x311
143 #define	THINKPAD_NOTIFY_Scissors	0x312
144 #define	THINKPAD_NOTIFY_Bluetooth	0x314
145 #define	THINKPAD_NOTIFY_Keyboard	0x315
146 
147 #define	THINKPAD_CMOS_BRIGHTNESS_UP	0x04
148 #define	THINKPAD_CMOS_BRIGHTNESS_DOWN	0x05
149 
150 #define	THINKPAD_HKEY_VERSION_1		0x0100
151 #define	THINKPAD_HKEY_VERSION_2		0x0200
152 
153 #define	THINKPAD_DISPLAY_LCD		0x01
154 #define	THINKPAD_DISPLAY_CRT		0x02
155 #define	THINKPAD_DISPLAY_DVI		0x08
156 #define	THINKPAD_DISPLAY_ALL \
157 	(THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI)
158 
159 #define THINKPAD_GET_CHARGE_START	"BCTG"
160 #define THINKPAD_SET_CHARGE_START	"BCCS"
161 #define THINKPAD_GET_CHARGE_STOP	"BCSG"
162 #define THINKPAD_SET_CHARGE_STOP	"BCSS"
163 #define THINKPAD_GET_FORCE_DISCHARGE	"BDSG"
164 #define THINKPAD_SET_FORCE_DISCHARGE	"BDSS"
165 #define THINKPAD_GET_CHARGE_INHIBIT	"BICG"
166 #define THINKPAD_SET_CHARGE_INHIBIT	"BICS"
167 
168 #define THINKPAD_CALL_ERROR		0x80000000
169 
170 #define THINKPAD_BLUETOOTH_HWPRESENT	0x01
171 #define THINKPAD_BLUETOOTH_RADIOSSW	0x02
172 #define THINKPAD_BLUETOOTH_RESUMECTRL	0x04
173 
174 #define THINKPAD_WWAN_HWPRESENT		0x01
175 #define THINKPAD_WWAN_RADIOSSW		0x02
176 #define THINKPAD_WWAN_RESUMECTRL	0x04
177 
178 #define THINKPAD_UWB_HWPRESENT		0x01
179 #define THINKPAD_UWB_RADIOSSW		0x02
180 
181 #define THINKPAD_RFK_BLUETOOTH		0
182 #define THINKPAD_RFK_WWAN		1
183 #define THINKPAD_RFK_UWB		2
184 
185 static int	thinkpad_match(device_t, cfdata_t, void *);
186 static void	thinkpad_attach(device_t, device_t, void *);
187 static int	thinkpad_detach(device_t, int);
188 
189 static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t);
190 static void	thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *);
191 static void	thinkpad_get_hotkeys(void *);
192 
193 static void	thinkpad_sensors_init(thinkpad_softc_t *);
194 static void	thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
195 static void	thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
196 static void	thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *);
197 
198 static void	thinkpad_uwb_toggle(thinkpad_softc_t *);
199 static void	thinkpad_wwan_toggle(thinkpad_softc_t *);
200 static void	thinkpad_bluetooth_toggle(thinkpad_softc_t *);
201 
202 static bool	thinkpad_resume(device_t, const pmf_qual_t *);
203 static void	thinkpad_brightness_up(device_t);
204 static void	thinkpad_brightness_down(device_t);
205 static uint8_t	thinkpad_brightness_read(thinkpad_softc_t *);
206 static void	thinkpad_cmos(thinkpad_softc_t *, uint8_t);
207 
208 static void	thinkpad_battery_probe_support(device_t);
209 static void	thinkpad_battery_sysctl_setup(device_t);
210 
211 CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t),
212     thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL,
213     0);
214 
215 static const struct device_compatible_entry compat_data[] = {
216 	{ .compat = "IBM0068" },
217 	{ .compat = "LEN0068" },
218 	{ .compat = "LEN0268" },
219 	DEVICE_COMPAT_EOL
220 };
221 
222 static int
thinkpad_match(device_t parent,cfdata_t match,void * opaque)223 thinkpad_match(device_t parent, cfdata_t match, void *opaque)
224 {
225 	struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
226 	ACPI_INTEGER ver;
227 	int ret;
228 
229 	ret = acpi_compatible_match(aa, compat_data);
230 	if (ret == 0)
231 		return 0;
232 
233 	/* We only support hotkey versions 0x0100 and 0x0200 */
234 	if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV",
235 	    &ver)))
236 		return 0;
237 
238 	switch (ver) {
239 	case THINKPAD_HKEY_VERSION_1:
240 	case THINKPAD_HKEY_VERSION_2:
241 		break;
242 	default:
243 		return 0;
244 	}
245 
246 	/* Cool, looks like we're good to go */
247 	return ret;
248 }
249 
250 static void
thinkpad_attach(device_t parent,device_t self,void * opaque)251 thinkpad_attach(device_t parent, device_t self, void *opaque)
252 {
253 	thinkpad_softc_t *sc = device_private(self);
254 	struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
255 	struct sysmon_pswitch *psw;
256 	device_t curdev;
257 	deviter_t di;
258 	ACPI_STATUS rv;
259 	ACPI_INTEGER val;
260 	int i;
261 
262 	sc->sc_dev = self;
263 	sc->sc_log = NULL;
264 	sc->sc_powhdl = NULL;
265 	sc->sc_cmoshdl = NULL;
266 	sc->sc_node = aa->aa_node;
267 	sc->sc_display_state = THINKPAD_DISPLAY_LCD;
268 
269 	aprint_naive("\n");
270 	aprint_normal("\n");
271 
272 	sc->sc_ecdev = NULL;
273 	for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST);
274 	    curdev != NULL; curdev = deviter_next(&di))
275 		if (device_is_a(curdev, "acpiecdt") ||
276 		    device_is_a(curdev, "acpiec")) {
277 			sc->sc_ecdev = curdev;
278 			break;
279 		}
280 	deviter_release(&di);
281 
282 	if (sc->sc_ecdev)
283 		aprint_debug_dev(self, "using EC at %s\n",
284 		    device_xname(sc->sc_ecdev));
285 
286 	/* Query the version number */
287 	rv = acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", &sc->sc_ver);
288 	if (ACPI_FAILURE(rv)) {
289 		aprint_error_dev(self, "couldn't evaluate MHKV: %s\n",
290 		    AcpiFormatException(rv));
291 		goto fail;
292 	}
293 	aprint_normal_dev(self, "version %04x\n", (unsigned)sc->sc_ver);
294 
295 	/* Get the supported event mask */
296 	switch (sc->sc_ver) {
297 	case THINKPAD_HKEY_VERSION_1:
298 		rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val);
299 		if (ACPI_FAILURE(rv)) {
300 			aprint_error_dev(self, "couldn't evaluate MHKA: %s\n",
301 			    AcpiFormatException(rv));
302 			goto fail;
303 		}
304 		break;
305 	case THINKPAD_HKEY_VERSION_2: {
306 		ACPI_OBJECT args[1] = {
307 			[0] = { .Integer = {
308 				.Type = ACPI_TYPE_INTEGER,
309 				.Value = 1, /* hotkey events */
310 			} },
311 		};
312 		ACPI_OBJECT_LIST arglist = {
313 			.Count = __arraycount(args),
314 			.Pointer = args,
315 		};
316 		ACPI_OBJECT ret;
317 		ACPI_BUFFER buf = { .Pointer = &ret, .Length = sizeof(ret) };
318 
319 		rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKA",
320 		    &arglist, &buf);
321 		if (ACPI_FAILURE(rv)) {
322 			aprint_error_dev(self, "couldn't evaluate MHKA(1):"
323 			    " %s\n",
324 			    AcpiFormatException(rv));
325 			goto fail;
326 		}
327 		if (buf.Length == 0 || ret.Type != ACPI_TYPE_INTEGER) {
328 			aprint_error_dev(self, "failed to evaluate MHKA(1)\n");
329 			goto fail;
330 		}
331 		val = ret.Integer.Value;
332 		break;
333 	}
334 	default:
335 		panic("%s: invalid version %jd", device_xname(self),
336 		    (intmax_t)sc->sc_ver);
337 	}
338 
339 	/* Enable all supported events */
340 	rv = thinkpad_mask_init(sc, val);
341 	if (ACPI_FAILURE(rv)) {
342 		aprint_error_dev(self, "couldn't set event mask: %s\n",
343 		    AcpiFormatException(rv));
344 		goto fail;
345 	}
346 
347 	(void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler);
348 
349 	/*
350 	 * Obtain a handle for CMOS commands. This is used by T61.
351 	 */
352 	(void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl);
353 
354 	/*
355 	 * Obtain a handle to the power resource available on many models.
356 	 * Since pmf(9) is not yet integrated with the ACPI power resource
357 	 * code, this must be turned on manually upon resume. Otherwise the
358 	 * system may, for instance, resume from S3 with usb(4) powered down.
359 	 */
360 	(void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl);
361 
362 	/* Register power switches with sysmon */
363 	psw = sc->sc_smpsw;
364 	sc->sc_smpsw_valid = true;
365 
366 	psw[TP_PSW_SLEEP].smpsw_name = device_xname(self);
367 	psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
368 #if notyet
369 	psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self);
370 	mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE;
371 #endif
372 	for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++)
373 		sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY;
374 
375 	psw[TP_PSW_DISPLAY_CYCLE].smpsw_name	= PSWITCH_HK_DISPLAY_CYCLE;
376 	psw[TP_PSW_LOCK_SCREEN].smpsw_name	= PSWITCH_HK_LOCK_SCREEN;
377 	psw[TP_PSW_BATTERY_INFO].smpsw_name	= PSWITCH_HK_BATTERY_INFO;
378 	psw[TP_PSW_EJECT_BUTTON].smpsw_name	= PSWITCH_HK_EJECT_BUTTON;
379 	psw[TP_PSW_ZOOM_BUTTON].smpsw_name	= PSWITCH_HK_ZOOM_BUTTON;
380 	psw[TP_PSW_VENDOR_BUTTON].smpsw_name	= PSWITCH_HK_VENDOR_BUTTON;
381 #ifndef THINKPAD_NORMAL_HOTKEYS
382 	psw[TP_PSW_FNF1_BUTTON].smpsw_name	= PSWITCH_HK_FNF1_BUTTON;
383 	psw[TP_PSW_WIRELESS_BUTTON].smpsw_name	= PSWITCH_HK_WIRELESS_BUTTON;
384 	psw[TP_PSW_WWAN_BUTTON].smpsw_name	= PSWITCH_HK_WWAN_BUTTON;
385 	psw[TP_PSW_POINTER_BUTTON].smpsw_name	= PSWITCH_HK_POINTER_BUTTON;
386 	psw[TP_PSW_FNF10_BUTTON].smpsw_name	= PSWITCH_HK_FNF10_BUTTON;
387 	psw[TP_PSW_FNF11_BUTTON].smpsw_name	= PSWITCH_HK_FNF11_BUTTON;
388 	psw[TP_PSW_BRIGHTNESS_UP].smpsw_name	= PSWITCH_HK_BRIGHTNESS_UP;
389 	psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name	= PSWITCH_HK_BRIGHTNESS_DOWN;
390 	psw[TP_PSW_THINKLIGHT].smpsw_name	= PSWITCH_HK_THINKLIGHT;
391 	psw[TP_PSW_VOLUME_UP].smpsw_name	= PSWITCH_HK_VOLUME_UP;
392 	psw[TP_PSW_VOLUME_DOWN].smpsw_name	= PSWITCH_HK_VOLUME_DOWN;
393 	psw[TP_PSW_VOLUME_MUTE].smpsw_name	= PSWITCH_HK_VOLUME_MUTE;
394 	psw[TP_PSW_STAR_BUTTON].smpsw_name	= PSWITCH_HK_STAR_BUTTON;
395 	psw[TP_PSW_SCISSORS_BUTTON].smpsw_name	= PSWITCH_HK_SCISSORS_BUTTON;
396 	psw[TP_PSW_BLUETOOTH_BUTTON].smpsw_name	= PSWITCH_HK_BLUETOOTH_BUTTON;
397 	psw[TP_PSW_KEYBOARD_BUTTON].smpsw_name	= PSWITCH_HK_KEYBOARD_BUTTON;
398 #endif /* THINKPAD_NORMAL_HOTKEYS */
399 
400 	for (i = 0; i < TP_PSW_LAST; i++) {
401 		/* not supported yet */
402 		if (i == TP_PSW_HIBERNATE)
403 			continue;
404 		if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
405 			aprint_error_dev(self,
406 			    "couldn't register with sysmon\n");
407 			sc->sc_smpsw_valid = false;
408 			break;
409 		}
410 	}
411 
412 	/* Register temperature and fan sensors with envsys */
413 	thinkpad_sensors_init(sc);
414 
415 	/* Probe supported battery charge/control operations */
416 	thinkpad_battery_probe_support(self);
417 
418 	if (sc->sc_batctl.have_any) {
419 		for (i = 0; i < THINKPAD_BAT_LAST; i++) {
420 			sc->sc_scparam[i].sp_dev = self;
421 			sc->sc_scparam[i].sp_bat = i;
422 		}
423 		thinkpad_battery_sysctl_setup(self);
424 	}
425 
426 fail:
427 	if (!pmf_device_register(self, NULL, thinkpad_resume))
428 		aprint_error_dev(self, "couldn't establish power handler\n");
429 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
430 	    thinkpad_brightness_up, true))
431 		aprint_error_dev(self, "couldn't register event handler\n");
432 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
433 	    thinkpad_brightness_down, true))
434 		aprint_error_dev(self, "couldn't register event handler\n");
435 }
436 
437 static int
thinkpad_detach(device_t self,int flags)438 thinkpad_detach(device_t self, int flags)
439 {
440 	struct thinkpad_softc *sc = device_private(self);
441 	int i;
442 
443 	acpi_deregister_notify(sc->sc_node);
444 
445 	for (i = 0; i < TP_PSW_LAST; i++)
446 		sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
447 
448 	if (sc->sc_sme != NULL)
449 		sysmon_envsys_unregister(sc->sc_sme);
450 
451 	if (sc->sc_log != NULL)
452 		sysctl_teardown(&sc->sc_log);
453 
454 	pmf_device_deregister(self);
455 
456 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP,
457 	    thinkpad_brightness_up, true);
458 
459 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
460 	    thinkpad_brightness_down, true);
461 
462 	return 0;
463 }
464 
465 static void
thinkpad_notify_handler(ACPI_HANDLE hdl,uint32_t notify,void * opaque)466 thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
467 {
468 	device_t self = opaque;
469 	thinkpad_softc_t *sc;
470 
471 	sc = device_private(self);
472 
473 	if (notify != 0x80) {
474 		aprint_debug_dev(self, "unknown notify 0x%02x\n", notify);
475 		return;
476 	}
477 
478 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc);
479 }
480 
481 SDT_PROBE_DEFINE2(sdt, thinkpad, hotkey, MHKP,
482     "struct thinkpad_softc *"/*sc*/,
483     "ACPI_INTEGER"/*val*/);
484 
485 static void
thinkpad_get_hotkeys(void * opaque)486 thinkpad_get_hotkeys(void *opaque)
487 {
488 	thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque;
489 	device_t self = sc->sc_dev;
490 	ACPI_STATUS rv;
491 	ACPI_INTEGER val;
492 	int type, event;
493 
494 	for (;;) {
495 		rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val);
496 		if (ACPI_FAILURE(rv)) {
497 			aprint_error_dev(self, "couldn't evaluate MHKP: %s\n",
498 			    AcpiFormatException(rv));
499 			return;
500 		}
501 		SDT_PROBE2(sdt, thinkpad, hotkey, MHKP,  sc, val);
502 
503 		if (val == 0)
504 			return;
505 
506 		type = (val & 0xf000) >> 12;
507 		event = val & 0x0fff;
508 
509 		if (type != 1)
510 			/* Only type 1 events are supported for now */
511 			continue;
512 
513 		switch (event) {
514 		case THINKPAD_NOTIFY_BrightnessUp:
515 			thinkpad_brightness_up(self);
516 #ifndef THINKPAD_NORMAL_HOTKEYS
517 			if (sc->sc_smpsw_valid == false)
518 				break;
519 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP],
520 			    PSWITCH_EVENT_PRESSED);
521 #endif
522 			break;
523 		case THINKPAD_NOTIFY_BrightnessDown:
524 			thinkpad_brightness_down(self);
525 #ifndef THINKPAD_NORMAL_HOTKEYS
526 			if (sc->sc_smpsw_valid == false)
527 				break;
528 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN],
529 			    PSWITCH_EVENT_PRESSED);
530 #endif
531 			break;
532 		case THINKPAD_NOTIFY_WirelessSwitch:
533 			thinkpad_uwb_toggle(sc);
534 			thinkpad_wwan_toggle(sc);
535 			thinkpad_bluetooth_toggle(sc);
536 #ifndef THINKPAD_NORMAL_HOTKEYS
537 			if (sc->sc_smpsw_valid == false)
538 				break;
539 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON],
540 			    PSWITCH_EVENT_PRESSED);
541 #endif
542 			break;
543 		case THINKPAD_NOTIFY_Bluetooth:
544 			thinkpad_bluetooth_toggle(sc);
545 #ifndef THINKPAD_NORMAL_HOTKEYS
546 			if (sc->sc_smpsw_valid == false)
547 				break;
548 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BLUETOOTH_BUTTON],
549 			    PSWITCH_EVENT_PRESSED);
550 #endif
551 			break;
552 		case THINKPAD_NOTIFY_wWANSwitch:
553 			thinkpad_wwan_toggle(sc);
554 #ifndef THINKPAD_NORMAL_HOTKEYS
555 			if (sc->sc_smpsw_valid == false)
556 				break;
557 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON],
558 			    PSWITCH_EVENT_PRESSED);
559 #endif
560 			break;
561 		case THINKPAD_NOTIFY_SleepButton:
562 			if (sc->sc_smpsw_valid == false)
563 				break;
564 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP],
565 			    PSWITCH_EVENT_PRESSED);
566 			break;
567 		case THINKPAD_NOTIFY_HibernateButton:
568 #if notyet
569 			if (sc->sc_smpsw_valid == false)
570 				break;
571 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE],
572 			    PSWITCH_EVENT_PRESSED);
573 #endif
574 			break;
575 		case THINKPAD_NOTIFY_DisplayCycle:
576 			if (sc->sc_smpsw_valid == false)
577 				break;
578 			sysmon_pswitch_event(
579 			    &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE],
580 			    PSWITCH_EVENT_PRESSED);
581 			break;
582 		case THINKPAD_NOTIFY_LockScreen:
583 			if (sc->sc_smpsw_valid == false)
584 				break;
585 			sysmon_pswitch_event(
586 			    &sc->sc_smpsw[TP_PSW_LOCK_SCREEN],
587 			    PSWITCH_EVENT_PRESSED);
588 			break;
589 		case THINKPAD_NOTIFY_BatteryInfo:
590 			if (sc->sc_smpsw_valid == false)
591 				break;
592 			sysmon_pswitch_event(
593 			    &sc->sc_smpsw[TP_PSW_BATTERY_INFO],
594 			    PSWITCH_EVENT_PRESSED);
595 			break;
596 		case THINKPAD_NOTIFY_EjectButton:
597 			if (sc->sc_smpsw_valid == false)
598 				break;
599 			sysmon_pswitch_event(
600 			    &sc->sc_smpsw[TP_PSW_EJECT_BUTTON],
601 			    PSWITCH_EVENT_PRESSED);
602 			break;
603 		case THINKPAD_NOTIFY_Zoom:
604 			if (sc->sc_smpsw_valid == false)
605 				break;
606 			sysmon_pswitch_event(
607 			    &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON],
608 			    PSWITCH_EVENT_PRESSED);
609 			break;
610 		case THINKPAD_NOTIFY_ThinkVantage:
611 			if (sc->sc_smpsw_valid == false)
612 				break;
613 			sysmon_pswitch_event(
614 			    &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON],
615 			    PSWITCH_EVENT_PRESSED);
616 			break;
617 #ifndef THINKPAD_NORMAL_HOTKEYS
618 		case THINKPAD_NOTIFY_FnF1:
619 			if (sc->sc_smpsw_valid == false)
620 				break;
621 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON],
622 			    PSWITCH_EVENT_PRESSED);
623 			break;
624 		case THINKPAD_NOTIFY_PointerSwitch:
625 			if (sc->sc_smpsw_valid == false)
626 				break;
627 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON],
628 			    PSWITCH_EVENT_PRESSED);
629 			break;
630 		case THINKPAD_NOTIFY_FnF11:
631 			if (sc->sc_smpsw_valid == false)
632 				break;
633 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON],
634 			    PSWITCH_EVENT_PRESSED);
635 			break;
636 		case THINKPAD_NOTIFY_ThinkLight:
637 			if (sc->sc_smpsw_valid == false)
638 				break;
639 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT],
640 			    PSWITCH_EVENT_PRESSED);
641 			break;
642 		/*
643 		 * For some reason the next four aren't seen on my T61.
644 		 */
645 		case THINKPAD_NOTIFY_FnF10:
646 			if (sc->sc_smpsw_valid == false)
647 				break;
648 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON],
649 			    PSWITCH_EVENT_PRESSED);
650 			break;
651 		case THINKPAD_NOTIFY_VolumeUp:
652 			if (sc->sc_smpsw_valid == false)
653 				break;
654 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP],
655 			    PSWITCH_EVENT_PRESSED);
656 			break;
657 		case THINKPAD_NOTIFY_VolumeDown:
658 			if (sc->sc_smpsw_valid == false)
659 				break;
660 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN],
661 			    PSWITCH_EVENT_PRESSED);
662 			break;
663 		case THINKPAD_NOTIFY_VolumeMute:
664 			if (sc->sc_smpsw_valid == false)
665 				break;
666 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE],
667 			    PSWITCH_EVENT_PRESSED);
668 			break;
669 		case THINKPAD_NOTIFY_Star:
670 			if (sc->sc_smpsw_valid == false)
671 				break;
672 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_STAR_BUTTON],
673 			    PSWITCH_EVENT_PRESSED);
674 			break;
675 		case THINKPAD_NOTIFY_Scissors:
676 			if (sc->sc_smpsw_valid == false)
677 				break;
678 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SCISSORS_BUTTON],
679 			    PSWITCH_EVENT_PRESSED);
680 			break;
681 		case THINKPAD_NOTIFY_Keyboard:
682 			if (sc->sc_smpsw_valid == false)
683 				break;
684 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_KEYBOARD_BUTTON],
685 			    PSWITCH_EVENT_PRESSED);
686 			break;
687 #else
688 		case THINKPAD_NOTIFY_FnF1:
689 		case THINKPAD_NOTIFY_PointerSwitch:
690 		case THINKPAD_NOTIFY_FnF10:
691 		case THINKPAD_NOTIFY_FnF11:
692 		case THINKPAD_NOTIFY_ThinkLight:
693 		case THINKPAD_NOTIFY_VolumeUp:
694 		case THINKPAD_NOTIFY_VolumeDown:
695 		case THINKPAD_NOTIFY_VolumeMute:
696 		case THINKPAD_NOTIFY_Star:
697 		case THINKPAD_NOTIFY_Scissors:
698 		case THINKPAD_NOTIFY_Keyboard:
699 			/* XXXJDM we should deliver hotkeys as keycodes */
700 			break;
701 #endif /* THINKPAD_NORMAL_HOTKEYS */
702 		default:
703 			aprint_debug_dev(self, "notify event 0x%03x\n", event);
704 			break;
705 		}
706 	}
707 }
708 
709 static ACPI_STATUS
thinkpad_mask_init(thinkpad_softc_t * sc,uint32_t mask)710 thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask)
711 {
712 	ACPI_OBJECT param[2];
713 	ACPI_OBJECT_LIST params;
714 	ACPI_STATUS rv;
715 	int i;
716 
717 	/* Update hotkey mask */
718 	params.Count = 2;
719 	params.Pointer = param;
720 	param[0].Type = param[1].Type = ACPI_TYPE_INTEGER;
721 
722 	for (i = 0; i < 32; i++) {
723 		param[0].Integer.Value = i + 1;
724 		param[1].Integer.Value = ((__BIT(i) & mask) != 0);
725 
726 		rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM",
727 		    &params, NULL);
728 		if (ACPI_FAILURE(rv))
729 			return rv;
730 	}
731 
732 	/* Enable hotkey events */
733 	rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1);
734 	if (ACPI_FAILURE(rv)) {
735 		aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n",
736 		    AcpiFormatException(rv));
737 		return rv;
738 	}
739 
740 	/* Claim ownership of brightness control */
741 	(void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0);
742 
743 	return AE_OK;
744 }
745 
746 static void
thinkpad_sensors_init(thinkpad_softc_t * sc)747 thinkpad_sensors_init(thinkpad_softc_t *sc)
748 {
749 	int i, j;
750 
751 	if (sc->sc_ecdev == NULL)
752 		return;	/* no chance of this working */
753 
754 	sc->sc_sme = sysmon_envsys_create();
755 
756 	for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) {
757 
758 		sc->sc_sensor[i].units = ENVSYS_STEMP;
759 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
760 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
761 
762 		(void)snprintf(sc->sc_sensor[i].desc,
763 		    sizeof(sc->sc_sensor[i].desc), "temperature %d", i);
764 
765 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
766 			&sc->sc_sensor[i]) != 0)
767 			goto fail;
768 	}
769 
770 	for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) {
771 
772 		sc->sc_sensor[i].units = ENVSYS_SFANRPM;
773 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
774 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
775 
776 		(void)snprintf(sc->sc_sensor[i].desc,
777 		    sizeof(sc->sc_sensor[i].desc), "fan speed %d", j);
778 
779 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
780 			&sc->sc_sensor[i]) != 0)
781 			goto fail;
782 	}
783 
784 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
785 	sc->sc_sme->sme_cookie = sc;
786 	sc->sc_sme->sme_refresh = thinkpad_sensors_refresh;
787 
788 	if (sysmon_envsys_register(sc->sc_sme) != 0)
789 		goto fail;
790 
791 	return;
792 
793 fail:
794 	aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n");
795 	sysmon_envsys_destroy(sc->sc_sme);
796 	sc->sc_sme = NULL;
797 }
798 
799 static void
thinkpad_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)800 thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
801 {
802 	switch (edata->units) {
803 	case ENVSYS_STEMP:
804 		thinkpad_temp_refresh(sme, edata);
805 		break;
806 	case ENVSYS_SFANRPM:
807 		thinkpad_fan_refresh(sme, edata);
808 		break;
809 	default:
810 		break;
811 	}
812 }
813 
814 static void
thinkpad_temp_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)815 thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
816 {
817 	thinkpad_softc_t *sc = sme->sme_cookie;
818 	char sname[5] = "TMP?";
819 	ACPI_INTEGER val;
820 	ACPI_STATUS rv;
821 	int temp;
822 
823 	sname[3] = '0' + edata->sensor;
824 	rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val);
825 	if (ACPI_FAILURE(rv)) {
826 		edata->state = ENVSYS_SINVALID;
827 		return;
828 	}
829 	temp = (int)val;
830 	if (temp > 127 || temp < -127) {
831 		edata->state = ENVSYS_SINVALID;
832 		return;
833 	}
834 
835 	edata->value_cur = temp * 1000000 + 273150000;
836 	edata->state = ENVSYS_SVALID;
837 }
838 
839 static void
thinkpad_fan_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)840 thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
841 {
842 	thinkpad_softc_t *sc = sme->sme_cookie;
843 	ACPI_INTEGER lo;
844 	ACPI_INTEGER hi;
845 	ACPI_STATUS rv;
846 	int rpm;
847 
848 	/*
849 	 * Read the low byte first to avoid a firmware bug.
850 	 */
851 	rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1);
852 	if (ACPI_FAILURE(rv)) {
853 		edata->state = ENVSYS_SINVALID;
854 		return;
855 	}
856 	rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1);
857 	if (ACPI_FAILURE(rv)) {
858 		edata->state = ENVSYS_SINVALID;
859 		return;
860 	}
861 
862 	rpm = ((((int)hi) << 8) | ((int)lo));
863 	if (rpm < 0) {
864 		edata->state = ENVSYS_SINVALID;
865 		return;
866 	}
867 
868 	edata->value_cur = rpm;
869 	edata->state = ENVSYS_SVALID;
870 }
871 
872 static void
thinkpad_bluetooth_toggle(thinkpad_softc_t * sc)873 thinkpad_bluetooth_toggle(thinkpad_softc_t *sc)
874 {
875 	ACPI_BUFFER buf;
876 	ACPI_OBJECT retobj;
877 	ACPI_OBJECT param[1];
878 	ACPI_OBJECT_LIST params;
879 	ACPI_STATUS rv;
880 
881 	/* Ignore return value, as the hardware may not support bluetooth */
882 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL);
883 	if (!ACPI_FAILURE(rv))
884 		return;
885 
886 	buf.Pointer = &retobj;
887 	buf.Length = sizeof(retobj);
888 
889 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GBDC", NULL, &buf);
890 	if (ACPI_FAILURE(rv))
891 		return;
892 
893 	params.Count = 1;
894 	params.Pointer = param;
895 	param[0].Type = ACPI_TYPE_INTEGER;
896 	param[0].Integer.Value =
897 		(retobj.Integer.Value & THINKPAD_BLUETOOTH_RADIOSSW) == 0
898 		? THINKPAD_BLUETOOTH_RADIOSSW | THINKPAD_BLUETOOTH_RESUMECTRL
899 		: 0;
900 
901 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SBDC", &params, NULL);
902 }
903 
904 static void
thinkpad_wwan_toggle(thinkpad_softc_t * sc)905 thinkpad_wwan_toggle(thinkpad_softc_t *sc)
906 {
907 	ACPI_BUFFER buf;
908 	ACPI_OBJECT retobj;
909 	ACPI_OBJECT param[1];
910 	ACPI_OBJECT_LIST params;
911 	ACPI_STATUS rv;
912 
913 	buf.Pointer = &retobj;
914 	buf.Length = sizeof(retobj);
915 
916 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, &buf);
917 	if (ACPI_FAILURE(rv))
918 		return;
919 
920 	params.Count = 1;
921 	params.Pointer = param;
922 	param[0].Type = ACPI_TYPE_INTEGER;
923 	param[0].Integer.Value =
924 		(retobj.Integer.Value & THINKPAD_WWAN_RADIOSSW) == 0
925 		? THINKPAD_WWAN_RADIOSSW | THINKPAD_WWAN_RESUMECTRL
926 		: 0;
927 
928 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SWAN", &params, NULL);
929 }
930 
931 static void
thinkpad_uwb_toggle(thinkpad_softc_t * sc)932 thinkpad_uwb_toggle(thinkpad_softc_t *sc)
933 {
934 	ACPI_BUFFER buf;
935 	ACPI_OBJECT retobj;
936 	ACPI_OBJECT param[1];
937 	ACPI_OBJECT_LIST params;
938 	ACPI_STATUS rv;
939 
940 	buf.Pointer = &retobj;
941 	buf.Length = sizeof(retobj);
942 
943 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GUWB", NULL, &buf);
944 	if (ACPI_FAILURE(rv))
945 		return;
946 
947 	params.Count = 1;
948 	params.Pointer = param;
949 	param[0].Type = ACPI_TYPE_INTEGER;
950 	param[0].Integer.Value =
951 		(retobj.Integer.Value & THINKPAD_UWB_RADIOSSW) == 0
952 		? THINKPAD_UWB_RADIOSSW
953 		: 0;
954 
955 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SUWB", &params, NULL);
956 }
957 
958 static uint8_t
thinkpad_brightness_read(thinkpad_softc_t * sc)959 thinkpad_brightness_read(thinkpad_softc_t *sc)
960 {
961 	uint32_t val = 0;
962 
963 	AcpiOsWritePort(IO_RTC, 0x6c, 8);
964 	AcpiOsReadPort(IO_RTC + 1, &val, 8);
965 
966 	return val & 7;
967 }
968 
969 static void
thinkpad_brightness_up(device_t self)970 thinkpad_brightness_up(device_t self)
971 {
972 	thinkpad_softc_t *sc = device_private(self);
973 
974 	if (thinkpad_brightness_read(sc) == 7)
975 		return;
976 
977 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP);
978 }
979 
980 static void
thinkpad_brightness_down(device_t self)981 thinkpad_brightness_down(device_t self)
982 {
983 	thinkpad_softc_t *sc = device_private(self);
984 
985 	if (thinkpad_brightness_read(sc) == 0)
986 		return;
987 
988 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN);
989 }
990 
991 static void
thinkpad_cmos(thinkpad_softc_t * sc,uint8_t cmd)992 thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd)
993 {
994 	ACPI_STATUS rv;
995 
996 	if (sc->sc_cmoshdl == NULL)
997 		return;
998 
999 	rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd);
1000 
1001 	if (ACPI_FAILURE(rv))
1002 		aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n",
1003 		    AcpiFormatException(rv));
1004 }
1005 
1006 static uint32_t
thinkpad_call_method(device_t self,const char * path,uint32_t arg)1007 thinkpad_call_method(device_t self, const char *path, uint32_t arg)
1008 {
1009 	thinkpad_softc_t *sc = device_private(self);
1010 	ACPI_HANDLE handle = sc->sc_node->ad_handle;
1011 	ACPI_OBJECT args[1];
1012 	ACPI_OBJECT_LIST arg_list;
1013 	ACPI_OBJECT rval;
1014 	ACPI_BUFFER buf;
1015 	ACPI_STATUS rv;
1016 
1017 	args[0].Type = ACPI_TYPE_INTEGER;
1018 	args[0].Integer.Value = arg;
1019 	arg_list.Pointer = &args[0];
1020 	arg_list.Count = __arraycount(args);
1021 
1022 	memset(&rval, 0, sizeof rval);
1023 	buf.Pointer = &rval;
1024 	buf.Length = sizeof rval;
1025 
1026 	rv = AcpiEvaluateObjectTyped(handle, path, &arg_list, &buf,
1027 	    ACPI_TYPE_INTEGER);
1028 
1029 	if (ACPI_FAILURE(rv)) {
1030 		aprint_error_dev(self, "call %s.%s(%x) failed: %s\n",
1031 		    acpi_name(handle), path, (unsigned)arg,
1032 		    AcpiFormatException(rv));
1033 		return THINKPAD_CALL_ERROR;
1034 	}
1035 
1036 	return rval.Integer.Value;
1037 }
1038 
1039 static void
thinkpad_battery_probe_support(device_t self)1040 thinkpad_battery_probe_support(device_t self)
1041 {
1042 	thinkpad_softc_t *sc = device_private(self);
1043 	ACPI_HANDLE hdl = sc->sc_node->ad_handle, tmp;
1044 	ACPI_STATUS rv;
1045 	uint32_t val;
1046 
1047 	sc->sc_batctl.have_any = 0;
1048 
1049 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_START, &tmp);
1050 	if (ACPI_SUCCESS(rv)) {
1051 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_START,
1052 		    THINKPAD_BAT_PRIMARY);
1053 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100)) {
1054 			sc->sc_batctl.have.charge_start = 1;
1055 			if (val & 0x200)
1056 				sc->sc_batctl.have.individual_control = 1;
1057 		}
1058 	}
1059 
1060 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_STOP, &tmp);
1061 	if (ACPI_SUCCESS(rv)) {
1062 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_STOP,
1063 		    THINKPAD_BAT_PRIMARY);
1064 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100))
1065 			sc->sc_batctl.have.charge_stop = 1;
1066 	}
1067 
1068 	rv = AcpiGetHandle(hdl, THINKPAD_GET_FORCE_DISCHARGE, &tmp);
1069 	if (ACPI_SUCCESS(rv)) {
1070 		val = thinkpad_call_method(self, THINKPAD_GET_FORCE_DISCHARGE,
1071 		    THINKPAD_BAT_PRIMARY);
1072 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100))
1073 			sc->sc_batctl.have.force_discharge = 1;
1074 	}
1075 
1076 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_INHIBIT, &tmp);
1077 	if (ACPI_SUCCESS(rv)) {
1078 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_INHIBIT,
1079 		    THINKPAD_BAT_PRIMARY);
1080 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x20))
1081 			sc->sc_batctl.have.charge_inhibit = 1;
1082 	}
1083 
1084 	if (sc->sc_batctl.have_any)
1085 		aprint_verbose_dev(self, "battery control capabilities: %x\n",
1086 		    sc->sc_batctl.have_any);
1087 }
1088 
1089 static int
thinkpad_battery_sysctl_charge_start(SYSCTLFN_ARGS)1090 thinkpad_battery_sysctl_charge_start(SYSCTLFN_ARGS)
1091 {
1092 	struct sysctlnode node = *rnode;
1093 	tp_sysctl_param_t *sp = node.sysctl_data;
1094 	int charge_start;
1095 	int err;
1096 
1097 	charge_start = thinkpad_call_method(sp->sp_dev,
1098 	    THINKPAD_GET_CHARGE_START, sp->sp_bat) & 0xff;
1099 
1100 	node.sysctl_data = &charge_start;
1101 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1102 	if (err || newp == NULL)
1103 		return err;
1104 
1105 	if (charge_start < 0 || charge_start > 99)
1106 		return EINVAL;
1107 
1108 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_START,
1109 	    charge_start | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1110 		return EIO;
1111 
1112 	return 0;
1113 }
1114 
1115 static int
thinkpad_battery_sysctl_charge_stop(SYSCTLFN_ARGS)1116 thinkpad_battery_sysctl_charge_stop(SYSCTLFN_ARGS)
1117 {
1118 	struct sysctlnode node = *rnode;
1119 	tp_sysctl_param_t *sp = node.sysctl_data;
1120 	int charge_stop;
1121 	int err;
1122 
1123 	charge_stop = thinkpad_call_method(sp->sp_dev,
1124 	    THINKPAD_GET_CHARGE_STOP, sp->sp_bat) & 0xff;
1125 
1126 	if (charge_stop == 0)
1127 		charge_stop = 100;
1128 
1129 	node.sysctl_data = &charge_stop;
1130 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1131 	if (err || newp == NULL)
1132 		return err;
1133 
1134 	if (charge_stop < 1 || charge_stop > 100)
1135 		return EINVAL;
1136 
1137 	if (charge_stop == 100)
1138 		charge_stop = 0;
1139 
1140 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_STOP,
1141 	    charge_stop | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1142 		return EIO;
1143 
1144 	return 0;
1145 }
1146 
1147 static int
thinkpad_battery_sysctl_charge_inhibit(SYSCTLFN_ARGS)1148 thinkpad_battery_sysctl_charge_inhibit(SYSCTLFN_ARGS)
1149 {
1150 	struct sysctlnode node = *rnode;
1151 	tp_sysctl_param_t *sp = node.sysctl_data;
1152 	bool charge_inhibit;
1153 	int err;
1154 
1155 	charge_inhibit = thinkpad_call_method(sp->sp_dev,
1156 	    THINKPAD_GET_CHARGE_INHIBIT, sp->sp_bat) & 0x01;
1157 
1158 	node.sysctl_data = &charge_inhibit;
1159 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1160 	if (err || newp == NULL)
1161 		return err;
1162 
1163 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_INHIBIT,
1164 	    charge_inhibit | sp->sp_bat<<4 | 0xffff<<8) & THINKPAD_CALL_ERROR)
1165 		return EIO;
1166 
1167 	return 0;
1168 }
1169 
1170 static int
thinkpad_battery_sysctl_force_discharge(SYSCTLFN_ARGS)1171 thinkpad_battery_sysctl_force_discharge(SYSCTLFN_ARGS)
1172 {
1173 	struct sysctlnode node = *rnode;
1174 	tp_sysctl_param_t *sp = node.sysctl_data;
1175 	bool force_discharge;
1176 	int err;
1177 
1178 	force_discharge = thinkpad_call_method(sp->sp_dev,
1179 	    THINKPAD_GET_FORCE_DISCHARGE, sp->sp_bat) & 0x01;
1180 
1181 	node.sysctl_data = &force_discharge;
1182 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1183 	if (err || newp == NULL)
1184 		return err;
1185 
1186 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_FORCE_DISCHARGE,
1187 	    force_discharge | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1188 		return EIO;
1189 
1190 	return 0;
1191 }
1192 
1193 static void
thinkpad_battery_sysctl_setup_controls(device_t self,const struct sysctlnode * rnode,int battery)1194 thinkpad_battery_sysctl_setup_controls(device_t self,
1195     const struct sysctlnode *rnode, int battery)
1196 {
1197 	thinkpad_softc_t *sc = device_private(self);
1198 
1199 	if (sc->sc_batctl.have.charge_start)
1200 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1201 		    CTLFLAG_READWRITE, CTLTYPE_INT, "charge_start",
1202 		    SYSCTL_DESCR("charge start threshold (0-99)"),
1203 		    thinkpad_battery_sysctl_charge_start, 0,
1204 		    (void *)&(sc->sc_scparam[battery]), 0,
1205 		    CTL_CREATE, CTL_EOL);
1206 
1207 	if (sc->sc_batctl.have.charge_stop)
1208 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1209 		    CTLFLAG_READWRITE, CTLTYPE_INT, "charge_stop",
1210 		    SYSCTL_DESCR("charge stop threshold (1-100)"),
1211 		    thinkpad_battery_sysctl_charge_stop, 0,
1212 		    (void *)&(sc->sc_scparam[battery]), 0,
1213 		    CTL_CREATE, CTL_EOL);
1214 
1215 	if (sc->sc_batctl.have.charge_inhibit)
1216 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1217 		    CTLFLAG_READWRITE, CTLTYPE_BOOL, "charge_inhibit",
1218 		    SYSCTL_DESCR("charge inhibit"),
1219 		    thinkpad_battery_sysctl_charge_inhibit, 0,
1220 		    (void *)&(sc->sc_scparam[battery]), 0,
1221 		    CTL_CREATE, CTL_EOL);
1222 
1223 	if (sc->sc_batctl.have.force_discharge)
1224 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1225 		    CTLFLAG_READWRITE, CTLTYPE_BOOL, "force_discharge",
1226 		    SYSCTL_DESCR("force discharge"),
1227 		    thinkpad_battery_sysctl_force_discharge, 0,
1228 		    (void *)&(sc->sc_scparam[battery]), 0,
1229 		    CTL_CREATE, CTL_EOL);
1230 }
1231 
1232 static void
thinkpad_battery_sysctl_setup(device_t self)1233 thinkpad_battery_sysctl_setup(device_t self)
1234 {
1235 	thinkpad_softc_t *sc = device_private(self);
1236 	const struct sysctlnode *rnode, *cnode;
1237 	int err;
1238 
1239 	err = sysctl_createv(&sc->sc_log, 0, NULL, &rnode,
1240 	    0, CTLTYPE_NODE, "acpi", NULL,
1241 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
1242 	if (err)
1243 		goto fail;
1244 
1245 	err = sysctl_createv(&sc->sc_log, 0, &rnode, &rnode,
1246 	    0, CTLTYPE_NODE, device_xname(self),
1247 	    SYSCTL_DESCR("ThinkPad ACPI controls"),
1248 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1249 	if (err)
1250 		goto fail;
1251 
1252 	if (sc->sc_batctl.have.individual_control) {
1253 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1254 		    0, CTLTYPE_NODE, "bat0",
1255 		    SYSCTL_DESCR("battery charge controls (primary battery)"),
1256 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1257 		if (err)
1258 			goto fail;
1259 
1260 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1261 		    THINKPAD_BAT_PRIMARY);
1262 
1263 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1264 		    0, CTLTYPE_NODE, "bat1",
1265 		    SYSCTL_DESCR("battery charge controls (secondary battery)"),
1266 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1267 		if (err)
1268 			goto fail;
1269 
1270 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1271 		    THINKPAD_BAT_SECONDARY);
1272 	} else {
1273 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1274 		    0, CTLTYPE_NODE, "bat",
1275 		    SYSCTL_DESCR("battery charge controls"),
1276 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1277 		if (err)
1278 			goto fail;
1279 
1280 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1281 		    THINKPAD_BAT_ANY);
1282 	}
1283 
1284 	return;
1285 
1286 fail:
1287 	aprint_error_dev(self, "unable to add sysctl nodes (%d)\n", err);
1288 }
1289 
1290 static bool
thinkpad_resume(device_t dv,const pmf_qual_t * qual)1291 thinkpad_resume(device_t dv, const pmf_qual_t *qual)
1292 {
1293 	thinkpad_softc_t *sc = device_private(dv);
1294 
1295 	if (sc->sc_powhdl == NULL)
1296 		return true;
1297 
1298 	(void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true);
1299 
1300 	return true;
1301 }
1302 
1303 MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power");
1304 
1305 #ifdef _MODULE
1306 #include "ioconf.c"
1307 #endif
1308 
1309 static int
thinkpad_modcmd(modcmd_t cmd,void * aux)1310 thinkpad_modcmd(modcmd_t cmd, void *aux)
1311 {
1312 	int rv = 0;
1313 
1314 	switch (cmd) {
1315 
1316 	case MODULE_CMD_INIT:
1317 
1318 #ifdef _MODULE
1319 		rv = config_init_component(cfdriver_ioconf_thinkpad,
1320 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
1321 #endif
1322 		break;
1323 
1324 	case MODULE_CMD_FINI:
1325 
1326 #ifdef _MODULE
1327 		rv = config_fini_component(cfdriver_ioconf_thinkpad,
1328 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
1329 #endif
1330 		break;
1331 
1332 	default:
1333 		rv = ENOTTY;
1334 	}
1335 
1336 	return rv;
1337 }
1338