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