xref: /netbsd-src/sys/dev/acpi/thinkpad_acpi.c (revision b9306226a65541a8cd2d3395799b819f30f735e3)
1*b9306226Schristos /* $NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos Exp $ */
25b0f734dSjmcneill 
35b0f734dSjmcneill /*-
45b0f734dSjmcneill  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
55b0f734dSjmcneill  * All rights reserved.
65b0f734dSjmcneill  *
75b0f734dSjmcneill  * Redistribution and use in source and binary forms, with or without
85b0f734dSjmcneill  * modification, are permitted provided that the following conditions
95b0f734dSjmcneill  * are met:
105b0f734dSjmcneill  * 1. Redistributions of source code must retain the above copyright
115b0f734dSjmcneill  *    notice, this list of conditions and the following disclaimer.
125b0f734dSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
135b0f734dSjmcneill  *    notice, this list of conditions and the following disclaimer in the
145b0f734dSjmcneill  *    documentation and/or other materials provided with the distribution.
155b0f734dSjmcneill  *
165b0f734dSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
175b0f734dSjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
185b0f734dSjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
195b0f734dSjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
205b0f734dSjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
215b0f734dSjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
225b0f734dSjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
235b0f734dSjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
245b0f734dSjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
255b0f734dSjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
265b0f734dSjmcneill  * POSSIBILITY OF SUCH DAMAGE.
275b0f734dSjmcneill  */
285b0f734dSjmcneill 
295b0f734dSjmcneill #include <sys/cdefs.h>
30*b9306226Schristos __KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos Exp $");
315b0f734dSjmcneill 
325b0f734dSjmcneill #include <sys/param.h>
335b0f734dSjmcneill #include <sys/device.h>
345a425210Sjruoho #include <sys/module.h>
35363e5ec8Sriastradh #include <sys/sdt.h>
365a425210Sjruoho #include <sys/systm.h>
37*b9306226Schristos #include <sys/sysctl.h>
385b0f734dSjmcneill 
39a86a8685Sjruoho #include <dev/acpi/acpireg.h>
405b0f734dSjmcneill #include <dev/acpi/acpivar.h>
41aa518c7bSjmcneill #include <dev/acpi/acpi_ecvar.h>
421e511da0Sjruoho #include <dev/acpi/acpi_power.h>
435b0f734dSjmcneill 
44da01aef3Sjmcneill #include <dev/isa/isareg.h>
455b0f734dSjmcneill 
46a86a8685Sjruoho #define _COMPONENT		ACPI_RESOURCE_COMPONENT
47a86a8685Sjruoho ACPI_MODULE_NAME		("thinkpad_acpi")
48a86a8685Sjruoho 
49e38f75f0Sjmcneill #define	THINKPAD_NTEMPSENSORS	8
50e38f75f0Sjmcneill #define	THINKPAD_NFANSENSORS	1
51e38f75f0Sjmcneill #define	THINKPAD_NSENSORS	(THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS)
52aa518c7bSjmcneill 
53*b9306226Schristos typedef struct tp_sysctl_param {
54*b9306226Schristos 	device_t		sp_dev;
55*b9306226Schristos 	int			sp_bat;
56*b9306226Schristos } tp_sysctl_param_t;
57*b9306226Schristos 
58*b9306226Schristos typedef union tp_batctl {
59*b9306226Schristos 	int			have_any;
60*b9306226Schristos 	struct {
61*b9306226Schristos 	    int			charge_start:1;
62*b9306226Schristos 	    int			charge_stop:1;
63*b9306226Schristos 	    int			charge_inhibit:1;
64*b9306226Schristos 	    int			force_discharge:1;
65*b9306226Schristos 	    int			individual_control:1;
66*b9306226Schristos 	}			have;
67*b9306226Schristos } tp_batctl_t;
68*b9306226Schristos 
695b0f734dSjmcneill typedef struct thinkpad_softc {
705b0f734dSjmcneill 	device_t		sc_dev;
71aa518c7bSjmcneill 	device_t		sc_ecdev;
725b0f734dSjmcneill 	struct acpi_devnode	*sc_node;
73*b9306226Schristos 	struct sysctllog	*sc_log;
741e511da0Sjruoho 	ACPI_HANDLE		sc_powhdl;
755b0f734dSjmcneill 	ACPI_HANDLE		sc_cmoshdl;
76622174b8Sriastradh 	ACPI_INTEGER		sc_ver;
775b0f734dSjmcneill 
78262712cdSchristos #define	TP_PSW_SLEEP		0	/* FnF4 */
79262712cdSchristos #define	TP_PSW_HIBERNATE	1	/* FnF12 */
80262712cdSchristos #define	TP_PSW_DISPLAY_CYCLE	2	/* FnF7 */
81262712cdSchristos #define	TP_PSW_LOCK_SCREEN	3	/* FnF2 */
82262712cdSchristos #define	TP_PSW_BATTERY_INFO	4	/* FnF3 */
83262712cdSchristos #define	TP_PSW_EJECT_BUTTON	5	/* FnF9 */
84262712cdSchristos #define	TP_PSW_ZOOM_BUTTON	6	/* FnSPACE */
85262712cdSchristos #define	TP_PSW_VENDOR_BUTTON	7	/* ThinkVantage */
86262712cdSchristos #define	TP_PSW_FNF1_BUTTON	8	/* FnF1 */
87262712cdSchristos #define	TP_PSW_WIRELESS_BUTTON	9	/* FnF5 */
88262712cdSchristos #define	TP_PSW_WWAN_BUTTON	10	/* FnF6 */
89262712cdSchristos #define	TP_PSW_POINTER_BUTTON	11	/* FnF8 */
90262712cdSchristos #define	TP_PSW_FNF10_BUTTON	12	/* FnF10 */
91262712cdSchristos #define	TP_PSW_FNF11_BUTTON	13	/* FnF11 */
92262712cdSchristos #define	TP_PSW_BRIGHTNESS_UP	14
93262712cdSchristos #define	TP_PSW_BRIGHTNESS_DOWN	15
94262712cdSchristos #define	TP_PSW_THINKLIGHT	16
95262712cdSchristos #define	TP_PSW_VOLUME_UP	17
96262712cdSchristos #define	TP_PSW_VOLUME_DOWN	18
97262712cdSchristos #define	TP_PSW_VOLUME_MUTE	19
9828099374Sriastradh #define	TP_PSW_STAR_BUTTON	20
9928099374Sriastradh #define	TP_PSW_SCISSORS_BUTTON	21
10028099374Sriastradh #define	TP_PSW_BLUETOOTH_BUTTON	22
10128099374Sriastradh #define	TP_PSW_KEYBOARD_BUTTON	23
10228099374Sriastradh #define	TP_PSW_LAST		24
103262712cdSchristos 
1048fce0f05Sjmcneill 	struct sysmon_pswitch	sc_smpsw[TP_PSW_LAST];
1055b0f734dSjmcneill 	bool			sc_smpsw_valid;
106aa518c7bSjmcneill 
107aa518c7bSjmcneill 	struct sysmon_envsys	*sc_sme;
108aa518c7bSjmcneill 	envsys_data_t		sc_sensor[THINKPAD_NSENSORS];
1096fffc8efSjmcneill 
1106fffc8efSjmcneill 	int			sc_display_state;
111*b9306226Schristos 
112*b9306226Schristos #define THINKPAD_BAT_ANY	0
113*b9306226Schristos #define THINKPAD_BAT_PRIMARY	1
114*b9306226Schristos #define THINKPAD_BAT_SECONDARY	2
115*b9306226Schristos #define THINKPAD_BAT_LAST	3
116*b9306226Schristos 
117*b9306226Schristos 	tp_batctl_t		sc_batctl;
118*b9306226Schristos 	tp_sysctl_param_t	sc_scparam[THINKPAD_BAT_LAST];
1195b0f734dSjmcneill } thinkpad_softc_t;
1205b0f734dSjmcneill 
1215b0f734dSjmcneill /* Hotkey events */
1225b0f734dSjmcneill #define	THINKPAD_NOTIFY_FnF1		0x001
1235b0f734dSjmcneill #define	THINKPAD_NOTIFY_LockScreen	0x002
1245b0f734dSjmcneill #define	THINKPAD_NOTIFY_BatteryInfo	0x003
1255b0f734dSjmcneill #define	THINKPAD_NOTIFY_SleepButton	0x004
1265b0f734dSjmcneill #define	THINKPAD_NOTIFY_WirelessSwitch	0x005
1271fdd2baeSspz #define	THINKPAD_NOTIFY_wWANSwitch	0x006
1285b0f734dSjmcneill #define	THINKPAD_NOTIFY_DisplayCycle	0x007
1295b0f734dSjmcneill #define	THINKPAD_NOTIFY_PointerSwitch	0x008
1305b0f734dSjmcneill #define	THINKPAD_NOTIFY_EjectButton	0x009
131262712cdSchristos #define	THINKPAD_NOTIFY_FnF10		0x00a	/* XXX: Not seen on T61 */
1325b0f734dSjmcneill #define	THINKPAD_NOTIFY_FnF11		0x00b
1335b0f734dSjmcneill #define	THINKPAD_NOTIFY_HibernateButton	0x00c
1345b0f734dSjmcneill #define	THINKPAD_NOTIFY_BrightnessUp	0x010
1355b0f734dSjmcneill #define	THINKPAD_NOTIFY_BrightnessDown	0x011
1365b0f734dSjmcneill #define	THINKPAD_NOTIFY_ThinkLight	0x012
1375b0f734dSjmcneill #define	THINKPAD_NOTIFY_Zoom		0x014
138262712cdSchristos #define	THINKPAD_NOTIFY_VolumeUp	0x015	/* XXX: Not seen on T61 */
139262712cdSchristos #define	THINKPAD_NOTIFY_VolumeDown	0x016	/* XXX: Not seen on T61 */
140262712cdSchristos #define	THINKPAD_NOTIFY_VolumeMute	0x017	/* XXX: Not seen on T61 */
1415b0f734dSjmcneill #define	THINKPAD_NOTIFY_ThinkVantage	0x018
14228099374Sriastradh #define	THINKPAD_NOTIFY_Star		0x311
14328099374Sriastradh #define	THINKPAD_NOTIFY_Scissors	0x312
14428099374Sriastradh #define	THINKPAD_NOTIFY_Bluetooth	0x314
14528099374Sriastradh #define	THINKPAD_NOTIFY_Keyboard	0x315
1465b0f734dSjmcneill 
1475b0f734dSjmcneill #define	THINKPAD_CMOS_BRIGHTNESS_UP	0x04
1485b0f734dSjmcneill #define	THINKPAD_CMOS_BRIGHTNESS_DOWN	0x05
1495b0f734dSjmcneill 
150aaa60685Sriastradh #define	THINKPAD_HKEY_VERSION_1		0x0100
151aaa60685Sriastradh #define	THINKPAD_HKEY_VERSION_2		0x0200
152ea50d249Sjmcneill 
1536fffc8efSjmcneill #define	THINKPAD_DISPLAY_LCD		0x01
1546fffc8efSjmcneill #define	THINKPAD_DISPLAY_CRT		0x02
1556fffc8efSjmcneill #define	THINKPAD_DISPLAY_DVI		0x08
1566fffc8efSjmcneill #define	THINKPAD_DISPLAY_ALL \
1576fffc8efSjmcneill 	(THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI)
1586fffc8efSjmcneill 
159*b9306226Schristos #define THINKPAD_GET_CHARGE_START	"BCTG"
160*b9306226Schristos #define THINKPAD_SET_CHARGE_START	"BCCS"
161*b9306226Schristos #define THINKPAD_GET_CHARGE_STOP	"BCSG"
162*b9306226Schristos #define THINKPAD_SET_CHARGE_STOP	"BCSS"
163*b9306226Schristos #define THINKPAD_GET_FORCE_DISCHARGE	"BDSG"
164*b9306226Schristos #define THINKPAD_SET_FORCE_DISCHARGE	"BDSS"
165*b9306226Schristos #define THINKPAD_GET_CHARGE_INHIBIT	"BICG"
166*b9306226Schristos #define THINKPAD_SET_CHARGE_INHIBIT	"BICS"
167*b9306226Schristos 
168*b9306226Schristos #define THINKPAD_CALL_ERROR		0x80000000
169*b9306226Schristos 
170b9565322Smlelstv #define THINKPAD_BLUETOOTH_HWPRESENT	0x01
171b9565322Smlelstv #define THINKPAD_BLUETOOTH_RADIOSSW	0x02
172b9565322Smlelstv #define THINKPAD_BLUETOOTH_RESUMECTRL	0x04
173b9565322Smlelstv 
174b9565322Smlelstv #define THINKPAD_WWAN_HWPRESENT		0x01
175b9565322Smlelstv #define THINKPAD_WWAN_RADIOSSW		0x02
176b9565322Smlelstv #define THINKPAD_WWAN_RESUMECTRL	0x04
177b9565322Smlelstv 
178b9565322Smlelstv #define THINKPAD_UWB_HWPRESENT		0x01
179b9565322Smlelstv #define THINKPAD_UWB_RADIOSSW		0x02
180b9565322Smlelstv 
181b9565322Smlelstv #define THINKPAD_RFK_BLUETOOTH		0
182b9565322Smlelstv #define THINKPAD_RFK_WWAN		1
183b9565322Smlelstv #define THINKPAD_RFK_UWB		2
184b9565322Smlelstv 
185e5339e11Scegger static int	thinkpad_match(device_t, cfdata_t, void *);
1865b0f734dSjmcneill static void	thinkpad_attach(device_t, device_t, void *);
1872554187bSjruoho static int	thinkpad_detach(device_t, int);
1885b0f734dSjmcneill 
1895b0f734dSjmcneill static ACPI_STATUS thinkpad_mask_init(thinkpad_softc_t *, uint32_t);
190fb53d8ceSjruoho static void	thinkpad_notify_handler(ACPI_HANDLE, uint32_t, void *);
191c48902ebSjmcneill static void	thinkpad_get_hotkeys(void *);
1925b0f734dSjmcneill 
193e38f75f0Sjmcneill static void	thinkpad_sensors_init(thinkpad_softc_t *);
194e38f75f0Sjmcneill static void	thinkpad_sensors_refresh(struct sysmon_envsys *, envsys_data_t *);
195aa518c7bSjmcneill static void	thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
196e38f75f0Sjmcneill static void	thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *);
197aa518c7bSjmcneill 
198b9565322Smlelstv static void	thinkpad_uwb_toggle(thinkpad_softc_t *);
1991fdd2baeSspz static void	thinkpad_wwan_toggle(thinkpad_softc_t *);
200b9565322Smlelstv static void	thinkpad_bluetooth_toggle(thinkpad_softc_t *);
2014ec3c2b1Sjmcneill 
202c1b390d4Sdyoung static bool	thinkpad_resume(device_t, const pmf_qual_t *);
2035b0f734dSjmcneill static void	thinkpad_brightness_up(device_t);
2045b0f734dSjmcneill static void	thinkpad_brightness_down(device_t);
2052c79d946Schristos static uint8_t	thinkpad_brightness_read(thinkpad_softc_t *);
2065b0f734dSjmcneill static void	thinkpad_cmos(thinkpad_softc_t *, uint8_t);
2075b0f734dSjmcneill 
208*b9306226Schristos static void	thinkpad_battery_probe_support(device_t);
209*b9306226Schristos static void	thinkpad_battery_sysctl_setup(device_t);
210*b9306226Schristos 
211b9565322Smlelstv CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t),
212b9565322Smlelstv     thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL,
2138769a220Sriastradh     0);
2145b0f734dSjmcneill 
21537725553Sthorpej static const struct device_compatible_entry compat_data[] = {
21637725553Sthorpej 	{ .compat = "IBM0068" },
21737725553Sthorpej 	{ .compat = "LEN0068" },
218aaa60685Sriastradh 	{ .compat = "LEN0268" },
21937725553Sthorpej 	DEVICE_COMPAT_EOL
2205b0f734dSjmcneill };
2215b0f734dSjmcneill 
2225b0f734dSjmcneill static int
thinkpad_match(device_t parent,cfdata_t match,void * opaque)223e5339e11Scegger thinkpad_match(device_t parent, cfdata_t match, void *opaque)
2245b0f734dSjmcneill {
2255b0f734dSjmcneill 	struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
226ea50d249Sjmcneill 	ACPI_INTEGER ver;
22737725553Sthorpej 	int ret;
2285b0f734dSjmcneill 
22937725553Sthorpej 	ret = acpi_compatible_match(aa, compat_data);
23037725553Sthorpej 	if (ret == 0)
2315b0f734dSjmcneill 		return 0;
2325b0f734dSjmcneill 
233aaa60685Sriastradh 	/* We only support hotkey versions 0x0100 and 0x0200 */
234eedb1a1fSjmcneill 	if (ACPI_FAILURE(acpi_eval_integer(aa->aa_node->ad_handle, "MHKV",
235eedb1a1fSjmcneill 	    &ver)))
236ea50d249Sjmcneill 		return 0;
237ea50d249Sjmcneill 
238aaa60685Sriastradh 	switch (ver) {
239aaa60685Sriastradh 	case THINKPAD_HKEY_VERSION_1:
240aaa60685Sriastradh 	case THINKPAD_HKEY_VERSION_2:
241aaa60685Sriastradh 		break;
242aaa60685Sriastradh 	default:
243ea50d249Sjmcneill 		return 0;
244aaa60685Sriastradh 	}
245ea50d249Sjmcneill 
2465b0f734dSjmcneill 	/* Cool, looks like we're good to go */
24737725553Sthorpej 	return ret;
2485b0f734dSjmcneill }
2495b0f734dSjmcneill 
2505b0f734dSjmcneill static void
thinkpad_attach(device_t parent,device_t self,void * opaque)2515b0f734dSjmcneill thinkpad_attach(device_t parent, device_t self, void *opaque)
2525b0f734dSjmcneill {
2535b0f734dSjmcneill 	thinkpad_softc_t *sc = device_private(self);
2545b0f734dSjmcneill 	struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
2558fce0f05Sjmcneill 	struct sysmon_pswitch *psw;
256aa518c7bSjmcneill 	device_t curdev;
2575fe0ededSdyoung 	deviter_t di;
2585b0f734dSjmcneill 	ACPI_STATUS rv;
2595b0f734dSjmcneill 	ACPI_INTEGER val;
2608fce0f05Sjmcneill 	int i;
2615b0f734dSjmcneill 
2625b0f734dSjmcneill 	sc->sc_dev = self;
263*b9306226Schristos 	sc->sc_log = NULL;
2641e511da0Sjruoho 	sc->sc_powhdl = NULL;
265f7681ae9Sjruoho 	sc->sc_cmoshdl = NULL;
2661e511da0Sjruoho 	sc->sc_node = aa->aa_node;
2676fffc8efSjmcneill 	sc->sc_display_state = THINKPAD_DISPLAY_LCD;
2685b0f734dSjmcneill 
2695b0f734dSjmcneill 	aprint_naive("\n");
2705b0f734dSjmcneill 	aprint_normal("\n");
2715b0f734dSjmcneill 
272aa518c7bSjmcneill 	sc->sc_ecdev = NULL;
2735fe0ededSdyoung 	for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST);
2745fe0ededSdyoung 	    curdev != NULL; curdev = deviter_next(&di))
275aa518c7bSjmcneill 		if (device_is_a(curdev, "acpiecdt") ||
276aa518c7bSjmcneill 		    device_is_a(curdev, "acpiec")) {
277aa518c7bSjmcneill 			sc->sc_ecdev = curdev;
278aa518c7bSjmcneill 			break;
279aa518c7bSjmcneill 		}
2805fe0ededSdyoung 	deviter_release(&di);
2815fe0ededSdyoung 
282aa518c7bSjmcneill 	if (sc->sc_ecdev)
2833c220ee0Sjmcneill 		aprint_debug_dev(self, "using EC at %s\n",
284aa518c7bSjmcneill 		    device_xname(sc->sc_ecdev));
285aa518c7bSjmcneill 
286622174b8Sriastradh 	/* Query the version number */
287622174b8Sriastradh 	rv = acpi_eval_integer(aa->aa_node->ad_handle, "MHKV", &sc->sc_ver);
288622174b8Sriastradh 	if (ACPI_FAILURE(rv)) {
289622174b8Sriastradh 		aprint_error_dev(self, "couldn't evaluate MHKV: %s\n",
290622174b8Sriastradh 		    AcpiFormatException(rv));
291622174b8Sriastradh 		goto fail;
292622174b8Sriastradh 	}
293622174b8Sriastradh 	aprint_normal_dev(self, "version %04x\n", (unsigned)sc->sc_ver);
294622174b8Sriastradh 
2955b0f734dSjmcneill 	/* Get the supported event mask */
296622174b8Sriastradh 	switch (sc->sc_ver) {
297622174b8Sriastradh 	case THINKPAD_HKEY_VERSION_1:
2985b0f734dSjmcneill 		rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKA", &val);
2995b0f734dSjmcneill 		if (ACPI_FAILURE(rv)) {
3005b0f734dSjmcneill 			aprint_error_dev(self, "couldn't evaluate MHKA: %s\n",
3015b0f734dSjmcneill 			    AcpiFormatException(rv));
3025b0f734dSjmcneill 			goto fail;
3035b0f734dSjmcneill 		}
304622174b8Sriastradh 		break;
305622174b8Sriastradh 	case THINKPAD_HKEY_VERSION_2: {
306622174b8Sriastradh 		ACPI_OBJECT args[1] = {
307622174b8Sriastradh 			[0] = { .Integer = {
308622174b8Sriastradh 				.Type = ACPI_TYPE_INTEGER,
309622174b8Sriastradh 				.Value = 1, /* hotkey events */
310622174b8Sriastradh 			} },
311622174b8Sriastradh 		};
312622174b8Sriastradh 		ACPI_OBJECT_LIST arglist = {
313622174b8Sriastradh 			.Count = __arraycount(args),
314622174b8Sriastradh 			.Pointer = args,
315622174b8Sriastradh 		};
316622174b8Sriastradh 		ACPI_OBJECT ret;
317622174b8Sriastradh 		ACPI_BUFFER buf = { .Pointer = &ret, .Length = sizeof(ret) };
318622174b8Sriastradh 
319622174b8Sriastradh 		rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKA",
320622174b8Sriastradh 		    &arglist, &buf);
321622174b8Sriastradh 		if (ACPI_FAILURE(rv)) {
322622174b8Sriastradh 			aprint_error_dev(self, "couldn't evaluate MHKA(1):"
323622174b8Sriastradh 			    " %s\n",
324622174b8Sriastradh 			    AcpiFormatException(rv));
325622174b8Sriastradh 			goto fail;
326622174b8Sriastradh 		}
327622174b8Sriastradh 		if (buf.Length == 0 || ret.Type != ACPI_TYPE_INTEGER) {
328622174b8Sriastradh 			aprint_error_dev(self, "failed to evaluate MHKA(1)\n");
329622174b8Sriastradh 			goto fail;
330622174b8Sriastradh 		}
331622174b8Sriastradh 		val = ret.Integer.Value;
332622174b8Sriastradh 		break;
333622174b8Sriastradh 	}
334622174b8Sriastradh 	default:
335622174b8Sriastradh 		panic("%s: invalid version %jd", device_xname(self),
336622174b8Sriastradh 		    (intmax_t)sc->sc_ver);
337622174b8Sriastradh 	}
3385b0f734dSjmcneill 
3395b0f734dSjmcneill 	/* Enable all supported events */
3405b0f734dSjmcneill 	rv = thinkpad_mask_init(sc, val);
3415b0f734dSjmcneill 	if (ACPI_FAILURE(rv)) {
3425b0f734dSjmcneill 		aprint_error_dev(self, "couldn't set event mask: %s\n",
3435b0f734dSjmcneill 		    AcpiFormatException(rv));
3445b0f734dSjmcneill 		goto fail;
3455b0f734dSjmcneill 	}
3465b0f734dSjmcneill 
34755052ecaSjruoho 	(void)acpi_register_notify(sc->sc_node, thinkpad_notify_handler);
3485b0f734dSjmcneill 
3491e511da0Sjruoho 	/*
350f7681ae9Sjruoho 	 * Obtain a handle for CMOS commands. This is used by T61.
351f7681ae9Sjruoho 	 */
352f7681ae9Sjruoho 	(void)AcpiGetHandle(NULL, "\\UCMS", &sc->sc_cmoshdl);
353f7681ae9Sjruoho 
354f7681ae9Sjruoho 	/*
3551e511da0Sjruoho 	 * Obtain a handle to the power resource available on many models.
3561e511da0Sjruoho 	 * Since pmf(9) is not yet integrated with the ACPI power resource
3571e511da0Sjruoho 	 * code, this must be turned on manually upon resume. Otherwise the
3581e511da0Sjruoho 	 * system may, for instance, resume from S3 with usb(4) powered down.
3591e511da0Sjruoho 	 */
3601e511da0Sjruoho 	(void)AcpiGetHandle(NULL, "\\_SB.PCI0.LPC.EC.PUBS", &sc->sc_powhdl);
3611e511da0Sjruoho 
362aa518c7bSjmcneill 	/* Register power switches with sysmon */
3638fce0f05Sjmcneill 	psw = sc->sc_smpsw;
3645b0f734dSjmcneill 	sc->sc_smpsw_valid = true;
3655b0f734dSjmcneill 
3668fce0f05Sjmcneill 	psw[TP_PSW_SLEEP].smpsw_name = device_xname(self);
3678fce0f05Sjmcneill 	psw[TP_PSW_SLEEP].smpsw_type = PSWITCH_TYPE_SLEEP;
3685b0f734dSjmcneill #if notyet
3698fce0f05Sjmcneill 	psw[TP_PSW_HIBERNATE].smpsw_name = device_xname(self);
3708fce0f05Sjmcneill 	mpsw[TP_PSW_HIBERNATE].smpsw_type = PSWITCH_TYPE_HIBERNATE;
3715b0f734dSjmcneill #endif
3728fce0f05Sjmcneill 	for (i = TP_PSW_DISPLAY_CYCLE; i < TP_PSW_LAST; i++)
3738fce0f05Sjmcneill 		sc->sc_smpsw[i].smpsw_type = PSWITCH_TYPE_HOTKEY;
3742c79d946Schristos 
3758fce0f05Sjmcneill 	psw[TP_PSW_DISPLAY_CYCLE].smpsw_name	= PSWITCH_HK_DISPLAY_CYCLE;
3768fce0f05Sjmcneill 	psw[TP_PSW_LOCK_SCREEN].smpsw_name	= PSWITCH_HK_LOCK_SCREEN;
3778fce0f05Sjmcneill 	psw[TP_PSW_BATTERY_INFO].smpsw_name	= PSWITCH_HK_BATTERY_INFO;
3788fce0f05Sjmcneill 	psw[TP_PSW_EJECT_BUTTON].smpsw_name	= PSWITCH_HK_EJECT_BUTTON;
3798fce0f05Sjmcneill 	psw[TP_PSW_ZOOM_BUTTON].smpsw_name	= PSWITCH_HK_ZOOM_BUTTON;
3808fce0f05Sjmcneill 	psw[TP_PSW_VENDOR_BUTTON].smpsw_name	= PSWITCH_HK_VENDOR_BUTTON;
3819b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
382262712cdSchristos 	psw[TP_PSW_FNF1_BUTTON].smpsw_name	= PSWITCH_HK_FNF1_BUTTON;
383262712cdSchristos 	psw[TP_PSW_WIRELESS_BUTTON].smpsw_name	= PSWITCH_HK_WIRELESS_BUTTON;
384262712cdSchristos 	psw[TP_PSW_WWAN_BUTTON].smpsw_name	= PSWITCH_HK_WWAN_BUTTON;
385262712cdSchristos 	psw[TP_PSW_POINTER_BUTTON].smpsw_name	= PSWITCH_HK_POINTER_BUTTON;
386262712cdSchristos 	psw[TP_PSW_FNF10_BUTTON].smpsw_name	= PSWITCH_HK_FNF10_BUTTON;
387262712cdSchristos 	psw[TP_PSW_FNF11_BUTTON].smpsw_name	= PSWITCH_HK_FNF11_BUTTON;
388262712cdSchristos 	psw[TP_PSW_BRIGHTNESS_UP].smpsw_name	= PSWITCH_HK_BRIGHTNESS_UP;
389262712cdSchristos 	psw[TP_PSW_BRIGHTNESS_DOWN].smpsw_name	= PSWITCH_HK_BRIGHTNESS_DOWN;
390262712cdSchristos 	psw[TP_PSW_THINKLIGHT].smpsw_name	= PSWITCH_HK_THINKLIGHT;
391262712cdSchristos 	psw[TP_PSW_VOLUME_UP].smpsw_name	= PSWITCH_HK_VOLUME_UP;
392262712cdSchristos 	psw[TP_PSW_VOLUME_DOWN].smpsw_name	= PSWITCH_HK_VOLUME_DOWN;
393262712cdSchristos 	psw[TP_PSW_VOLUME_MUTE].smpsw_name	= PSWITCH_HK_VOLUME_MUTE;
39428099374Sriastradh 	psw[TP_PSW_STAR_BUTTON].smpsw_name	= PSWITCH_HK_STAR_BUTTON;
39528099374Sriastradh 	psw[TP_PSW_SCISSORS_BUTTON].smpsw_name	= PSWITCH_HK_SCISSORS_BUTTON;
39628099374Sriastradh 	psw[TP_PSW_BLUETOOTH_BUTTON].smpsw_name	= PSWITCH_HK_BLUETOOTH_BUTTON;
39728099374Sriastradh 	psw[TP_PSW_KEYBOARD_BUTTON].smpsw_name	= PSWITCH_HK_KEYBOARD_BUTTON;
3989b9040ceSchristos #endif /* THINKPAD_NORMAL_HOTKEYS */
3995b0f734dSjmcneill 
4008fce0f05Sjmcneill 	for (i = 0; i < TP_PSW_LAST; i++) {
4018fce0f05Sjmcneill 		/* not supported yet */
4028fce0f05Sjmcneill 		if (i == TP_PSW_HIBERNATE)
4038fce0f05Sjmcneill 			continue;
4048fce0f05Sjmcneill 		if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
4058fce0f05Sjmcneill 			aprint_error_dev(self,
4068fce0f05Sjmcneill 			    "couldn't register with sysmon\n");
4075b0f734dSjmcneill 			sc->sc_smpsw_valid = false;
4088fce0f05Sjmcneill 			break;
4095b0f734dSjmcneill 		}
4105b0f734dSjmcneill 	}
4115b0f734dSjmcneill 
412e38f75f0Sjmcneill 	/* Register temperature and fan sensors with envsys */
413e38f75f0Sjmcneill 	thinkpad_sensors_init(sc);
414aa518c7bSjmcneill 
415*b9306226Schristos 	/* Probe supported battery charge/control operations */
416*b9306226Schristos 	thinkpad_battery_probe_support(self);
417*b9306226Schristos 
418*b9306226Schristos 	if (sc->sc_batctl.have_any) {
419*b9306226Schristos 		for (i = 0; i < THINKPAD_BAT_LAST; i++) {
420*b9306226Schristos 			sc->sc_scparam[i].sp_dev = self;
421*b9306226Schristos 			sc->sc_scparam[i].sp_bat = i;
422*b9306226Schristos 		}
423*b9306226Schristos 		thinkpad_battery_sysctl_setup(self);
424*b9306226Schristos 	}
425*b9306226Schristos 
4265b0f734dSjmcneill fail:
427b7cf8d35Sjmcneill 	if (!pmf_device_register(self, NULL, thinkpad_resume))
4285b0f734dSjmcneill 		aprint_error_dev(self, "couldn't establish power handler\n");
4295b0f734dSjmcneill 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
4305b0f734dSjmcneill 	    thinkpad_brightness_up, true))
4315b0f734dSjmcneill 		aprint_error_dev(self, "couldn't register event handler\n");
4325b0f734dSjmcneill 	if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
4335b0f734dSjmcneill 	    thinkpad_brightness_down, true))
4345b0f734dSjmcneill 		aprint_error_dev(self, "couldn't register event handler\n");
4355b0f734dSjmcneill }
4365b0f734dSjmcneill 
4372554187bSjruoho static int
thinkpad_detach(device_t self,int flags)4382554187bSjruoho thinkpad_detach(device_t self, int flags)
4392554187bSjruoho {
4402554187bSjruoho 	struct thinkpad_softc *sc = device_private(self);
4412554187bSjruoho 	int i;
4422554187bSjruoho 
44355052ecaSjruoho 	acpi_deregister_notify(sc->sc_node);
4442554187bSjruoho 
4452554187bSjruoho 	for (i = 0; i < TP_PSW_LAST; i++)
4462554187bSjruoho 		sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
4472554187bSjruoho 
4482554187bSjruoho 	if (sc->sc_sme != NULL)
4492554187bSjruoho 		sysmon_envsys_unregister(sc->sc_sme);
4502554187bSjruoho 
451*b9306226Schristos 	if (sc->sc_log != NULL)
452*b9306226Schristos 		sysctl_teardown(&sc->sc_log);
453*b9306226Schristos 
4542554187bSjruoho 	pmf_device_deregister(self);
4552554187bSjruoho 
4562554187bSjruoho 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP,
4572554187bSjruoho 	    thinkpad_brightness_up, true);
4582554187bSjruoho 
4592554187bSjruoho 	pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
4602554187bSjruoho 	    thinkpad_brightness_down, true);
4612554187bSjruoho 
4622554187bSjruoho 	return 0;
4632554187bSjruoho }
4642554187bSjruoho 
4655b0f734dSjmcneill static void
thinkpad_notify_handler(ACPI_HANDLE hdl,uint32_t notify,void * opaque)466fb53d8ceSjruoho thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
4675b0f734dSjmcneill {
46855052ecaSjruoho 	device_t self = opaque;
46955052ecaSjruoho 	thinkpad_softc_t *sc;
47055052ecaSjruoho 
47155052ecaSjruoho 	sc = device_private(self);
4725b0f734dSjmcneill 
4735b0f734dSjmcneill 	if (notify != 0x80) {
4745b0f734dSjmcneill 		aprint_debug_dev(self, "unknown notify 0x%02x\n", notify);
4755b0f734dSjmcneill 		return;
4765b0f734dSjmcneill 	}
4775b0f734dSjmcneill 
47855052ecaSjruoho 	(void)AcpiOsExecute(OSL_NOTIFY_HANDLER, thinkpad_get_hotkeys, sc);
479c48902ebSjmcneill }
480c48902ebSjmcneill 
481363e5ec8Sriastradh SDT_PROBE_DEFINE2(sdt, thinkpad, hotkey, MHKP,
482363e5ec8Sriastradh     "struct thinkpad_softc *"/*sc*/,
483363e5ec8Sriastradh     "ACPI_INTEGER"/*val*/);
484363e5ec8Sriastradh 
485c48902ebSjmcneill static void
thinkpad_get_hotkeys(void * opaque)486c48902ebSjmcneill thinkpad_get_hotkeys(void *opaque)
487c48902ebSjmcneill {
488c48902ebSjmcneill 	thinkpad_softc_t *sc = (thinkpad_softc_t *)opaque;
489c48902ebSjmcneill 	device_t self = sc->sc_dev;
490c48902ebSjmcneill 	ACPI_STATUS rv;
491c48902ebSjmcneill 	ACPI_INTEGER val;
492c48902ebSjmcneill 	int type, event;
493c48902ebSjmcneill 
494ea50d249Sjmcneill 	for (;;) {
4955b0f734dSjmcneill 		rv = acpi_eval_integer(sc->sc_node->ad_handle, "MHKP", &val);
4965b0f734dSjmcneill 		if (ACPI_FAILURE(rv)) {
4975b0f734dSjmcneill 			aprint_error_dev(self, "couldn't evaluate MHKP: %s\n",
4985b0f734dSjmcneill 			    AcpiFormatException(rv));
4995b0f734dSjmcneill 			return;
5005b0f734dSjmcneill 		}
501363e5ec8Sriastradh 		SDT_PROBE2(sdt, thinkpad, hotkey, MHKP,  sc, val);
5025b0f734dSjmcneill 
503ea50d249Sjmcneill 		if (val == 0)
504ea50d249Sjmcneill 			return;
505ea50d249Sjmcneill 
5065b0f734dSjmcneill 		type = (val & 0xf000) >> 12;
5075b0f734dSjmcneill 		event = val & 0x0fff;
5085b0f734dSjmcneill 
5095b0f734dSjmcneill 		if (type != 1)
5105b0f734dSjmcneill 			/* Only type 1 events are supported for now */
511ea50d249Sjmcneill 			continue;
5125b0f734dSjmcneill 
5135b0f734dSjmcneill 		switch (event) {
5145b0f734dSjmcneill 		case THINKPAD_NOTIFY_BrightnessUp:
5155b0f734dSjmcneill 			thinkpad_brightness_up(self);
5169b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
517262712cdSchristos 			if (sc->sc_smpsw_valid == false)
518262712cdSchristos 				break;
519262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_UP],
520262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
521262712cdSchristos #endif
5225b0f734dSjmcneill 			break;
5235b0f734dSjmcneill 		case THINKPAD_NOTIFY_BrightnessDown:
5245b0f734dSjmcneill 			thinkpad_brightness_down(self);
5259b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
526262712cdSchristos 			if (sc->sc_smpsw_valid == false)
527262712cdSchristos 				break;
528262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BRIGHTNESS_DOWN],
529262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
530262712cdSchristos #endif
5315b0f734dSjmcneill 			break;
5324ec3c2b1Sjmcneill 		case THINKPAD_NOTIFY_WirelessSwitch:
533b9565322Smlelstv 			thinkpad_uwb_toggle(sc);
534b9565322Smlelstv 			thinkpad_wwan_toggle(sc);
535b9565322Smlelstv 			thinkpad_bluetooth_toggle(sc);
5369b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
537262712cdSchristos 			if (sc->sc_smpsw_valid == false)
538262712cdSchristos 				break;
539262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WIRELESS_BUTTON],
540262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
541262712cdSchristos #endif
5424ec3c2b1Sjmcneill 			break;
54328099374Sriastradh 		case THINKPAD_NOTIFY_Bluetooth:
54428099374Sriastradh 			thinkpad_bluetooth_toggle(sc);
54528099374Sriastradh #ifndef THINKPAD_NORMAL_HOTKEYS
54628099374Sriastradh 			if (sc->sc_smpsw_valid == false)
54728099374Sriastradh 				break;
54828099374Sriastradh 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_BLUETOOTH_BUTTON],
54928099374Sriastradh 			    PSWITCH_EVENT_PRESSED);
55028099374Sriastradh #endif
55128099374Sriastradh 			break;
5521fdd2baeSspz 		case THINKPAD_NOTIFY_wWANSwitch:
5531fdd2baeSspz 			thinkpad_wwan_toggle(sc);
5549b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
555262712cdSchristos 			if (sc->sc_smpsw_valid == false)
556262712cdSchristos 				break;
557262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_WWAN_BUTTON],
558262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
559262712cdSchristos #endif
5601fdd2baeSspz 			break;
5615b0f734dSjmcneill 		case THINKPAD_NOTIFY_SleepButton:
5625b0f734dSjmcneill 			if (sc->sc_smpsw_valid == false)
5635b0f734dSjmcneill 				break;
5645b0f734dSjmcneill 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SLEEP],
5655b0f734dSjmcneill 			    PSWITCH_EVENT_PRESSED);
5665b0f734dSjmcneill 			break;
5675b0f734dSjmcneill 		case THINKPAD_NOTIFY_HibernateButton:
5685b0f734dSjmcneill #if notyet
5695b0f734dSjmcneill 			if (sc->sc_smpsw_valid == false)
5705b0f734dSjmcneill 				break;
5715b0f734dSjmcneill 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_HIBERNATE],
5725b0f734dSjmcneill 			    PSWITCH_EVENT_PRESSED);
5735b0f734dSjmcneill #endif
5744ec3c2b1Sjmcneill 			break;
5758fce0f05Sjmcneill 		case THINKPAD_NOTIFY_DisplayCycle:
5768fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
5778fce0f05Sjmcneill 				break;
5788fce0f05Sjmcneill 			sysmon_pswitch_event(
5798fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_DISPLAY_CYCLE],
5808fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
5818fce0f05Sjmcneill 			break;
5825b0f734dSjmcneill 		case THINKPAD_NOTIFY_LockScreen:
5838fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
5848fce0f05Sjmcneill 				break;
5858fce0f05Sjmcneill 			sysmon_pswitch_event(
5868fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_LOCK_SCREEN],
5878fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
5888fce0f05Sjmcneill 			break;
5895b0f734dSjmcneill 		case THINKPAD_NOTIFY_BatteryInfo:
5908fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
5918fce0f05Sjmcneill 				break;
5928fce0f05Sjmcneill 			sysmon_pswitch_event(
5938fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_BATTERY_INFO],
5948fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
5958fce0f05Sjmcneill 			break;
5968fce0f05Sjmcneill 		case THINKPAD_NOTIFY_EjectButton:
5978fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
5988fce0f05Sjmcneill 				break;
5998fce0f05Sjmcneill 			sysmon_pswitch_event(
6008fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_EJECT_BUTTON],
6018fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
6028fce0f05Sjmcneill 			break;
6038fce0f05Sjmcneill 		case THINKPAD_NOTIFY_Zoom:
6048fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
6058fce0f05Sjmcneill 				break;
6068fce0f05Sjmcneill 			sysmon_pswitch_event(
6078fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_ZOOM_BUTTON],
6088fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
6098fce0f05Sjmcneill 			break;
6108fce0f05Sjmcneill 		case THINKPAD_NOTIFY_ThinkVantage:
6118fce0f05Sjmcneill 			if (sc->sc_smpsw_valid == false)
6128fce0f05Sjmcneill 				break;
6138fce0f05Sjmcneill 			sysmon_pswitch_event(
6148fce0f05Sjmcneill 			    &sc->sc_smpsw[TP_PSW_VENDOR_BUTTON],
6158fce0f05Sjmcneill 			    PSWITCH_EVENT_PRESSED);
6168fce0f05Sjmcneill 			break;
6179b9040ceSchristos #ifndef THINKPAD_NORMAL_HOTKEYS
618262712cdSchristos 		case THINKPAD_NOTIFY_FnF1:
619262712cdSchristos 			if (sc->sc_smpsw_valid == false)
620262712cdSchristos 				break;
621262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF1_BUTTON],
622262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
623262712cdSchristos 			break;
624262712cdSchristos 		case THINKPAD_NOTIFY_PointerSwitch:
625262712cdSchristos 			if (sc->sc_smpsw_valid == false)
626262712cdSchristos 				break;
627262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_POINTER_BUTTON],
628262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
629262712cdSchristos 			break;
630262712cdSchristos 		case THINKPAD_NOTIFY_FnF11:
631262712cdSchristos 			if (sc->sc_smpsw_valid == false)
632262712cdSchristos 				break;
633262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF11_BUTTON],
634262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
635262712cdSchristos 			break;
636262712cdSchristos 		case THINKPAD_NOTIFY_ThinkLight:
637262712cdSchristos 			if (sc->sc_smpsw_valid == false)
638262712cdSchristos 				break;
639262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_THINKLIGHT],
640262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
641262712cdSchristos 			break;
642262712cdSchristos 		/*
643262712cdSchristos 		 * For some reason the next four aren't seen on my T61.
644262712cdSchristos 		 */
645262712cdSchristos 		case THINKPAD_NOTIFY_FnF10:
646262712cdSchristos 			if (sc->sc_smpsw_valid == false)
647262712cdSchristos 				break;
648262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_FNF10_BUTTON],
649262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
650262712cdSchristos 			break;
651262712cdSchristos 		case THINKPAD_NOTIFY_VolumeUp:
652262712cdSchristos 			if (sc->sc_smpsw_valid == false)
653262712cdSchristos 				break;
654262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_UP],
655262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
656262712cdSchristos 			break;
657262712cdSchristos 		case THINKPAD_NOTIFY_VolumeDown:
658262712cdSchristos 			if (sc->sc_smpsw_valid == false)
659262712cdSchristos 				break;
660262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_DOWN],
661262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
662262712cdSchristos 			break;
663262712cdSchristos 		case THINKPAD_NOTIFY_VolumeMute:
664262712cdSchristos 			if (sc->sc_smpsw_valid == false)
665262712cdSchristos 				break;
666262712cdSchristos 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_VOLUME_MUTE],
667262712cdSchristos 			    PSWITCH_EVENT_PRESSED);
668262712cdSchristos 			break;
66928099374Sriastradh 		case THINKPAD_NOTIFY_Star:
67028099374Sriastradh 			if (sc->sc_smpsw_valid == false)
67128099374Sriastradh 				break;
67228099374Sriastradh 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_STAR_BUTTON],
67328099374Sriastradh 			    PSWITCH_EVENT_PRESSED);
67428099374Sriastradh 			break;
67528099374Sriastradh 		case THINKPAD_NOTIFY_Scissors:
67628099374Sriastradh 			if (sc->sc_smpsw_valid == false)
67728099374Sriastradh 				break;
67828099374Sriastradh 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_SCISSORS_BUTTON],
67928099374Sriastradh 			    PSWITCH_EVENT_PRESSED);
68028099374Sriastradh 			break;
68128099374Sriastradh 		case THINKPAD_NOTIFY_Keyboard:
68228099374Sriastradh 			if (sc->sc_smpsw_valid == false)
68328099374Sriastradh 				break;
68428099374Sriastradh 			sysmon_pswitch_event(&sc->sc_smpsw[TP_PSW_KEYBOARD_BUTTON],
68528099374Sriastradh 			    PSWITCH_EVENT_PRESSED);
68628099374Sriastradh 			break;
687262712cdSchristos #else
6888fce0f05Sjmcneill 		case THINKPAD_NOTIFY_FnF1:
6895b0f734dSjmcneill 		case THINKPAD_NOTIFY_PointerSwitch:
6905b0f734dSjmcneill 		case THINKPAD_NOTIFY_FnF10:
6915b0f734dSjmcneill 		case THINKPAD_NOTIFY_FnF11:
6925b0f734dSjmcneill 		case THINKPAD_NOTIFY_ThinkLight:
69391262361Sjruoho 		case THINKPAD_NOTIFY_VolumeUp:
69491262361Sjruoho 		case THINKPAD_NOTIFY_VolumeDown:
69591262361Sjruoho 		case THINKPAD_NOTIFY_VolumeMute:
69628099374Sriastradh 		case THINKPAD_NOTIFY_Star:
69728099374Sriastradh 		case THINKPAD_NOTIFY_Scissors:
69828099374Sriastradh 		case THINKPAD_NOTIFY_Keyboard:
699ea50d249Sjmcneill 			/* XXXJDM we should deliver hotkeys as keycodes */
7005b0f734dSjmcneill 			break;
7019b9040ceSchristos #endif /* THINKPAD_NORMAL_HOTKEYS */
7025b0f734dSjmcneill 		default:
7035b0f734dSjmcneill 			aprint_debug_dev(self, "notify event 0x%03x\n", event);
7045b0f734dSjmcneill 			break;
7055b0f734dSjmcneill 		}
706ea50d249Sjmcneill 	}
7075b0f734dSjmcneill }
7085b0f734dSjmcneill 
7095b0f734dSjmcneill static ACPI_STATUS
thinkpad_mask_init(thinkpad_softc_t * sc,uint32_t mask)7105b0f734dSjmcneill thinkpad_mask_init(thinkpad_softc_t *sc, uint32_t mask)
7115b0f734dSjmcneill {
7125b0f734dSjmcneill 	ACPI_OBJECT param[2];
7135b0f734dSjmcneill 	ACPI_OBJECT_LIST params;
7145b0f734dSjmcneill 	ACPI_STATUS rv;
7155b0f734dSjmcneill 	int i;
7165b0f734dSjmcneill 
7175b0f734dSjmcneill 	/* Update hotkey mask */
7185b0f734dSjmcneill 	params.Count = 2;
7195b0f734dSjmcneill 	params.Pointer = param;
7205b0f734dSjmcneill 	param[0].Type = param[1].Type = ACPI_TYPE_INTEGER;
7215b0f734dSjmcneill 
7225b0f734dSjmcneill 	for (i = 0; i < 32; i++) {
7235b0f734dSjmcneill 		param[0].Integer.Value = i + 1;
724432f5bfcSmsaitoh 		param[1].Integer.Value = ((__BIT(i) & mask) != 0);
7255b0f734dSjmcneill 
7265b0f734dSjmcneill 		rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "MHKM",
7275b0f734dSjmcneill 		    &params, NULL);
7285b0f734dSjmcneill 		if (ACPI_FAILURE(rv))
7295b0f734dSjmcneill 			return rv;
7305b0f734dSjmcneill 	}
7315b0f734dSjmcneill 
7325b0f734dSjmcneill 	/* Enable hotkey events */
73363ec7a18Scegger 	rv = acpi_eval_set_integer(sc->sc_node->ad_handle, "MHKC", 1);
7345b0f734dSjmcneill 	if (ACPI_FAILURE(rv)) {
7355b0f734dSjmcneill 		aprint_error_dev(sc->sc_dev, "couldn't enable hotkeys: %s\n",
7365b0f734dSjmcneill 		    AcpiFormatException(rv));
7375b0f734dSjmcneill 		return rv;
7385b0f734dSjmcneill 	}
7395b0f734dSjmcneill 
740c48902ebSjmcneill 	/* Claim ownership of brightness control */
74163ec7a18Scegger 	(void)acpi_eval_set_integer(sc->sc_node->ad_handle, "PWMS", 0);
742c48902ebSjmcneill 
7435b0f734dSjmcneill 	return AE_OK;
7445b0f734dSjmcneill }
7455b0f734dSjmcneill 
746aa518c7bSjmcneill static void
thinkpad_sensors_init(thinkpad_softc_t * sc)747e38f75f0Sjmcneill thinkpad_sensors_init(thinkpad_softc_t *sc)
748aa518c7bSjmcneill {
7492554187bSjruoho 	int i, j;
750aa518c7bSjmcneill 
751aa518c7bSjmcneill 	if (sc->sc_ecdev == NULL)
752aa518c7bSjmcneill 		return;	/* no chance of this working */
753aa518c7bSjmcneill 
754aa518c7bSjmcneill 	sc->sc_sme = sysmon_envsys_create();
7552554187bSjruoho 
7562554187bSjruoho 	for (i = j = 0; i < THINKPAD_NTEMPSENSORS; i++) {
7572554187bSjruoho 
758aa518c7bSjmcneill 		sc->sc_sensor[i].units = ENVSYS_STEMP;
759b0a4d362Spgoyette 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
7604f0c2c3fSjruoho 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
761aa518c7bSjmcneill 
7622554187bSjruoho 		(void)snprintf(sc->sc_sensor[i].desc,
76377f707c9Sjruoho 		    sizeof(sc->sc_sensor[i].desc), "temperature %d", i);
7642554187bSjruoho 
7652554187bSjruoho 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
7662554187bSjruoho 			&sc->sc_sensor[i]) != 0)
7672554187bSjruoho 			goto fail;
768aa518c7bSjmcneill 	}
7692554187bSjruoho 
7702554187bSjruoho 	for (i = THINKPAD_NTEMPSENSORS; i < THINKPAD_NSENSORS; i++, j++) {
7712554187bSjruoho 
772e38f75f0Sjmcneill 		sc->sc_sensor[i].units = ENVSYS_SFANRPM;
773b0a4d362Spgoyette 		sc->sc_sensor[i].state = ENVSYS_SINVALID;
7744f0c2c3fSjruoho 		sc->sc_sensor[i].flags = ENVSYS_FHAS_ENTROPY;
775e38f75f0Sjmcneill 
7762554187bSjruoho 		(void)snprintf(sc->sc_sensor[i].desc,
77777f707c9Sjruoho 		    sizeof(sc->sc_sensor[i].desc), "fan speed %d", j);
7782554187bSjruoho 
7792554187bSjruoho 		if (sysmon_envsys_sensor_attach(sc->sc_sme,
7802554187bSjruoho 			&sc->sc_sensor[i]) != 0)
7812554187bSjruoho 			goto fail;
782e38f75f0Sjmcneill 	}
783aa518c7bSjmcneill 
784aa518c7bSjmcneill 	sc->sc_sme->sme_name = device_xname(sc->sc_dev);
785aa518c7bSjmcneill 	sc->sc_sme->sme_cookie = sc;
786e38f75f0Sjmcneill 	sc->sc_sme->sme_refresh = thinkpad_sensors_refresh;
787aa518c7bSjmcneill 
7882554187bSjruoho 	if (sysmon_envsys_register(sc->sc_sme) != 0)
7892554187bSjruoho 		goto fail;
7902554187bSjruoho 
7912554187bSjruoho 	return;
7922554187bSjruoho 
7932554187bSjruoho fail:
7942554187bSjruoho 	aprint_error_dev(sc->sc_dev, "failed to initialize sysmon\n");
795aa518c7bSjmcneill 	sysmon_envsys_destroy(sc->sc_sme);
7962554187bSjruoho 	sc->sc_sme = NULL;
797aa518c7bSjmcneill }
798aa518c7bSjmcneill 
799aa518c7bSjmcneill static void
thinkpad_sensors_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)800e38f75f0Sjmcneill thinkpad_sensors_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
801e38f75f0Sjmcneill {
802e38f75f0Sjmcneill 	switch (edata->units) {
803e38f75f0Sjmcneill 	case ENVSYS_STEMP:
804e38f75f0Sjmcneill 		thinkpad_temp_refresh(sme, edata);
805e38f75f0Sjmcneill 		break;
806e38f75f0Sjmcneill 	case ENVSYS_SFANRPM:
807e38f75f0Sjmcneill 		thinkpad_fan_refresh(sme, edata);
808e38f75f0Sjmcneill 		break;
809e38f75f0Sjmcneill 	default:
810e38f75f0Sjmcneill 		break;
811e38f75f0Sjmcneill 	}
812e38f75f0Sjmcneill }
813e38f75f0Sjmcneill 
814e38f75f0Sjmcneill static void
thinkpad_temp_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)815aa518c7bSjmcneill thinkpad_temp_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
816aa518c7bSjmcneill {
817aa518c7bSjmcneill 	thinkpad_softc_t *sc = sme->sme_cookie;
818aa518c7bSjmcneill 	char sname[5] = "TMP?";
819aa518c7bSjmcneill 	ACPI_INTEGER val;
820aa518c7bSjmcneill 	ACPI_STATUS rv;
821aa518c7bSjmcneill 	int temp;
822aa518c7bSjmcneill 
823aa518c7bSjmcneill 	sname[3] = '0' + edata->sensor;
824aa518c7bSjmcneill 	rv = acpi_eval_integer(acpiec_get_handle(sc->sc_ecdev), sname, &val);
825aa518c7bSjmcneill 	if (ACPI_FAILURE(rv)) {
826aa518c7bSjmcneill 		edata->state = ENVSYS_SINVALID;
827aa518c7bSjmcneill 		return;
828aa518c7bSjmcneill 	}
829aa518c7bSjmcneill 	temp = (int)val;
830aa518c7bSjmcneill 	if (temp > 127 || temp < -127) {
831aa518c7bSjmcneill 		edata->state = ENVSYS_SINVALID;
832aa518c7bSjmcneill 		return;
833aa518c7bSjmcneill 	}
834aa518c7bSjmcneill 
835aa518c7bSjmcneill 	edata->value_cur = temp * 1000000 + 273150000;
836aa518c7bSjmcneill 	edata->state = ENVSYS_SVALID;
837aa518c7bSjmcneill }
838aa518c7bSjmcneill 
8394ec3c2b1Sjmcneill static void
thinkpad_fan_refresh(struct sysmon_envsys * sme,envsys_data_t * edata)840e38f75f0Sjmcneill thinkpad_fan_refresh(struct sysmon_envsys *sme, envsys_data_t *edata)
841e38f75f0Sjmcneill {
842e38f75f0Sjmcneill 	thinkpad_softc_t *sc = sme->sme_cookie;
843e38f75f0Sjmcneill 	ACPI_INTEGER lo;
844e38f75f0Sjmcneill 	ACPI_INTEGER hi;
845e38f75f0Sjmcneill 	ACPI_STATUS rv;
846e38f75f0Sjmcneill 	int rpm;
847e38f75f0Sjmcneill 
848e38f75f0Sjmcneill 	/*
849e38f75f0Sjmcneill 	 * Read the low byte first to avoid a firmware bug.
850e38f75f0Sjmcneill 	 */
851e38f75f0Sjmcneill 	rv = acpiec_bus_read(sc->sc_ecdev, 0x84, &lo, 1);
852e38f75f0Sjmcneill 	if (ACPI_FAILURE(rv)) {
853e38f75f0Sjmcneill 		edata->state = ENVSYS_SINVALID;
854e38f75f0Sjmcneill 		return;
855e38f75f0Sjmcneill 	}
856e38f75f0Sjmcneill 	rv = acpiec_bus_read(sc->sc_ecdev, 0x85, &hi, 1);
857e38f75f0Sjmcneill 	if (ACPI_FAILURE(rv)) {
858e38f75f0Sjmcneill 		edata->state = ENVSYS_SINVALID;
859e38f75f0Sjmcneill 		return;
860e38f75f0Sjmcneill 	}
861d2f40a18Smlelstv 
862e38f75f0Sjmcneill 	rpm = ((((int)hi) << 8) | ((int)lo));
863e38f75f0Sjmcneill 	if (rpm < 0) {
864e38f75f0Sjmcneill 		edata->state = ENVSYS_SINVALID;
865e38f75f0Sjmcneill 		return;
866e38f75f0Sjmcneill 	}
867e38f75f0Sjmcneill 
868e38f75f0Sjmcneill 	edata->value_cur = rpm;
869e38f75f0Sjmcneill 	edata->state = ENVSYS_SVALID;
870e38f75f0Sjmcneill }
871e38f75f0Sjmcneill 
872e38f75f0Sjmcneill static void
thinkpad_bluetooth_toggle(thinkpad_softc_t * sc)873b9565322Smlelstv thinkpad_bluetooth_toggle(thinkpad_softc_t *sc)
8744ec3c2b1Sjmcneill {
875b9565322Smlelstv 	ACPI_BUFFER buf;
876b9565322Smlelstv 	ACPI_OBJECT retobj;
877b9565322Smlelstv 	ACPI_OBJECT param[1];
878b9565322Smlelstv 	ACPI_OBJECT_LIST params;
879b9565322Smlelstv 	ACPI_STATUS rv;
880b9565322Smlelstv 
881bf34549bSjmcneill 	/* Ignore return value, as the hardware may not support bluetooth */
882b9565322Smlelstv 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "BTGL", NULL, NULL);
883b9565322Smlelstv 	if (!ACPI_FAILURE(rv))
884b9565322Smlelstv 		return;
885b9565322Smlelstv 
886b9565322Smlelstv 	buf.Pointer = &retobj;
887b9565322Smlelstv 	buf.Length = sizeof(retobj);
888b9565322Smlelstv 
889b9565322Smlelstv 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GBDC", NULL, &buf);
890b9565322Smlelstv 	if (ACPI_FAILURE(rv))
891b9565322Smlelstv 		return;
892b9565322Smlelstv 
893b9565322Smlelstv 	params.Count = 1;
894b9565322Smlelstv 	params.Pointer = param;
895b9565322Smlelstv 	param[0].Type = ACPI_TYPE_INTEGER;
896b9565322Smlelstv 	param[0].Integer.Value =
897b9565322Smlelstv 		(retobj.Integer.Value & THINKPAD_BLUETOOTH_RADIOSSW) == 0
898b9565322Smlelstv 		? THINKPAD_BLUETOOTH_RADIOSSW | THINKPAD_BLUETOOTH_RESUMECTRL
899b9565322Smlelstv 		: 0;
900b9565322Smlelstv 
901b9565322Smlelstv 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SBDC", &params, NULL);
9024ec3c2b1Sjmcneill }
9034ec3c2b1Sjmcneill 
9041fdd2baeSspz static void
thinkpad_wwan_toggle(thinkpad_softc_t * sc)9051fdd2baeSspz thinkpad_wwan_toggle(thinkpad_softc_t *sc)
9061fdd2baeSspz {
907b9565322Smlelstv 	ACPI_BUFFER buf;
908b9565322Smlelstv 	ACPI_OBJECT retobj;
909b9565322Smlelstv 	ACPI_OBJECT param[1];
910b9565322Smlelstv 	ACPI_OBJECT_LIST params;
911b9565322Smlelstv 	ACPI_STATUS rv;
912b9565322Smlelstv 
913b9565322Smlelstv 	buf.Pointer = &retobj;
914b9565322Smlelstv 	buf.Length = sizeof(retobj);
915b9565322Smlelstv 
916b9565322Smlelstv 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GWAN", NULL, &buf);
917b9565322Smlelstv 	if (ACPI_FAILURE(rv))
918b9565322Smlelstv 		return;
919b9565322Smlelstv 
920b9565322Smlelstv 	params.Count = 1;
921b9565322Smlelstv 	params.Pointer = param;
922b9565322Smlelstv 	param[0].Type = ACPI_TYPE_INTEGER;
923b9565322Smlelstv 	param[0].Integer.Value =
924b9565322Smlelstv 		(retobj.Integer.Value & THINKPAD_WWAN_RADIOSSW) == 0
925b9565322Smlelstv 		? THINKPAD_WWAN_RADIOSSW | THINKPAD_WWAN_RESUMECTRL
926b9565322Smlelstv 		: 0;
927b9565322Smlelstv 
928b9565322Smlelstv 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SWAN", &params, NULL);
929b9565322Smlelstv }
930b9565322Smlelstv 
931b9565322Smlelstv static void
thinkpad_uwb_toggle(thinkpad_softc_t * sc)932b9565322Smlelstv thinkpad_uwb_toggle(thinkpad_softc_t *sc)
933b9565322Smlelstv {
934b9565322Smlelstv 	ACPI_BUFFER buf;
935b9565322Smlelstv 	ACPI_OBJECT retobj;
936b9565322Smlelstv 	ACPI_OBJECT param[1];
937b9565322Smlelstv 	ACPI_OBJECT_LIST params;
938b9565322Smlelstv 	ACPI_STATUS rv;
939b9565322Smlelstv 
940b9565322Smlelstv 	buf.Pointer = &retobj;
941b9565322Smlelstv 	buf.Length = sizeof(retobj);
942b9565322Smlelstv 
943b9565322Smlelstv 	rv = AcpiEvaluateObject(sc->sc_node->ad_handle, "GUWB", NULL, &buf);
944b9565322Smlelstv 	if (ACPI_FAILURE(rv))
945b9565322Smlelstv 		return;
946b9565322Smlelstv 
947b9565322Smlelstv 	params.Count = 1;
948b9565322Smlelstv 	params.Pointer = param;
949b9565322Smlelstv 	param[0].Type = ACPI_TYPE_INTEGER;
950b9565322Smlelstv 	param[0].Integer.Value =
951b9565322Smlelstv 		(retobj.Integer.Value & THINKPAD_UWB_RADIOSSW) == 0
952b9565322Smlelstv 		? THINKPAD_UWB_RADIOSSW
953b9565322Smlelstv 		: 0;
954b9565322Smlelstv 
955b9565322Smlelstv 	(void)AcpiEvaluateObject(sc->sc_node->ad_handle, "SUWB", &params, NULL);
9561fdd2baeSspz }
9571fdd2baeSspz 
9585b0f734dSjmcneill static uint8_t
thinkpad_brightness_read(thinkpad_softc_t * sc)9595b0f734dSjmcneill thinkpad_brightness_read(thinkpad_softc_t *sc)
9605b0f734dSjmcneill {
9612dd19c51Sjmcneill 	uint32_t val = 0;
9622dd19c51Sjmcneill 
9632dd19c51Sjmcneill 	AcpiOsWritePort(IO_RTC, 0x6c, 8);
9642dd19c51Sjmcneill 	AcpiOsReadPort(IO_RTC + 1, &val, 8);
9652dd19c51Sjmcneill 
9662dd19c51Sjmcneill 	return val & 7;
9675b0f734dSjmcneill }
9685b0f734dSjmcneill 
9695b0f734dSjmcneill static void
thinkpad_brightness_up(device_t self)9705b0f734dSjmcneill thinkpad_brightness_up(device_t self)
9715b0f734dSjmcneill {
9725b0f734dSjmcneill 	thinkpad_softc_t *sc = device_private(self);
9735b0f734dSjmcneill 
9745b0f734dSjmcneill 	if (thinkpad_brightness_read(sc) == 7)
9755b0f734dSjmcneill 		return;
976f7681ae9Sjruoho 
9775b0f734dSjmcneill 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP);
9785b0f734dSjmcneill }
9795b0f734dSjmcneill 
9805b0f734dSjmcneill static void
thinkpad_brightness_down(device_t self)9815b0f734dSjmcneill thinkpad_brightness_down(device_t self)
9825b0f734dSjmcneill {
9835b0f734dSjmcneill 	thinkpad_softc_t *sc = device_private(self);
9845b0f734dSjmcneill 
9855b0f734dSjmcneill 	if (thinkpad_brightness_read(sc) == 0)
9865b0f734dSjmcneill 		return;
987f7681ae9Sjruoho 
9885b0f734dSjmcneill 	thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN);
9895b0f734dSjmcneill }
9905b0f734dSjmcneill 
9915b0f734dSjmcneill static void
thinkpad_cmos(thinkpad_softc_t * sc,uint8_t cmd)9925b0f734dSjmcneill thinkpad_cmos(thinkpad_softc_t *sc, uint8_t cmd)
9935b0f734dSjmcneill {
9945b0f734dSjmcneill 	ACPI_STATUS rv;
9955b0f734dSjmcneill 
996f7681ae9Sjruoho 	if (sc->sc_cmoshdl == NULL)
9975b0f734dSjmcneill 		return;
9985b0f734dSjmcneill 
99963ec7a18Scegger 	rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd);
1000f7681ae9Sjruoho 
10015b0f734dSjmcneill 	if (ACPI_FAILURE(rv))
1002f7681ae9Sjruoho 		aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n",
10035b0f734dSjmcneill 		    AcpiFormatException(rv));
10045b0f734dSjmcneill }
1005b7cf8d35Sjmcneill 
1006*b9306226Schristos static uint32_t
thinkpad_call_method(device_t self,const char * path,uint32_t arg)1007*b9306226Schristos thinkpad_call_method(device_t self, const char *path, uint32_t arg)
1008*b9306226Schristos {
1009*b9306226Schristos 	thinkpad_softc_t *sc = device_private(self);
1010*b9306226Schristos 	ACPI_HANDLE handle = sc->sc_node->ad_handle;
1011*b9306226Schristos 	ACPI_OBJECT args[1];
1012*b9306226Schristos 	ACPI_OBJECT_LIST arg_list;
1013*b9306226Schristos 	ACPI_OBJECT rval;
1014*b9306226Schristos 	ACPI_BUFFER buf;
1015*b9306226Schristos 	ACPI_STATUS rv;
1016*b9306226Schristos 
1017*b9306226Schristos 	args[0].Type = ACPI_TYPE_INTEGER;
1018*b9306226Schristos 	args[0].Integer.Value = arg;
1019*b9306226Schristos 	arg_list.Pointer = &args[0];
1020*b9306226Schristos 	arg_list.Count = __arraycount(args);
1021*b9306226Schristos 
1022*b9306226Schristos 	memset(&rval, 0, sizeof rval);
1023*b9306226Schristos 	buf.Pointer = &rval;
1024*b9306226Schristos 	buf.Length = sizeof rval;
1025*b9306226Schristos 
1026*b9306226Schristos 	rv = AcpiEvaluateObjectTyped(handle, path, &arg_list, &buf,
1027*b9306226Schristos 	    ACPI_TYPE_INTEGER);
1028*b9306226Schristos 
1029*b9306226Schristos 	if (ACPI_FAILURE(rv)) {
1030*b9306226Schristos 		aprint_error_dev(self, "call %s.%s(%x) failed: %s\n",
1031*b9306226Schristos 		    acpi_name(handle), path, (unsigned)arg,
1032*b9306226Schristos 		    AcpiFormatException(rv));
1033*b9306226Schristos 		return THINKPAD_CALL_ERROR;
1034*b9306226Schristos 	}
1035*b9306226Schristos 
1036*b9306226Schristos 	return rval.Integer.Value;
1037*b9306226Schristos }
1038*b9306226Schristos 
1039*b9306226Schristos static void
thinkpad_battery_probe_support(device_t self)1040*b9306226Schristos thinkpad_battery_probe_support(device_t self)
1041*b9306226Schristos {
1042*b9306226Schristos 	thinkpad_softc_t *sc = device_private(self);
1043*b9306226Schristos 	ACPI_HANDLE hdl = sc->sc_node->ad_handle, tmp;
1044*b9306226Schristos 	ACPI_STATUS rv;
1045*b9306226Schristos 	uint32_t val;
1046*b9306226Schristos 
1047*b9306226Schristos 	sc->sc_batctl.have_any = 0;
1048*b9306226Schristos 
1049*b9306226Schristos 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_START, &tmp);
1050*b9306226Schristos 	if (ACPI_SUCCESS(rv)) {
1051*b9306226Schristos 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_START,
1052*b9306226Schristos 		    THINKPAD_BAT_PRIMARY);
1053*b9306226Schristos 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100)) {
1054*b9306226Schristos 			sc->sc_batctl.have.charge_start = 1;
1055*b9306226Schristos 			if (val & 0x200)
1056*b9306226Schristos 				sc->sc_batctl.have.individual_control = 1;
1057*b9306226Schristos 		}
1058*b9306226Schristos 	}
1059*b9306226Schristos 
1060*b9306226Schristos 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_STOP, &tmp);
1061*b9306226Schristos 	if (ACPI_SUCCESS(rv)) {
1062*b9306226Schristos 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_STOP,
1063*b9306226Schristos 		    THINKPAD_BAT_PRIMARY);
1064*b9306226Schristos 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100))
1065*b9306226Schristos 			sc->sc_batctl.have.charge_stop = 1;
1066*b9306226Schristos 	}
1067*b9306226Schristos 
1068*b9306226Schristos 	rv = AcpiGetHandle(hdl, THINKPAD_GET_FORCE_DISCHARGE, &tmp);
1069*b9306226Schristos 	if (ACPI_SUCCESS(rv)) {
1070*b9306226Schristos 		val = thinkpad_call_method(self, THINKPAD_GET_FORCE_DISCHARGE,
1071*b9306226Schristos 		    THINKPAD_BAT_PRIMARY);
1072*b9306226Schristos 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x100))
1073*b9306226Schristos 			sc->sc_batctl.have.force_discharge = 1;
1074*b9306226Schristos 	}
1075*b9306226Schristos 
1076*b9306226Schristos 	rv = AcpiGetHandle(hdl, THINKPAD_GET_CHARGE_INHIBIT, &tmp);
1077*b9306226Schristos 	if (ACPI_SUCCESS(rv)) {
1078*b9306226Schristos 		val = thinkpad_call_method(self, THINKPAD_GET_CHARGE_INHIBIT,
1079*b9306226Schristos 		    THINKPAD_BAT_PRIMARY);
1080*b9306226Schristos 		if (!(val & THINKPAD_CALL_ERROR) && (val & 0x20))
1081*b9306226Schristos 			sc->sc_batctl.have.charge_inhibit = 1;
1082*b9306226Schristos 	}
1083*b9306226Schristos 
1084*b9306226Schristos 	if (sc->sc_batctl.have_any)
1085*b9306226Schristos 		aprint_verbose_dev(self, "battery control capabilities: %x\n",
1086*b9306226Schristos 		    sc->sc_batctl.have_any);
1087*b9306226Schristos }
1088*b9306226Schristos 
1089*b9306226Schristos static int
thinkpad_battery_sysctl_charge_start(SYSCTLFN_ARGS)1090*b9306226Schristos thinkpad_battery_sysctl_charge_start(SYSCTLFN_ARGS)
1091*b9306226Schristos {
1092*b9306226Schristos 	struct sysctlnode node = *rnode;
1093*b9306226Schristos 	tp_sysctl_param_t *sp = node.sysctl_data;
1094*b9306226Schristos 	int charge_start;
1095*b9306226Schristos 	int err;
1096*b9306226Schristos 
1097*b9306226Schristos 	charge_start = thinkpad_call_method(sp->sp_dev,
1098*b9306226Schristos 	    THINKPAD_GET_CHARGE_START, sp->sp_bat) & 0xff;
1099*b9306226Schristos 
1100*b9306226Schristos 	node.sysctl_data = &charge_start;
1101*b9306226Schristos 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1102*b9306226Schristos 	if (err || newp == NULL)
1103*b9306226Schristos 		return err;
1104*b9306226Schristos 
1105*b9306226Schristos 	if (charge_start < 0 || charge_start > 99)
1106*b9306226Schristos 		return EINVAL;
1107*b9306226Schristos 
1108*b9306226Schristos 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_START,
1109*b9306226Schristos 	    charge_start | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1110*b9306226Schristos 		return EIO;
1111*b9306226Schristos 
1112*b9306226Schristos 	return 0;
1113*b9306226Schristos }
1114*b9306226Schristos 
1115*b9306226Schristos static int
thinkpad_battery_sysctl_charge_stop(SYSCTLFN_ARGS)1116*b9306226Schristos thinkpad_battery_sysctl_charge_stop(SYSCTLFN_ARGS)
1117*b9306226Schristos {
1118*b9306226Schristos 	struct sysctlnode node = *rnode;
1119*b9306226Schristos 	tp_sysctl_param_t *sp = node.sysctl_data;
1120*b9306226Schristos 	int charge_stop;
1121*b9306226Schristos 	int err;
1122*b9306226Schristos 
1123*b9306226Schristos 	charge_stop = thinkpad_call_method(sp->sp_dev,
1124*b9306226Schristos 	    THINKPAD_GET_CHARGE_STOP, sp->sp_bat) & 0xff;
1125*b9306226Schristos 
1126*b9306226Schristos 	if (charge_stop == 0)
1127*b9306226Schristos 		charge_stop = 100;
1128*b9306226Schristos 
1129*b9306226Schristos 	node.sysctl_data = &charge_stop;
1130*b9306226Schristos 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1131*b9306226Schristos 	if (err || newp == NULL)
1132*b9306226Schristos 		return err;
1133*b9306226Schristos 
1134*b9306226Schristos 	if (charge_stop < 1 || charge_stop > 100)
1135*b9306226Schristos 		return EINVAL;
1136*b9306226Schristos 
1137*b9306226Schristos 	if (charge_stop == 100)
1138*b9306226Schristos 		charge_stop = 0;
1139*b9306226Schristos 
1140*b9306226Schristos 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_STOP,
1141*b9306226Schristos 	    charge_stop | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1142*b9306226Schristos 		return EIO;
1143*b9306226Schristos 
1144*b9306226Schristos 	return 0;
1145*b9306226Schristos }
1146*b9306226Schristos 
1147*b9306226Schristos static int
thinkpad_battery_sysctl_charge_inhibit(SYSCTLFN_ARGS)1148*b9306226Schristos thinkpad_battery_sysctl_charge_inhibit(SYSCTLFN_ARGS)
1149*b9306226Schristos {
1150*b9306226Schristos 	struct sysctlnode node = *rnode;
1151*b9306226Schristos 	tp_sysctl_param_t *sp = node.sysctl_data;
1152*b9306226Schristos 	bool charge_inhibit;
1153*b9306226Schristos 	int err;
1154*b9306226Schristos 
1155*b9306226Schristos 	charge_inhibit = thinkpad_call_method(sp->sp_dev,
1156*b9306226Schristos 	    THINKPAD_GET_CHARGE_INHIBIT, sp->sp_bat) & 0x01;
1157*b9306226Schristos 
1158*b9306226Schristos 	node.sysctl_data = &charge_inhibit;
1159*b9306226Schristos 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1160*b9306226Schristos 	if (err || newp == NULL)
1161*b9306226Schristos 		return err;
1162*b9306226Schristos 
1163*b9306226Schristos 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_CHARGE_INHIBIT,
1164*b9306226Schristos 	    charge_inhibit | sp->sp_bat<<4 | 0xffff<<8) & THINKPAD_CALL_ERROR)
1165*b9306226Schristos 		return EIO;
1166*b9306226Schristos 
1167*b9306226Schristos 	return 0;
1168*b9306226Schristos }
1169*b9306226Schristos 
1170*b9306226Schristos static int
thinkpad_battery_sysctl_force_discharge(SYSCTLFN_ARGS)1171*b9306226Schristos thinkpad_battery_sysctl_force_discharge(SYSCTLFN_ARGS)
1172*b9306226Schristos {
1173*b9306226Schristos 	struct sysctlnode node = *rnode;
1174*b9306226Schristos 	tp_sysctl_param_t *sp = node.sysctl_data;
1175*b9306226Schristos 	bool force_discharge;
1176*b9306226Schristos 	int err;
1177*b9306226Schristos 
1178*b9306226Schristos 	force_discharge = thinkpad_call_method(sp->sp_dev,
1179*b9306226Schristos 	    THINKPAD_GET_FORCE_DISCHARGE, sp->sp_bat) & 0x01;
1180*b9306226Schristos 
1181*b9306226Schristos 	node.sysctl_data = &force_discharge;
1182*b9306226Schristos 	err = sysctl_lookup(SYSCTLFN_CALL(&node));
1183*b9306226Schristos 	if (err || newp == NULL)
1184*b9306226Schristos 		return err;
1185*b9306226Schristos 
1186*b9306226Schristos 	if (thinkpad_call_method(sp->sp_dev, THINKPAD_SET_FORCE_DISCHARGE,
1187*b9306226Schristos 	    force_discharge | sp->sp_bat<<8) & THINKPAD_CALL_ERROR)
1188*b9306226Schristos 		return EIO;
1189*b9306226Schristos 
1190*b9306226Schristos 	return 0;
1191*b9306226Schristos }
1192*b9306226Schristos 
1193*b9306226Schristos static void
thinkpad_battery_sysctl_setup_controls(device_t self,const struct sysctlnode * rnode,int battery)1194*b9306226Schristos thinkpad_battery_sysctl_setup_controls(device_t self,
1195*b9306226Schristos     const struct sysctlnode *rnode, int battery)
1196*b9306226Schristos {
1197*b9306226Schristos 	thinkpad_softc_t *sc = device_private(self);
1198*b9306226Schristos 
1199*b9306226Schristos 	if (sc->sc_batctl.have.charge_start)
1200*b9306226Schristos 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1201*b9306226Schristos 		    CTLFLAG_READWRITE, CTLTYPE_INT, "charge_start",
1202*b9306226Schristos 		    SYSCTL_DESCR("charge start threshold (0-99)"),
1203*b9306226Schristos 		    thinkpad_battery_sysctl_charge_start, 0,
1204*b9306226Schristos 		    (void *)&(sc->sc_scparam[battery]), 0,
1205*b9306226Schristos 		    CTL_CREATE, CTL_EOL);
1206*b9306226Schristos 
1207*b9306226Schristos 	if (sc->sc_batctl.have.charge_stop)
1208*b9306226Schristos 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1209*b9306226Schristos 		    CTLFLAG_READWRITE, CTLTYPE_INT, "charge_stop",
1210*b9306226Schristos 		    SYSCTL_DESCR("charge stop threshold (1-100)"),
1211*b9306226Schristos 		    thinkpad_battery_sysctl_charge_stop, 0,
1212*b9306226Schristos 		    (void *)&(sc->sc_scparam[battery]), 0,
1213*b9306226Schristos 		    CTL_CREATE, CTL_EOL);
1214*b9306226Schristos 
1215*b9306226Schristos 	if (sc->sc_batctl.have.charge_inhibit)
1216*b9306226Schristos 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1217*b9306226Schristos 		    CTLFLAG_READWRITE, CTLTYPE_BOOL, "charge_inhibit",
1218*b9306226Schristos 		    SYSCTL_DESCR("charge inhibit"),
1219*b9306226Schristos 		    thinkpad_battery_sysctl_charge_inhibit, 0,
1220*b9306226Schristos 		    (void *)&(sc->sc_scparam[battery]), 0,
1221*b9306226Schristos 		    CTL_CREATE, CTL_EOL);
1222*b9306226Schristos 
1223*b9306226Schristos 	if (sc->sc_batctl.have.force_discharge)
1224*b9306226Schristos 		(void)sysctl_createv(&sc->sc_log, 0, &rnode, NULL,
1225*b9306226Schristos 		    CTLFLAG_READWRITE, CTLTYPE_BOOL, "force_discharge",
1226*b9306226Schristos 		    SYSCTL_DESCR("force discharge"),
1227*b9306226Schristos 		    thinkpad_battery_sysctl_force_discharge, 0,
1228*b9306226Schristos 		    (void *)&(sc->sc_scparam[battery]), 0,
1229*b9306226Schristos 		    CTL_CREATE, CTL_EOL);
1230*b9306226Schristos }
1231*b9306226Schristos 
1232*b9306226Schristos static void
thinkpad_battery_sysctl_setup(device_t self)1233*b9306226Schristos thinkpad_battery_sysctl_setup(device_t self)
1234*b9306226Schristos {
1235*b9306226Schristos 	thinkpad_softc_t *sc = device_private(self);
1236*b9306226Schristos 	const struct sysctlnode *rnode, *cnode;
1237*b9306226Schristos 	int err;
1238*b9306226Schristos 
1239*b9306226Schristos 	err = sysctl_createv(&sc->sc_log, 0, NULL, &rnode,
1240*b9306226Schristos 	    0, CTLTYPE_NODE, "acpi", NULL,
1241*b9306226Schristos 	    NULL, 0, NULL, 0, CTL_HW, CTL_CREATE, CTL_EOL);
1242*b9306226Schristos 	if (err)
1243*b9306226Schristos 		goto fail;
1244*b9306226Schristos 
1245*b9306226Schristos 	err = sysctl_createv(&sc->sc_log, 0, &rnode, &rnode,
1246*b9306226Schristos 	    0, CTLTYPE_NODE, device_xname(self),
1247*b9306226Schristos 	    SYSCTL_DESCR("ThinkPad ACPI controls"),
1248*b9306226Schristos 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1249*b9306226Schristos 	if (err)
1250*b9306226Schristos 		goto fail;
1251*b9306226Schristos 
1252*b9306226Schristos 	if (sc->sc_batctl.have.individual_control) {
1253*b9306226Schristos 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1254*b9306226Schristos 		    0, CTLTYPE_NODE, "bat0",
1255*b9306226Schristos 		    SYSCTL_DESCR("battery charge controls (primary battery)"),
1256*b9306226Schristos 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1257*b9306226Schristos 		if (err)
1258*b9306226Schristos 			goto fail;
1259*b9306226Schristos 
1260*b9306226Schristos 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1261*b9306226Schristos 		    THINKPAD_BAT_PRIMARY);
1262*b9306226Schristos 
1263*b9306226Schristos 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1264*b9306226Schristos 		    0, CTLTYPE_NODE, "bat1",
1265*b9306226Schristos 		    SYSCTL_DESCR("battery charge controls (secondary battery)"),
1266*b9306226Schristos 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1267*b9306226Schristos 		if (err)
1268*b9306226Schristos 			goto fail;
1269*b9306226Schristos 
1270*b9306226Schristos 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1271*b9306226Schristos 		    THINKPAD_BAT_SECONDARY);
1272*b9306226Schristos 	} else {
1273*b9306226Schristos 		err = sysctl_createv(&sc->sc_log, 0, &rnode, &cnode,
1274*b9306226Schristos 		    0, CTLTYPE_NODE, "bat",
1275*b9306226Schristos 		    SYSCTL_DESCR("battery charge controls"),
1276*b9306226Schristos 		    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
1277*b9306226Schristos 		if (err)
1278*b9306226Schristos 			goto fail;
1279*b9306226Schristos 
1280*b9306226Schristos 		thinkpad_battery_sysctl_setup_controls(self, cnode,
1281*b9306226Schristos 		    THINKPAD_BAT_ANY);
1282*b9306226Schristos 	}
1283*b9306226Schristos 
1284*b9306226Schristos 	return;
1285*b9306226Schristos 
1286*b9306226Schristos fail:
1287*b9306226Schristos 	aprint_error_dev(self, "unable to add sysctl nodes (%d)\n", err);
1288*b9306226Schristos }
1289*b9306226Schristos 
1290b7cf8d35Sjmcneill static bool
thinkpad_resume(device_t dv,const pmf_qual_t * qual)1291c1b390d4Sdyoung thinkpad_resume(device_t dv, const pmf_qual_t *qual)
1292b7cf8d35Sjmcneill {
12931e511da0Sjruoho 	thinkpad_softc_t *sc = device_private(dv);
1294b7cf8d35Sjmcneill 
12951e511da0Sjruoho 	if (sc->sc_powhdl == NULL)
12961e511da0Sjruoho 		return true;
1297b7cf8d35Sjmcneill 
12981e511da0Sjruoho 	(void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true);
1299b7cf8d35Sjmcneill 
1300b7cf8d35Sjmcneill 	return true;
1301b7cf8d35Sjmcneill }
130286c30149Sjruoho 
130376a2f91aSpgoyette MODULE(MODULE_CLASS_DRIVER, thinkpad, "sysmon_envsys,sysmon_power");
130486c30149Sjruoho 
13052b0b13f2Sjruoho #ifdef _MODULE
13062b0b13f2Sjruoho #include "ioconf.c"
13072b0b13f2Sjruoho #endif
130886c30149Sjruoho 
130986c30149Sjruoho static int
thinkpad_modcmd(modcmd_t cmd,void * aux)13102b0b13f2Sjruoho thinkpad_modcmd(modcmd_t cmd, void *aux)
131186c30149Sjruoho {
13122b0b13f2Sjruoho 	int rv = 0;
131386c30149Sjruoho 
131486c30149Sjruoho 	switch (cmd) {
131586c30149Sjruoho 
131686c30149Sjruoho 	case MODULE_CMD_INIT:
131786c30149Sjruoho 
13182b0b13f2Sjruoho #ifdef _MODULE
13192b0b13f2Sjruoho 		rv = config_init_component(cfdriver_ioconf_thinkpad,
13202b0b13f2Sjruoho 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
13212b0b13f2Sjruoho #endif
13222b0b13f2Sjruoho 		break;
132386c30149Sjruoho 
132486c30149Sjruoho 	case MODULE_CMD_FINI:
132586c30149Sjruoho 
13262b0b13f2Sjruoho #ifdef _MODULE
13272b0b13f2Sjruoho 		rv = config_fini_component(cfdriver_ioconf_thinkpad,
13282b0b13f2Sjruoho 		    cfattach_ioconf_thinkpad, cfdata_ioconf_thinkpad);
13292b0b13f2Sjruoho #endif
13302b0b13f2Sjruoho 		break;
133186c30149Sjruoho 
133286c30149Sjruoho 	default:
13332b0b13f2Sjruoho 		rv = ENOTTY;
133486c30149Sjruoho 	}
133586c30149Sjruoho 
13362b0b13f2Sjruoho 	return rv;
13372b0b13f2Sjruoho }
1338