xref: /openbsd-src/sys/dev/acpi/acpithinkpad.c (revision 3e37c3917fb64de485d8029e0dc97361c537fde4)
1*3e37c391Sclaudio /*	$OpenBSD: acpithinkpad.c,v 1.74 2023/07/07 07:37:59 claudio Exp $	*/
2866be0c9Sjcs /*
3866be0c9Sjcs  * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
4866be0c9Sjcs  *
5866be0c9Sjcs  * Permission to use, copy, modify, and distribute this software for any
6866be0c9Sjcs  * purpose with or without fee is hereby granted, provided that the above
7866be0c9Sjcs  * copyright notice and this permission notice appear in all copies.
8866be0c9Sjcs  *
9866be0c9Sjcs  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10866be0c9Sjcs  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11866be0c9Sjcs  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12866be0c9Sjcs  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13866be0c9Sjcs  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14866be0c9Sjcs  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15866be0c9Sjcs  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16866be0c9Sjcs  */
17866be0c9Sjcs 
18866be0c9Sjcs #include <sys/param.h>
19866be0c9Sjcs #include <sys/systm.h>
20ec849c8aSjsg #include <sys/sensors.h>
21866be0c9Sjcs 
22866be0c9Sjcs #include <dev/acpi/acpireg.h>
23866be0c9Sjcs #include <dev/acpi/acpivar.h>
24866be0c9Sjcs #include <dev/acpi/acpidev.h>
25866be0c9Sjcs #include <dev/acpi/amltypes.h>
26866be0c9Sjcs #include <dev/acpi/dsdt.h>
2723b692d1Skettenis #include <dev/wscons/wsconsio.h>
28d95a16b0Skettenis #include <dev/wscons/wsdisplayvar.h>
29866be0c9Sjcs 
3030cdf572Sderaadt #include <machine/apmvar.h>
3130cdf572Sderaadt 
3292a3e4ccSderaadt #include "audio.h"
3392a3e4ccSderaadt #include "wskbd.h"
3492a3e4ccSderaadt 
35b96dcde9Sjcs /* #define ACPITHINKPAD_DEBUG */
36b96dcde9Sjcs 
37b96dcde9Sjcs #ifdef ACPITHINKPAD_DEBUG
38b96dcde9Sjcs #define DPRINTF(x) printf x
39b96dcde9Sjcs #else
40b96dcde9Sjcs #define DPRINTF(x)
41b96dcde9Sjcs #endif
42b96dcde9Sjcs 
438107bf4dSjcs #define	THINKPAD_HKEY_VERSION1		0x0100
448107bf4dSjcs #define	THINKPAD_HKEY_VERSION2		0x0200
45866be0c9Sjcs 
463e7c1b35Sjcs #define	THINKPAD_CMOS_VOLUME_DOWN	0x00
473e7c1b35Sjcs #define	THINKPAD_CMOS_VOLUME_UP		0x01
483e7c1b35Sjcs #define	THINKPAD_CMOS_VOLUME_MUTE	0x02
49866be0c9Sjcs #define	THINKPAD_CMOS_BRIGHTNESS_UP	0x04
50866be0c9Sjcs #define	THINKPAD_CMOS_BRIGHTNESS_DOWN	0x05
51866be0c9Sjcs 
52866be0c9Sjcs #define	THINKPAD_BLUETOOTH_PRESENT	0x01
53866be0c9Sjcs #define	THINKPAD_BLUETOOTH_ENABLED	0x02
54866be0c9Sjcs 
55866be0c9Sjcs /* wan (not wifi) card */
56866be0c9Sjcs #define	THINKPAD_WAN_PRESENT		0x01
57866be0c9Sjcs #define	THINKPAD_WAN_ENABLED		0x02
58866be0c9Sjcs 
590b3884edSfgsch #define	THINKPAD_BUTTON_FN_F1		0x1001
600b3884edSfgsch #define	THINKPAD_BUTTON_LOCK_SCREEN	0x1002
610b3884edSfgsch #define	THINKPAD_BUTTON_BATTERY_INFO	0x1003
620b3884edSfgsch #define	THINKPAD_BUTTON_SUSPEND		0x1004
630b3884edSfgsch #define	THINKPAD_BUTTON_WIRELESS	0x1005
640b3884edSfgsch #define	THINKPAD_BUTTON_FN_F6		0x1006
650b3884edSfgsch #define	THINKPAD_BUTTON_EXTERNAL_SCREEN	0x1007
660b3884edSfgsch #define	THINKPAD_BUTTON_POINTER_SWITCH	0x1008
670b3884edSfgsch #define	THINKPAD_BUTTON_EJECT		0x1009
6825556f88Skettenis #define	THINKPAD_BUTTON_FN_F11		0x100b
6925556f88Skettenis #define	THINKPAD_BUTTON_HIBERNATE	0x100c
700b3884edSfgsch #define	THINKPAD_BUTTON_BRIGHTNESS_UP	0x1010
710b3884edSfgsch #define	THINKPAD_BUTTON_BRIGHTNESS_DOWN	0x1011
720b3884edSfgsch #define	THINKPAD_BUTTON_THINKLIGHT	0x1012
730b3884edSfgsch #define	THINKPAD_BUTTON_FN_SPACE	0x1014
740b3884edSfgsch #define	THINKPAD_BUTTON_VOLUME_UP	0x1015
750b3884edSfgsch #define	THINKPAD_BUTTON_VOLUME_DOWN	0x1016
760b3884edSfgsch #define	THINKPAD_BUTTON_VOLUME_MUTE	0x1017
770b3884edSfgsch #define	THINKPAD_BUTTON_THINKVANTAGE	0x1018
78571d6818Smiod #define	THINKPAD_BUTTON_BLACK		0x101a
7992a3e4ccSderaadt #define	THINKPAD_BUTTON_MICROPHONE_MUTE	0x101b
8026f5b670Sderaadt #define	THINKPAD_KEYLIGHT_CHANGED	0x101c
8126f5b670Sderaadt #define	THINKPAD_BUTTON_CONFIG		0x101d
82b7f7f96eSphessler #define	THINKPAD_BUTTON_FIND		0x101e
83b7f7f96eSphessler #define	THINKPAD_BUTTON_ALL_ACTIVEPROGS	0x101f
84b7f7f96eSphessler #define	THINKPAD_BUTTON_ALL_PROGS	0x1020
85b7f7f96eSphessler 
868107bf4dSjcs #define	THINKPAD_ADAPTIVE_NEXT		0x1101
878107bf4dSjcs #define	THINKPAD_ADAPTIVE_QUICK		0x1102
888107bf4dSjcs #define	THINKPAD_ADAPTIVE_SNIP		0x1105
898107bf4dSjcs #define	THINKPAD_ADAPTIVE_VOICE		0x1108
908107bf4dSjcs #define	THINKPAD_ADAPTIVE_GESTURES	0x110a
918107bf4dSjcs #define	THINKPAD_ADAPTIVE_SETTINGS	0x110e
928107bf4dSjcs #define	THINKPAD_ADAPTIVE_TAB		0x110f
938107bf4dSjcs #define	THINKPAD_ADAPTIVE_REFRESH	0x1110
948107bf4dSjcs #define	THINKPAD_ADAPTIVE_BACK		0x1111
95abb81af2Smlarkin #define THINKPAD_PORT_REPL_DOCKED	0x4010
96abb81af2Smlarkin #define THINKPAD_PORT_REPL_UNDOCKED	0x4011
9702830852Sstsp #define	THINKPAD_TABLET_DOCKED		0x4012
9802830852Sstsp #define	THINKPAD_TABLET_UNDOCKED	0x4013
990b3884edSfgsch #define	THINKPAD_LID_OPEN		0x5001
1000b3884edSfgsch #define	THINKPAD_LID_CLOSED		0x5002
1010b3884edSfgsch #define	THINKPAD_TABLET_SCREEN_NORMAL	0x500a
1020b3884edSfgsch #define	THINKPAD_TABLET_SCREEN_ROTATED	0x5009
1030b3884edSfgsch #define	THINKPAD_BRIGHTNESS_CHANGED	0x5010
1040b3884edSfgsch #define	THINKPAD_TABLET_PEN_INSERTED	0x500b
1050b3884edSfgsch #define	THINKPAD_TABLET_PEN_REMOVED	0x500c
106f91475cfSderaadt #define	THINKPAD_SWITCH_NUMLOCK		0x6000
1071862e1cbSguenther #define	THINKPAD_BUTTON_ROTATION_LOCK	0x6020
10899f2e9f6Sjcs #define	THINKPAD_THERMAL_TABLE_CHANGED	0x6030
10999f2e9f6Sjcs #define	THINKPAD_POWER_CHANGED		0x6040
1107fcffd8bSkettenis #define	THINKPAD_BACKLIGHT_CHANGED	0x6050
111f84de5a3Sderaadt #define	THINKPAD_BUTTON_FN_TOGGLE       0x6060
11226f5b670Sderaadt #define	THINKPAD_TABLET_SCREEN_CHANGED	0x60c0
1130b3884edSfgsch #define	THINKPAD_SWITCH_WIRELESS	0x7000
1143e7c1b35Sjcs 
115d91d9c7cSjcs #define THINKPAD_SENSOR_FANRPM		0
116d91d9c7cSjcs #define THINKPAD_SENSOR_PORTREPL	1
117d91d9c7cSjcs #define THINKPAD_SENSOR_TMP0		2
118306c5139Smlarkin #define THINKPAD_NSENSORS		10
119306c5139Smlarkin 
120f07ad920Sderaadt #define THINKPAD_ECOFFSET_VOLUME	0x30
121f07ad920Sderaadt #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
1221c450453Sjordan #define THINKPAD_ECOFFSET_FANLO		0x84
1231c450453Sjordan #define THINKPAD_ECOFFSET_FANHI		0x85
1241c450453Sjordan 
1258107bf4dSjcs #define	THINKPAD_ADAPTIVE_MODE_HOME	1
1268107bf4dSjcs #define	THINKPAD_ADAPTIVE_MODE_FUNCTION	3
1278107bf4dSjcs 
128171b130bSjcs #define THINKPAD_MASK_MIC_MUTE		(1 << 14)
1290d574372Sjcs #define THINKPAD_MASK_BRIGHTNESS_UP	(1 << 15)
1300d574372Sjcs #define THINKPAD_MASK_BRIGHTNESS_DOWN	(1 << 16)
1310d574372Sjcs #define THINKPAD_MASK_KBD_BACKLIGHT	(1 << 17)
1320d574372Sjcs 
133a81f321cSkettenis #define THINKPAD_BATTERY_ERROR		0x80000000
134a81f321cSkettenis #define THINKPAD_BATTERY_SUPPORT	0x00000100
135a81f321cSkettenis #define THINKPAD_BATTERY_SUPPORT_BICG	0x00000020
136a81f321cSkettenis #define THINKPAD_BATTERY_SHIFT		8
137a81f321cSkettenis 
138866be0c9Sjcs struct acpithinkpad_softc {
139866be0c9Sjcs 	struct device		 sc_dev;
140866be0c9Sjcs 
1411c450453Sjordan 	struct acpiec_softc     *sc_ec;
142866be0c9Sjcs 	struct acpi_softc	*sc_acpi;
143866be0c9Sjcs 	struct aml_node		*sc_devnode;
1441c450453Sjordan 
1451c450453Sjordan 	struct ksensor		 sc_sens[THINKPAD_NSENSORS];
1461c450453Sjordan 	struct ksensordev	 sc_sensdev;
147d91d9c7cSjcs 	int			 sc_ntempsens;
14823b692d1Skettenis 
1490d574372Sjcs 	uint64_t		 sc_hkey_version;
1500d574372Sjcs 
15123b692d1Skettenis 	uint64_t		 sc_thinklight;
152c1535f53Skettenis 	const char		*sc_thinklight_get;
153c1535f53Skettenis 	const char		*sc_thinklight_set;
1544ea90f4eSkettenis 
1554ea90f4eSkettenis 	uint64_t		 sc_brightness;
156866be0c9Sjcs };
157866be0c9Sjcs 
158d2eaebe9Skettenis extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
1591c450453Sjordan 
160866be0c9Sjcs int	thinkpad_match(struct device *, void *, void *);
161866be0c9Sjcs void	thinkpad_attach(struct device *, struct device *, void *);
162866be0c9Sjcs int	thinkpad_hotkey(struct aml_node *, int, void *);
163866be0c9Sjcs int	thinkpad_enable_events(struct acpithinkpad_softc *);
164866be0c9Sjcs int	thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
165866be0c9Sjcs int	thinkpad_toggle_wan(struct acpithinkpad_softc *);
166866be0c9Sjcs int	thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
1673e7c1b35Sjcs int	thinkpad_volume_down(struct acpithinkpad_softc *);
1683e7c1b35Sjcs int	thinkpad_volume_up(struct acpithinkpad_softc *);
1693e7c1b35Sjcs int	thinkpad_volume_mute(struct acpithinkpad_softc *);
170c2b1253dSjcs int	thinkpad_brightness_up(struct acpithinkpad_softc *);
171e7f08eacSjsg int	thinkpad_brightness_down(struct acpithinkpad_softc *);
1728107bf4dSjcs int	thinkpad_adaptive_change(struct acpithinkpad_softc *);
1738107bf4dSjcs int	thinkpad_activate(struct device *, int);
174866be0c9Sjcs 
1754ea90f4eSkettenis /* wscons hook functions */
17623b692d1Skettenis void	thinkpad_get_thinklight(struct acpithinkpad_softc *);
17723b692d1Skettenis void	thinkpad_set_thinklight(void *, int);
1780dc71f27Sjcs int	thinkpad_get_kbd_backlight(struct wskbd_backlight *);
1790dc71f27Sjcs int	thinkpad_set_kbd_backlight(struct wskbd_backlight *);
18023b692d1Skettenis extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
18123b692d1Skettenis extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
182171b130bSjcs int	thinkpad_get_brightness(struct acpithinkpad_softc *);
183171b130bSjcs int	thinkpad_set_brightness(void *, int);
1844ea90f4eSkettenis int	thinkpad_get_param(struct wsdisplay_param *);
1854ea90f4eSkettenis int	thinkpad_set_param(struct wsdisplay_param *);
186d91d9c7cSjcs int	thinkpad_get_temp(struct acpithinkpad_softc *, int, int64_t *);
18723b692d1Skettenis 
1881c450453Sjordan void    thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
1891c450453Sjordan void    thinkpad_sensor_refresh(void *);
1901c450453Sjordan 
19192a3e4ccSderaadt #if NAUDIO > 0 && NWSKBD > 0
192f07ad920Sderaadt void thinkpad_attach_deferred(void *);
193f07ad920Sderaadt int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
194f07ad920Sderaadt extern int wskbd_set_mixermute(long, long);
195012023bfSmpi extern int wskbd_set_mixervolume(long, long);
19692a3e4ccSderaadt #endif
19792a3e4ccSderaadt 
198a81f321cSkettenis int	thinkpad_battery_setchargemode(int);
199a81f321cSkettenis int	thinkpad_battery_setchargestart(int);
200a81f321cSkettenis int	thinkpad_battery_setchargestop(int);
201a81f321cSkettenis 
202a81f321cSkettenis extern int (*hw_battery_setchargemode)(int);
203a81f321cSkettenis extern int (*hw_battery_setchargestart)(int);
204a81f321cSkettenis extern int (*hw_battery_setchargestop)(int);
205a81f321cSkettenis extern int hw_battery_chargemode;
206a81f321cSkettenis extern int hw_battery_chargestart;
207a81f321cSkettenis extern int hw_battery_chargestop;
208a81f321cSkettenis 
209471aeecfSnaddy const struct cfattach acpithinkpad_ca = {
2108107bf4dSjcs 	sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
2118107bf4dSjcs 	NULL, thinkpad_activate
212866be0c9Sjcs };
213866be0c9Sjcs 
214866be0c9Sjcs struct cfdriver acpithinkpad_cd = {
215866be0c9Sjcs 	NULL, "acpithinkpad", DV_DULL
216866be0c9Sjcs };
217866be0c9Sjcs 
2184adc7ca3Sjcs const char *acpithinkpad_hids[] = {
2196bf9874fSjcs 	"IBM0068",
2206bf9874fSjcs 	"LEN0068",
2216bf9874fSjcs 	"LEN0268",
222128e94b2Smlarkin 	NULL
2234adc7ca3Sjcs };
2247df90d51Smarco 
225866be0c9Sjcs int
thinkpad_match(struct device * parent,void * match,void * aux)226866be0c9Sjcs thinkpad_match(struct device *parent, void *match, void *aux)
227866be0c9Sjcs {
228866be0c9Sjcs 	struct acpi_attach_args	*aa = aux;
229866be0c9Sjcs 	struct cfdata *cf = match;
230a95d1b72Sjordan 	int64_t	res;
231866be0c9Sjcs 
2327df90d51Smarco 	if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
23339d6c70eSderaadt 		return (0);
234866be0c9Sjcs 
235a95d1b72Sjordan 	if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
236866be0c9Sjcs 	    "MHKV", 0, NULL, &res))
23739d6c70eSderaadt 		return (0);
238866be0c9Sjcs 
2398107bf4dSjcs 	if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
2400b3884edSfgsch 		return (0);
241866be0c9Sjcs 
2420b3884edSfgsch 	return (1);
243866be0c9Sjcs }
244866be0c9Sjcs 
245866be0c9Sjcs void
thinkpad_sensor_attach(struct acpithinkpad_softc * sc)2461c450453Sjordan thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
2471c450453Sjordan {
248d91d9c7cSjcs 	int64_t tmp;
2491c450453Sjordan 	int i;
2501c450453Sjordan 
2511c450453Sjordan 	if (sc->sc_acpi->sc_ec == NULL)
2521c450453Sjordan 		return;
2531c450453Sjordan 	sc->sc_ec = sc->sc_acpi->sc_ec;
2541c450453Sjordan 
2551c450453Sjordan 	/* Add temperature probes */
2561c450453Sjordan 	strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
2571c450453Sjordan 	    sizeof(sc->sc_sensdev.xname));
258d91d9c7cSjcs 	sc->sc_ntempsens = 0;
259d91d9c7cSjcs 	for (i = 0; i < THINKPAD_NSENSORS - THINKPAD_SENSOR_TMP0; i++) {
260d91d9c7cSjcs 		if (thinkpad_get_temp(sc, i, &tmp) != 0)
261d91d9c7cSjcs 			break;
262d91d9c7cSjcs 
263d91d9c7cSjcs 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].type = SENSOR_TEMP;
264d91d9c7cSjcs 		sensor_attach(&sc->sc_sensdev,
265d91d9c7cSjcs 		    &sc->sc_sens[THINKPAD_SENSOR_TMP0 + i]);
266d91d9c7cSjcs 		sc->sc_ntempsens++;
2671c450453Sjordan  	}
2681c450453Sjordan 
2691c450453Sjordan 	/* Add fan probe */
270306c5139Smlarkin 	sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
271306c5139Smlarkin 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
272306c5139Smlarkin 
273306c5139Smlarkin 	/* Add port replicator indicator */
274306c5139Smlarkin 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
275306c5139Smlarkin 	sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
276306c5139Smlarkin 	strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
277306c5139Smlarkin 	        sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
278306c5139Smlarkin 	sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
2791c450453Sjordan 
2801c450453Sjordan 	sensordev_install(&sc->sc_sensdev);
2811c450453Sjordan }
2821c450453Sjordan 
2831c450453Sjordan void
thinkpad_sensor_refresh(void * arg)2841c450453Sjordan thinkpad_sensor_refresh(void *arg)
2851c450453Sjordan {
2861c450453Sjordan 	struct acpithinkpad_softc *sc = arg;
287d2eaebe9Skettenis 	uint8_t lo, hi, i;
2881c450453Sjordan 	int64_t tmp;
2891c450453Sjordan 
2901c450453Sjordan 	/* Refresh sensor readings */
291d91d9c7cSjcs 	for (i = 0; i < sc->sc_ntempsens; i++) {
292d91d9c7cSjcs 		if (thinkpad_get_temp(sc, i, &tmp) != 0) {
2931c450453Sjordan  			sc->sc_sens[i].flags = SENSOR_FINVALID;
294d91d9c7cSjcs 			continue;
295d91d9c7cSjcs 		}
296d91d9c7cSjcs 
297d91d9c7cSjcs 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].value =
298d91d9c7cSjcs 		    (tmp * 1000000) + 273150000;
299d91d9c7cSjcs 		sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].flags =
300d91d9c7cSjcs 		    (tmp > 127 || tmp < -127) ? SENSOR_FINVALID : 0;
3011c450453Sjordan  	}
3021c450453Sjordan 
3031c450453Sjordan 	/* Read fan RPM */
3041c450453Sjordan 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
3051c450453Sjordan 	acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
306995549b7Smiod 	if (hi == 0xff && lo == 0xff) {
307995549b7Smiod  		sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = SENSOR_FINVALID;
308995549b7Smiod 	} else {
309306c5139Smlarkin 		sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
310995549b7Smiod  		sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = 0;
311995549b7Smiod 	}
3121c450453Sjordan }
3131c450453Sjordan 
3141c450453Sjordan void
thinkpad_attach(struct device * parent,struct device * self,void * aux)315866be0c9Sjcs thinkpad_attach(struct device *parent, struct device *self, void *aux)
316866be0c9Sjcs {
317866be0c9Sjcs 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
318866be0c9Sjcs 	struct acpi_attach_args	*aa = aux;
319a81f321cSkettenis 	struct aml_value arg;
320a81f321cSkettenis 	uint64_t ret;
321866be0c9Sjcs 
322866be0c9Sjcs 	sc->sc_acpi = (struct acpi_softc *)parent;
3233b455a03Smarco 	sc->sc_devnode = aa->aaa_node;
324866be0c9Sjcs 
3250d574372Sjcs 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
3260d574372Sjcs 	    &sc->sc_hkey_version))
3270d574372Sjcs 		sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
3280d574372Sjcs 
3290ccc52bbSkettenis 	printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8,
3300ccc52bbSkettenis 	    sc->sc_hkey_version & 0xff);
3310ccc52bbSkettenis 
332f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
333f07ad920Sderaadt 	/* Defer speaker mute */
334f07ad920Sderaadt 	if (thinkpad_get_volume_mute(sc) == 1)
335f07ad920Sderaadt 		startuphook_establish(thinkpad_attach_deferred, sc);
336f07ad920Sderaadt #endif
337f07ad920Sderaadt 
3380b3884edSfgsch 	/* Set event mask to receive everything */
339866be0c9Sjcs 	thinkpad_enable_events(sc);
3401c450453Sjordan 	thinkpad_sensor_attach(sc);
341866be0c9Sjcs 
342c1535f53Skettenis 	/* Check for ThinkLight or keyboard backlight */
34323b692d1Skettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
34423b692d1Skettenis 	    0, NULL, &sc->sc_thinklight) == 0) {
345c1535f53Skettenis 		sc->sc_thinklight_get = "KLCG";
346c1535f53Skettenis 		sc->sc_thinklight_set = "KLCS";
3470dc71f27Sjcs 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
3480dc71f27Sjcs 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
349c1535f53Skettenis 	} else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
350c1535f53Skettenis 	    0, NULL, &sc->sc_thinklight) == 0) {
351c1535f53Skettenis 		sc->sc_thinklight_get = "MLCG";
352c1535f53Skettenis 		sc->sc_thinklight_set = "MLCS";
3530dc71f27Sjcs 		wskbd_get_backlight = thinkpad_get_kbd_backlight;
3540dc71f27Sjcs 		wskbd_set_backlight = thinkpad_set_kbd_backlight;
35523b692d1Skettenis 	}
35623b692d1Skettenis 
35785897bf6Sjcs 	/* On version 2 and newer, let *drm or acpivout control brightness */
35885897bf6Sjcs 	if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 &&
35985897bf6Sjcs 	    (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
36085897bf6Sjcs 	    0, NULL, &sc->sc_brightness) == 0)) {
3614ea90f4eSkettenis 		ws_get_param = thinkpad_get_param;
3624ea90f4eSkettenis 		ws_set_param = thinkpad_set_param;
3634ea90f4eSkettenis 	}
3644ea90f4eSkettenis 
365a81f321cSkettenis 	memset(&arg, 0, sizeof(arg));
366a81f321cSkettenis 	arg.type = AML_OBJTYPE_INTEGER;
367a81f321cSkettenis 	arg.v_integer = 1;
368a81f321cSkettenis 
369a81f321cSkettenis 	hw_battery_chargemode = 1;
370a81f321cSkettenis 	hw_battery_chargestart = 0;
371a81f321cSkettenis 	hw_battery_chargestop = 100;
372a81f321cSkettenis 
373a81f321cSkettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCTG",
374a81f321cSkettenis 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
375a81f321cSkettenis 		if (ret & THINKPAD_BATTERY_SUPPORT) {
376a81f321cSkettenis 			hw_battery_chargestart = ret & 0xff;
377a81f321cSkettenis 			hw_battery_setchargestart =
378a81f321cSkettenis 				thinkpad_battery_setchargestart;
379a81f321cSkettenis 		}
380a81f321cSkettenis 	}
381a81f321cSkettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSG",
382a81f321cSkettenis 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
383a81f321cSkettenis 		if (ret & THINKPAD_BATTERY_SUPPORT) {
384a81f321cSkettenis 			if ((ret & 0xff) == 0)
385a81f321cSkettenis 				hw_battery_chargestop = 100;
386a81f321cSkettenis 			else
387a81f321cSkettenis 				hw_battery_chargestop = ret & 0xff;
388a81f321cSkettenis 			hw_battery_setchargestop =
389a81f321cSkettenis 				thinkpad_battery_setchargestop;
390a81f321cSkettenis 		}
391a81f321cSkettenis 	}
392a81f321cSkettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSG",
393a81f321cSkettenis 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
394a81f321cSkettenis 		if (ret & THINKPAD_BATTERY_SUPPORT) {
395a81f321cSkettenis 			if (ret & 0x1)
396a81f321cSkettenis 				hw_battery_chargemode = -1;
397a81f321cSkettenis 			hw_battery_setchargemode =
398a81f321cSkettenis 				thinkpad_battery_setchargemode;
399a81f321cSkettenis 		}
400a81f321cSkettenis 	}
401a81f321cSkettenis 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICG",
402a81f321cSkettenis 	    1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
403a81f321cSkettenis 		if (ret & THINKPAD_BATTERY_SUPPORT_BICG) {
404a81f321cSkettenis 			if (ret & 0x1)
405a81f321cSkettenis 				hw_battery_chargemode = 0;
406a81f321cSkettenis 			hw_battery_setchargemode =
407a81f321cSkettenis 				thinkpad_battery_setchargemode;
408a81f321cSkettenis 		}
409a81f321cSkettenis 	}
410a81f321cSkettenis 
4110b3884edSfgsch 	/* Run thinkpad_hotkey on button presses */
4123b455a03Smarco 	aml_register_notify(sc->sc_devnode, aa->aaa_dev,
4131c450453Sjordan 	    thinkpad_hotkey, sc, ACPIDEV_POLL);
414866be0c9Sjcs }
415866be0c9Sjcs 
416866be0c9Sjcs int
thinkpad_enable_events(struct acpithinkpad_softc * sc)417866be0c9Sjcs thinkpad_enable_events(struct acpithinkpad_softc *sc)
418866be0c9Sjcs {
419a95d1b72Sjordan 	struct aml_value arg, args[2];
420866be0c9Sjcs 	int64_t	mask;
4210b3884edSfgsch 	int i;
422866be0c9Sjcs 
4230d574372Sjcs 	/* Get the default event mask */
4240b3884edSfgsch 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
4250b3884edSfgsch 	    0, NULL, &mask)) {
426866be0c9Sjcs 		printf("%s: no MHKA\n", DEVNAME(sc));
4270b3884edSfgsch 		return (1);
428866be0c9Sjcs 	}
429866be0c9Sjcs 
4300d574372Sjcs 	/* Enable events we need to know about */
431171b130bSjcs 	mask |= (THINKPAD_MASK_MIC_MUTE	|
432171b130bSjcs 	    THINKPAD_MASK_BRIGHTNESS_UP |
433171b130bSjcs 	    THINKPAD_MASK_BRIGHTNESS_DOWN |
4340d574372Sjcs 	    THINKPAD_MASK_KBD_BACKLIGHT);
4350d574372Sjcs 
4360d574372Sjcs 	DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
4370d574372Sjcs 
4380b3884edSfgsch 	/* Update hotkey mask */
43981ac86e5Smarco 	bzero(args, sizeof(args));
440866be0c9Sjcs 	args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
441866be0c9Sjcs 	for (i = 0; i < 32; i++) {
442866be0c9Sjcs 		args[0].v_integer = i + 1;
443866be0c9Sjcs 		args[1].v_integer = (((1 << i) & mask) != 0);
444866be0c9Sjcs 
4450b3884edSfgsch 		if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
4460b3884edSfgsch 		    2, args, NULL)) {
447866be0c9Sjcs 			printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
4480b3884edSfgsch 			return (1);
449866be0c9Sjcs 		}
450866be0c9Sjcs 	}
451866be0c9Sjcs 
4520b3884edSfgsch 	/* Enable hotkeys */
45381ac86e5Smarco 	bzero(&arg, sizeof(arg));
454866be0c9Sjcs 	arg.type = AML_OBJTYPE_INTEGER;
455866be0c9Sjcs 	arg.v_integer = 1;
4560b3884edSfgsch 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
4570b3884edSfgsch 	    1, &arg, NULL)) {
458866be0c9Sjcs 		printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
4590b3884edSfgsch 		return (1);
460866be0c9Sjcs 	}
461866be0c9Sjcs 
4620b3884edSfgsch 	return (0);
463866be0c9Sjcs }
464866be0c9Sjcs 
465866be0c9Sjcs int
thinkpad_hotkey(struct aml_node * node,int notify_type,void * arg)466866be0c9Sjcs thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
467866be0c9Sjcs {
468866be0c9Sjcs 	struct acpithinkpad_softc *sc = arg;
4690b3884edSfgsch 	int64_t	event;
47081ac86e5Smarco 
4711c450453Sjordan 	if (notify_type == 0x00) {
4720b3884edSfgsch 		/* Poll sensors */
4731c450453Sjordan 		thinkpad_sensor_refresh(sc);
4740b3884edSfgsch 		return (0);
4751c450453Sjordan 	}
4760b3884edSfgsch 
47781ac86e5Smarco 	if (notify_type != 0x80)
4780b3884edSfgsch 		return (1);
479866be0c9Sjcs 
480866be0c9Sjcs 	for (;;) {
4810b3884edSfgsch 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
4820b3884edSfgsch 		    0, NULL, &event))
4830b3884edSfgsch 			break;
4840d574372Sjcs 
4850d574372Sjcs 		DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
4860b3884edSfgsch 		if (event == 0)
4870b3884edSfgsch 			break;
488866be0c9Sjcs 
489866be0c9Sjcs 		switch (event) {
490e7f08eacSjsg 		case THINKPAD_BUTTON_BRIGHTNESS_UP:
491e7f08eacSjsg 			thinkpad_brightness_up(sc);
492e7f08eacSjsg 			break;
493e7f08eacSjsg 		case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
494e7f08eacSjsg 			thinkpad_brightness_down(sc);
495e7f08eacSjsg 			break;
496866be0c9Sjcs 		case THINKPAD_BUTTON_WIRELESS:
497866be0c9Sjcs 			thinkpad_toggle_bluetooth(sc);
498866be0c9Sjcs 			break;
499866be0c9Sjcs 		case THINKPAD_BUTTON_SUSPEND:
50030cdf572Sderaadt #ifndef SMALL_KERNEL
5017a94bfdfSderaadt 			if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
50290680e5dSjordan 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
503ad814436Sderaadt 				    sc->sc_acpi, SLEEP_SUSPEND);
50430cdf572Sderaadt #endif
505866be0c9Sjcs 			break;
5060b3884edSfgsch 		case THINKPAD_BUTTON_VOLUME_MUTE:
5070b3884edSfgsch 			thinkpad_volume_mute(sc);
5080b3884edSfgsch 			break;
5093e7c1b35Sjcs 		case THINKPAD_BUTTON_VOLUME_DOWN:
5103e7c1b35Sjcs 			thinkpad_volume_down(sc);
5113e7c1b35Sjcs 			break;
5123e7c1b35Sjcs 		case THINKPAD_BUTTON_VOLUME_UP:
5133e7c1b35Sjcs 			thinkpad_volume_up(sc);
5143e7c1b35Sjcs 			break;
51592a3e4ccSderaadt 		case THINKPAD_BUTTON_MICROPHONE_MUTE:
51692a3e4ccSderaadt #if NAUDIO > 0 && NWSKBD > 0
517012023bfSmpi 			wskbd_set_mixervolume(0, 0);
51892a3e4ccSderaadt #endif
51992a3e4ccSderaadt 			break;
5202dd2a277Sderaadt 		case THINKPAD_BUTTON_HIBERNATE:
5219502e089Sdcoppa #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
5228d4bc9d5Sdcoppa 			if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
5232dd2a277Sderaadt 				acpi_addtask(sc->sc_acpi, acpi_sleep_task,
524ad814436Sderaadt 				    sc->sc_acpi, SLEEP_HIBERNATE);
5252dd2a277Sderaadt #endif
5262dd2a277Sderaadt 			break;
52723b692d1Skettenis 		case THINKPAD_BUTTON_THINKLIGHT:
52823b692d1Skettenis 			thinkpad_get_thinklight(sc);
52923b692d1Skettenis 			break;
5308107bf4dSjcs 		case THINKPAD_ADAPTIVE_NEXT:
5318107bf4dSjcs 		case THINKPAD_ADAPTIVE_QUICK:
5328107bf4dSjcs 			thinkpad_adaptive_change(sc);
5338107bf4dSjcs 			break;
5344ea90f4eSkettenis 		case THINKPAD_BACKLIGHT_CHANGED:
5354ea90f4eSkettenis 			thinkpad_get_brightness(sc);
5364ea90f4eSkettenis 			break;
537306c5139Smlarkin 		case THINKPAD_PORT_REPL_DOCKED:
538306c5139Smlarkin 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
539306c5139Smlarkin 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
540306c5139Smlarkin 			    SENSOR_S_OK;
541306c5139Smlarkin 			break;
542306c5139Smlarkin 		case THINKPAD_PORT_REPL_UNDOCKED:
543306c5139Smlarkin 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
544306c5139Smlarkin 			sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
545306c5139Smlarkin 			    SENSOR_S_OK;
546306c5139Smlarkin 			break;
5470b3884edSfgsch 		default:
548b96dcde9Sjcs 			/* unknown or boring event */
549b96dcde9Sjcs 			DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
550b96dcde9Sjcs 			    event));
551b96dcde9Sjcs 			break;
552866be0c9Sjcs 		}
553866be0c9Sjcs 	}
554866be0c9Sjcs 
555b96dcde9Sjcs 	return (0);
556866be0c9Sjcs }
557866be0c9Sjcs 
558866be0c9Sjcs int
thinkpad_toggle_bluetooth(struct acpithinkpad_softc * sc)559866be0c9Sjcs thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
560866be0c9Sjcs {
561a95d1b72Sjordan 	struct aml_value arg;
562a95d1b72Sjordan 	int64_t	bluetooth;
563866be0c9Sjcs 
5640b3884edSfgsch 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
5650b3884edSfgsch 	    0, NULL, &bluetooth))
5660b3884edSfgsch 		return (1);
567866be0c9Sjcs 
568866be0c9Sjcs 	if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
5690b3884edSfgsch 		return (1);
570866be0c9Sjcs 
57181ac86e5Smarco 	bzero(&arg, sizeof(arg));
572866be0c9Sjcs 	arg.type = AML_OBJTYPE_INTEGER;
5730b3884edSfgsch 	arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
5740b3884edSfgsch 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
5750b3884edSfgsch 	    1, &arg, NULL)) {
576866be0c9Sjcs 		printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
5770b3884edSfgsch 		return (1);
578866be0c9Sjcs 	}
579866be0c9Sjcs 
5800b3884edSfgsch 	return (0);
581866be0c9Sjcs }
582866be0c9Sjcs 
583866be0c9Sjcs int
thinkpad_toggle_wan(struct acpithinkpad_softc * sc)584866be0c9Sjcs thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
585866be0c9Sjcs {
586a95d1b72Sjordan 	struct aml_value arg;
587a95d1b72Sjordan 	int64_t wan;
588866be0c9Sjcs 
5890b3884edSfgsch 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
5900b3884edSfgsch 	    0, NULL, &wan))
5910b3884edSfgsch 		return (1);
592866be0c9Sjcs 
59381ac86e5Smarco 	if (!(wan & THINKPAD_WAN_PRESENT))
5940b3884edSfgsch 		return (1);
59581ac86e5Smarco 
59681ac86e5Smarco 	bzero(&arg, sizeof(arg));
597866be0c9Sjcs 	arg.type = AML_OBJTYPE_INTEGER;
5980b3884edSfgsch 	arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
5990b3884edSfgsch 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
6000b3884edSfgsch 	    1, &arg, NULL)) {
601866be0c9Sjcs 		printf("%s: couldn't toggle wan\n", DEVNAME(sc));
6020b3884edSfgsch 		return (1);
603866be0c9Sjcs 	}
604866be0c9Sjcs 
6050b3884edSfgsch 	return (0);
606866be0c9Sjcs }
607866be0c9Sjcs 
608866be0c9Sjcs int
thinkpad_cmos(struct acpithinkpad_softc * sc,uint8_t cmd)609866be0c9Sjcs thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
610866be0c9Sjcs {
611866be0c9Sjcs 	struct aml_value arg;
612866be0c9Sjcs 
61381ac86e5Smarco 	bzero(&arg, sizeof(arg));
614866be0c9Sjcs 	arg.type = AML_OBJTYPE_INTEGER;
615866be0c9Sjcs 	arg.v_integer = cmd;
61690c6566bSderaadt 	aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
617866be0c9Sjcs 	return (0);
618866be0c9Sjcs }
619866be0c9Sjcs 
620866be0c9Sjcs int
thinkpad_volume_down(struct acpithinkpad_softc * sc)6213e7c1b35Sjcs thinkpad_volume_down(struct acpithinkpad_softc *sc)
6223e7c1b35Sjcs {
62381ac86e5Smarco 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
6243e7c1b35Sjcs }
6253e7c1b35Sjcs 
6263e7c1b35Sjcs int
thinkpad_volume_up(struct acpithinkpad_softc * sc)6273e7c1b35Sjcs thinkpad_volume_up(struct acpithinkpad_softc *sc)
6283e7c1b35Sjcs {
62981ac86e5Smarco 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
6303e7c1b35Sjcs }
6313e7c1b35Sjcs 
6323e7c1b35Sjcs int
thinkpad_volume_mute(struct acpithinkpad_softc * sc)6333e7c1b35Sjcs thinkpad_volume_mute(struct acpithinkpad_softc *sc)
6343e7c1b35Sjcs {
63581ac86e5Smarco 	return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
6363e7c1b35Sjcs }
6373e7c1b35Sjcs 
6383e7c1b35Sjcs int
thinkpad_brightness_up(struct acpithinkpad_softc * sc)639866be0c9Sjcs thinkpad_brightness_up(struct acpithinkpad_softc *sc)
640866be0c9Sjcs {
6410d574372Sjcs 	int b;
6420d574372Sjcs 
643171b130bSjcs 	if (thinkpad_get_brightness(sc) == 0) {
6440d574372Sjcs 		b = sc->sc_brightness & 0xff;
6450d574372Sjcs 		if (b < ((sc->sc_brightness >> 8) & 0xff)) {
6460d574372Sjcs 			sc->sc_brightness = b + 1;
6470d574372Sjcs 			thinkpad_set_brightness(sc, 0);
6480d574372Sjcs 		}
6490d574372Sjcs 
6500d574372Sjcs 		return (0);
6510d574372Sjcs 	} else
65281ac86e5Smarco 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
653866be0c9Sjcs }
654866be0c9Sjcs 
655866be0c9Sjcs int
thinkpad_brightness_down(struct acpithinkpad_softc * sc)656866be0c9Sjcs thinkpad_brightness_down(struct acpithinkpad_softc *sc)
657866be0c9Sjcs {
6580d574372Sjcs 	int b;
6590d574372Sjcs 
660171b130bSjcs 	if (thinkpad_get_brightness(sc) == 0) {
6610d574372Sjcs 		b = sc->sc_brightness & 0xff;
6620d574372Sjcs 		if (b > 0) {
6630d574372Sjcs 			sc->sc_brightness = b - 1;
6640d574372Sjcs 			thinkpad_set_brightness(sc, 0);
6650d574372Sjcs 		}
6660d574372Sjcs 
6670d574372Sjcs 		return (0);
6680d574372Sjcs 	} else
66981ac86e5Smarco 		return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
670866be0c9Sjcs }
6718107bf4dSjcs 
6728107bf4dSjcs int
thinkpad_adaptive_change(struct acpithinkpad_softc * sc)6738107bf4dSjcs thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
6748107bf4dSjcs {
6758107bf4dSjcs 	struct aml_value arg;
6768107bf4dSjcs 	int64_t	mode;
6778107bf4dSjcs 
6788107bf4dSjcs 	if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
6798107bf4dSjcs 	    0, NULL, &mode)) {
6808107bf4dSjcs 		printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
6818107bf4dSjcs 		return (1);
6828107bf4dSjcs 	}
6838107bf4dSjcs 
6848107bf4dSjcs 	bzero(&arg, sizeof(arg));
6858107bf4dSjcs 	arg.type = AML_OBJTYPE_INTEGER;
6868107bf4dSjcs 
6878107bf4dSjcs 	if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
6888107bf4dSjcs 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
6898107bf4dSjcs 	else
6908107bf4dSjcs 		arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
6918107bf4dSjcs 
6928107bf4dSjcs 	if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
6938107bf4dSjcs 	    1, &arg, NULL)) {
6948107bf4dSjcs 		printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
6958107bf4dSjcs 		return (1);
6968107bf4dSjcs 	}
6978107bf4dSjcs 
6988107bf4dSjcs 	return (0);
6998107bf4dSjcs }
7008107bf4dSjcs 
7018107bf4dSjcs int
thinkpad_activate(struct device * self,int act)7028107bf4dSjcs thinkpad_activate(struct device *self, int act)
7038107bf4dSjcs {
7048107bf4dSjcs 
7058107bf4dSjcs 	struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
7068107bf4dSjcs 	int64_t res;
707f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
708f07ad920Sderaadt 	int mute;
709f07ad920Sderaadt #endif
7108107bf4dSjcs 
7118107bf4dSjcs 	switch (act) {
7128107bf4dSjcs 	case DVACT_WAKEUP:
7138107bf4dSjcs 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
7148107bf4dSjcs 		    0, NULL, &res) == 0)
7158107bf4dSjcs 			thinkpad_adaptive_change(sc);
716f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
717f07ad920Sderaadt 		mute = thinkpad_get_volume_mute(sc);
718f07ad920Sderaadt 		if (mute != -1)
719f07ad920Sderaadt 			wskbd_set_mixermute(mute, 1);
720f07ad920Sderaadt #endif
7218107bf4dSjcs 		break;
7228107bf4dSjcs 	}
7238107bf4dSjcs 	return (0);
7248107bf4dSjcs }
72523b692d1Skettenis 
72623b692d1Skettenis void
thinkpad_get_thinklight(struct acpithinkpad_softc * sc)72723b692d1Skettenis thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
72823b692d1Skettenis {
729a77df94aSkettenis 	if (sc->sc_thinklight_get)
730c1535f53Skettenis 		aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
731c1535f53Skettenis 		    sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
73223b692d1Skettenis }
73323b692d1Skettenis 
73423b692d1Skettenis void
thinkpad_set_thinklight(void * arg0,int arg1)73523b692d1Skettenis thinkpad_set_thinklight(void *arg0, int arg1)
73623b692d1Skettenis {
73723b692d1Skettenis 	struct acpithinkpad_softc *sc = arg0;
73823b692d1Skettenis 	struct aml_value arg;
73923b692d1Skettenis 
74023b692d1Skettenis 	memset(&arg, 0, sizeof(arg));
74123b692d1Skettenis 	arg.type = AML_OBJTYPE_INTEGER;
742c1535f53Skettenis 	arg.v_integer = sc->sc_thinklight & 0x0f;
743c1535f53Skettenis 	aml_evalname(sc->sc_acpi, sc->sc_devnode,
744c1535f53Skettenis 	    sc->sc_thinklight_set, 1, &arg, NULL);
74523b692d1Skettenis }
74623b692d1Skettenis 
74723b692d1Skettenis int
thinkpad_get_kbd_backlight(struct wskbd_backlight * kbl)7480dc71f27Sjcs thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
74923b692d1Skettenis {
75023b692d1Skettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
75123b692d1Skettenis 
75223b692d1Skettenis 	KASSERT(sc != NULL);
75323b692d1Skettenis 
75423b692d1Skettenis 	kbl->min = 0;
755c1535f53Skettenis 	kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
756c1535f53Skettenis 	kbl->curval = sc->sc_thinklight & 0x0f;
757ae785f32Sjsg 
758ae785f32Sjsg 	if (kbl->max == 0)
759ae785f32Sjsg 		return (ENOTTY);
760ae785f32Sjsg 
76123b692d1Skettenis 	return 0;
76223b692d1Skettenis }
76323b692d1Skettenis 
76423b692d1Skettenis int
thinkpad_set_kbd_backlight(struct wskbd_backlight * kbl)7650dc71f27Sjcs thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
76623b692d1Skettenis {
76723b692d1Skettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
768aca16f73Sjsg 	int maxval;
76923b692d1Skettenis 
77023b692d1Skettenis 	KASSERT(sc != NULL);
77123b692d1Skettenis 
772aca16f73Sjsg 	maxval = (sc->sc_thinklight >> 8) & 0x0f;
773aca16f73Sjsg 
774ae785f32Sjsg 	if (maxval == 0)
775ae785f32Sjsg 		return (ENOTTY);
776ae785f32Sjsg 
77723b692d1Skettenis 	if (kbl->curval > maxval)
77823b692d1Skettenis 		return EINVAL;
77923b692d1Skettenis 
78023b692d1Skettenis 	sc->sc_thinklight &= ~0xff;
78123b692d1Skettenis 	sc->sc_thinklight |= kbl->curval;
78223b692d1Skettenis 	acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
78323b692d1Skettenis 	acpi_wakeup(sc->sc_acpi);
78423b692d1Skettenis 	return 0;
78523b692d1Skettenis }
7864ea90f4eSkettenis 
787171b130bSjcs int
thinkpad_get_brightness(struct acpithinkpad_softc * sc)7884ea90f4eSkettenis thinkpad_get_brightness(struct acpithinkpad_softc *sc)
7894ea90f4eSkettenis {
790171b130bSjcs 	int ret;
791171b130bSjcs 
792171b130bSjcs 	ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
793171b130bSjcs 	    &sc->sc_brightness);
7940d574372Sjcs 
7950d574372Sjcs 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
796171b130bSjcs 
797171b130bSjcs 	return ret;
7984ea90f4eSkettenis }
7994ea90f4eSkettenis 
800171b130bSjcs int
thinkpad_set_brightness(void * arg0,int arg1)8014ea90f4eSkettenis thinkpad_set_brightness(void *arg0, int arg1)
8024ea90f4eSkettenis {
8034ea90f4eSkettenis 	struct acpithinkpad_softc *sc = arg0;
8044ea90f4eSkettenis 	struct aml_value arg;
805171b130bSjcs 	int ret;
8064ea90f4eSkettenis 
8070d574372Sjcs 	DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
8080d574372Sjcs 
8094ea90f4eSkettenis 	memset(&arg, 0, sizeof(arg));
8104ea90f4eSkettenis 	arg.type = AML_OBJTYPE_INTEGER;
8114ea90f4eSkettenis 	arg.v_integer = sc->sc_brightness & 0xff;
812171b130bSjcs 	ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
813171b130bSjcs 
814171b130bSjcs 	if (ret)
815171b130bSjcs 		return ret;
8160d574372Sjcs 
8170d574372Sjcs 	thinkpad_get_brightness(sc);
818171b130bSjcs 
819171b130bSjcs 	return 0;
8204ea90f4eSkettenis }
8214ea90f4eSkettenis 
8224ea90f4eSkettenis int
thinkpad_get_param(struct wsdisplay_param * dp)8234ea90f4eSkettenis thinkpad_get_param(struct wsdisplay_param *dp)
8244ea90f4eSkettenis {
8254ea90f4eSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
8264ea90f4eSkettenis 
8274ea90f4eSkettenis 	if (sc == NULL)
8284ea90f4eSkettenis 		return -1;
8294ea90f4eSkettenis 
8304ea90f4eSkettenis 	switch (dp->param) {
8314ea90f4eSkettenis 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
8324ea90f4eSkettenis 		dp->min = 0;
8334ea90f4eSkettenis 		dp->max = (sc->sc_brightness >> 8) & 0xff;
8344ea90f4eSkettenis 		dp->curval = sc->sc_brightness & 0xff;
8354ea90f4eSkettenis 		return 0;
8364ea90f4eSkettenis 	default:
8374ea90f4eSkettenis 		return -1;
8384ea90f4eSkettenis 	}
8394ea90f4eSkettenis }
8404ea90f4eSkettenis 
8414ea90f4eSkettenis int
thinkpad_set_param(struct wsdisplay_param * dp)8424ea90f4eSkettenis thinkpad_set_param(struct wsdisplay_param *dp)
8434ea90f4eSkettenis {
8444ea90f4eSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
845aca16f73Sjsg 	int maxval;
8464ea90f4eSkettenis 
8474ea90f4eSkettenis 	if (sc == NULL)
8484ea90f4eSkettenis 		return -1;
8494ea90f4eSkettenis 
850aca16f73Sjsg 	maxval = (sc->sc_brightness >> 8) & 0xff;
851aca16f73Sjsg 
8524ea90f4eSkettenis 	switch (dp->param) {
8534ea90f4eSkettenis 	case WSDISPLAYIO_PARAM_BRIGHTNESS:
8544ea90f4eSkettenis 		if (dp->curval < 0)
8554ea90f4eSkettenis 			dp->curval = 0;
8564ea90f4eSkettenis 		if (dp->curval > maxval)
8574ea90f4eSkettenis 			dp->curval = maxval;
8584ea90f4eSkettenis 		sc->sc_brightness &= ~0xff;
8594ea90f4eSkettenis 		sc->sc_brightness |= dp->curval;
860171b130bSjcs 		acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
861171b130bSjcs 		    0);
8624ea90f4eSkettenis 		acpi_wakeup(sc->sc_acpi);
8634ea90f4eSkettenis 		return 0;
8644ea90f4eSkettenis 	default:
8654ea90f4eSkettenis 		return -1;
8664ea90f4eSkettenis 	}
8674ea90f4eSkettenis }
868f07ad920Sderaadt 
869d91d9c7cSjcs int
thinkpad_get_temp(struct acpithinkpad_softc * sc,int idx,int64_t * temp)870d91d9c7cSjcs thinkpad_get_temp(struct acpithinkpad_softc *sc, int idx, int64_t *temp)
871d91d9c7cSjcs {
872d91d9c7cSjcs 	char sname[5];
873d91d9c7cSjcs 
874d91d9c7cSjcs 	snprintf(sname, sizeof(sname), "TMP%d", idx);
875d91d9c7cSjcs 
876d91d9c7cSjcs 	if (aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, sname, 0, 0,
877d91d9c7cSjcs 	    temp) != 0)
878d91d9c7cSjcs 		return (1);
879d91d9c7cSjcs 
880d91d9c7cSjcs 	return (0);
881d91d9c7cSjcs }
882d91d9c7cSjcs 
883f07ad920Sderaadt #if NAUDIO > 0 && NWSKBD > 0
884f07ad920Sderaadt void
thinkpad_attach_deferred(void * v __unused)885f07ad920Sderaadt thinkpad_attach_deferred(void *v __unused)
886f07ad920Sderaadt {
887f07ad920Sderaadt 	wskbd_set_mixermute(1, 1);
888f07ad920Sderaadt }
889f07ad920Sderaadt 
890f07ad920Sderaadt int
thinkpad_get_volume_mute(struct acpithinkpad_softc * sc)891f07ad920Sderaadt thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
892f07ad920Sderaadt {
893d2eaebe9Skettenis 	uint8_t vol = 0;
894f07ad920Sderaadt 
895f07ad920Sderaadt 	if (sc->sc_acpi->sc_ec == NULL)
896f07ad920Sderaadt 		return (-1);
897f07ad920Sderaadt 
898f07ad920Sderaadt 	acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
899f07ad920Sderaadt 	return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
900f07ad920Sderaadt 	    THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
901f07ad920Sderaadt }
902f07ad920Sderaadt #endif
903a81f321cSkettenis 
904a81f321cSkettenis int
thinkpad_battery_inhibit_charge(int state)905a81f321cSkettenis thinkpad_battery_inhibit_charge(int state)
906a81f321cSkettenis {
907a81f321cSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
908a81f321cSkettenis 	struct aml_value arg;
909*3e37c391Sclaudio 	int battery, count;
910a81f321cSkettenis 	uint64_t ret;
911a81f321cSkettenis 
912*3e37c391Sclaudio 	count = acpi_batcount(sc->sc_acpi);
913*3e37c391Sclaudio 	for (battery = 1; battery <= count; battery++) {
914a81f321cSkettenis 		memset(&arg, 0, sizeof(arg));
915a81f321cSkettenis 		arg.type = AML_OBJTYPE_INTEGER;
916a81f321cSkettenis 		arg.v_integer = (0xffff << 8) | (battery << 4) | state;
917a81f321cSkettenis 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICS",
918a81f321cSkettenis 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
919a81f321cSkettenis 			return EIO;
920*3e37c391Sclaudio 	}
921a81f321cSkettenis 	return 0;
922a81f321cSkettenis }
923a81f321cSkettenis 
924a81f321cSkettenis int
thinkpad_battery_force_discharge(int state)925a81f321cSkettenis thinkpad_battery_force_discharge(int state)
926a81f321cSkettenis {
927a81f321cSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
928a81f321cSkettenis 	struct aml_value arg;
929*3e37c391Sclaudio 	int battery, count;
930a81f321cSkettenis 	uint64_t ret;
931a81f321cSkettenis 
932*3e37c391Sclaudio 	count = acpi_batcount(sc->sc_acpi);
933*3e37c391Sclaudio 	for (battery = 1; battery <= count; battery++) {
934a81f321cSkettenis 		memset(&arg, 0, sizeof(arg));
935a81f321cSkettenis 		arg.type = AML_OBJTYPE_INTEGER;
936a81f321cSkettenis 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | state;
937a81f321cSkettenis 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSS",
938a81f321cSkettenis 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
939a81f321cSkettenis 			return EIO;
940*3e37c391Sclaudio 	}
941a81f321cSkettenis 	return 0;
942a81f321cSkettenis }
943a81f321cSkettenis 
944a81f321cSkettenis int
thinkpad_battery_setchargemode(int mode)945a81f321cSkettenis thinkpad_battery_setchargemode(int mode)
946a81f321cSkettenis {
947a81f321cSkettenis 	int error;
948a81f321cSkettenis 
949a81f321cSkettenis 	switch (mode) {
950a81f321cSkettenis 	case -1:
951a81f321cSkettenis 		error = thinkpad_battery_inhibit_charge(1);
952a81f321cSkettenis 		if (error)
953a81f321cSkettenis 			return error;
954a81f321cSkettenis 		error = thinkpad_battery_force_discharge(1);
955a81f321cSkettenis 		if (error)
956a81f321cSkettenis 			return error;
957a81f321cSkettenis 		break;
958a81f321cSkettenis 	case 0:
959a81f321cSkettenis 		error = thinkpad_battery_force_discharge(0);
960a81f321cSkettenis 		if (error)
961a81f321cSkettenis 			return error;
962a81f321cSkettenis 		error = thinkpad_battery_inhibit_charge(1);
963a81f321cSkettenis 		if (error)
964a81f321cSkettenis 			return error;
965a81f321cSkettenis 		break;
966a81f321cSkettenis 	case 1:
967a81f321cSkettenis 		error = thinkpad_battery_force_discharge(0);
968a81f321cSkettenis 		if (error)
969a81f321cSkettenis 			return error;
970a81f321cSkettenis 		error = thinkpad_battery_inhibit_charge(0);
971a81f321cSkettenis 		if (error)
972a81f321cSkettenis 			return error;
973a81f321cSkettenis 		break;
974a81f321cSkettenis 	default:
975a81f321cSkettenis 		return EOPNOTSUPP;
976a81f321cSkettenis 	}
977a81f321cSkettenis 
978a81f321cSkettenis 	hw_battery_chargemode = mode;
979a81f321cSkettenis 	return 0;
980a81f321cSkettenis }
981a81f321cSkettenis 
982a81f321cSkettenis int
thinkpad_battery_setchargestart(int start)983a81f321cSkettenis thinkpad_battery_setchargestart(int start)
984a81f321cSkettenis {
985a81f321cSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
986a81f321cSkettenis 	struct aml_value arg;
987*3e37c391Sclaudio 	int battery, count;
988a81f321cSkettenis 	uint64_t ret;
989a81f321cSkettenis 
990a81f321cSkettenis 	if (start >= hw_battery_chargestop)
991a81f321cSkettenis 		return EINVAL;
992a81f321cSkettenis 
993*3e37c391Sclaudio 	count = acpi_batcount(sc->sc_acpi);
994*3e37c391Sclaudio 	for (battery = 1; battery <= count; battery++) {
995a81f321cSkettenis 		memset(&arg, 0, sizeof(arg));
996a81f321cSkettenis 		arg.type = AML_OBJTYPE_INTEGER;
997a81f321cSkettenis 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | start;
998a81f321cSkettenis 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCCS",
999a81f321cSkettenis 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1000a81f321cSkettenis 			return EIO;
1001*3e37c391Sclaudio 	}
1002a81f321cSkettenis 	hw_battery_chargestart = start;
1003a81f321cSkettenis 	return 0;
1004a81f321cSkettenis }
1005a81f321cSkettenis 
1006a81f321cSkettenis int
thinkpad_battery_setchargestop(int stop)1007a81f321cSkettenis thinkpad_battery_setchargestop(int stop)
1008a81f321cSkettenis {
1009a81f321cSkettenis 	struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
1010a81f321cSkettenis 	struct aml_value arg;
1011*3e37c391Sclaudio 	int battery, count;
1012a81f321cSkettenis 	uint64_t ret;
1013a81f321cSkettenis 
1014a81f321cSkettenis 	if (stop <= hw_battery_chargestart)
1015a81f321cSkettenis 		return EINVAL;
1016a81f321cSkettenis 
1017a81f321cSkettenis 	if (stop == 100)
1018a81f321cSkettenis 		stop = 0;
1019a81f321cSkettenis 
1020*3e37c391Sclaudio 	count = acpi_batcount(sc->sc_acpi);
1021*3e37c391Sclaudio 	for (battery = 1; battery <= count; battery++) {
1022a81f321cSkettenis 		memset(&arg, 0, sizeof(arg));
1023a81f321cSkettenis 		arg.type = AML_OBJTYPE_INTEGER;
1024a81f321cSkettenis 		arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | stop;
1025a81f321cSkettenis 		if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSS",
1026a81f321cSkettenis 		    1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1027a81f321cSkettenis 			return EIO;
1028*3e37c391Sclaudio 	}
1029a81f321cSkettenis 	hw_battery_chargestop = (stop == 0) ? 100 : stop;
1030a81f321cSkettenis 	return 0;
1031a81f321cSkettenis }
1032