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