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