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
47ACPI_MODULE_NAME		("thinkpad_acpi")
48
49#define	THINKPAD_NTEMPSENSORS	8
50#define	THINKPAD_NFANSENSORS	1
51#define	THINKPAD_NSENSORS	(THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS)
52
53typedef struct tp_sysctl_param {
54	device_t		sp_dev;
55	int			sp_bat;
56} tp_sysctl_param_t;
57
58typedef 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
69typedef 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
185static int	thinkpad_match(device_t, cfdata_t, void *);
186static void	thinkpad_attach(device_t, device_t, void *);
187static int	thinkpad_detach(device_t, int);
188
189static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t);
190static void	thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *);
191static void	thinkpad_get_hotkeys(void *);
192
193static void	thinkpad_sensors_init(thinkpad_softc_t *);
194static void	thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
195static void	thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
196static void	thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *);
197
198static void	thinkpad_uwb_toggle(thinkpad_softc_t *);
199static void	thinkpad_wwan_toggle(thinkpad_softc_t *);
200static void	thinkpad_bluetooth_toggle(thinkpad_softc_t *);
201
202static bool	thinkpad_resume(device_t, const pmf_qual_t *);
203static void	thinkpad_brightness_up(device_t);
204static void	thinkpad_brightness_down(device_t);
205static uint8_t	thinkpad_brightness_read(thinkpad_softc_t *);
206static void	thinkpad_cmos(thinkpad_softc_t *, uint8_t);
207
208static void	thinkpad_battery_probe_support(device_t);
209static void	thinkpad_battery_sysctl_setup(device_t);
210
211CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t),
212    thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL,
213    0);
214
215static const struct device_compatible_entry compat_data[] = {
216	{ .compat = "IBM0068" },
217	{ .compat = "LEN0068" },
218	{ .compat = "LEN0268" },
219	DEVICE_COMPAT_EOL
220};
221
222static int
223thinkpad_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
250static void
251thinkpad_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
426fail:
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
437static int
438thinkpad_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
465static void
466thinkpad_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
481SDT_PROBE_DEFINE2(sdt, thinkpad, hotkey, MHKP,
482    "struct thinkpad_softc *"/*sc*/,
483    "ACPI_INTEGER"/*val*/);
484
485static void
486thinkpad_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
709static ACPI_STATUS
710thinkpad_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
746static void
747thinkpad_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
793fail:
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
799static void
800thinkpad_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
814static void
815thinkpad_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
839static void
840thinkpad_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
872static void
873thinkpad_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
904static void
905thinkpad_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
931static void
932thinkpad_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
958static uint8_t
959thinkpad_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
969static void
970thinkpad_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
980static void
981thinkpad_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
991static void
992thinkpad_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
1006static uint32_t
1007thinkpad_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
1039static void
1040thinkpad_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
1089static int
1090thinkpad_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
1115static int
1116thinkpad_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
1147static int
1148thinkpad_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
1170static int
1171thinkpad_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
1193static void
1194thinkpad_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
1232static void
1233thinkpad_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
1286fail:
1287	aprint_error_dev(self, "unable to add sysctl nodes (%d)\n", err);
1288}
1289
1290static bool
1291thinkpad_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
1303MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power");
1304
1305#ifdef _MODULE
1306#include "ioconf.c"
1307#endif
1308
1309static int
1310thinkpad_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