1 /* $OpenBSD: acpithinkpad.c,v 1.74 2023/07/07 07:37:59 claudio Exp $ */
2 /*
3 * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/sensors.h>
21
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/acpidev.h>
25 #include <dev/acpi/amltypes.h>
26 #include <dev/acpi/dsdt.h>
27 #include <dev/wscons/wsconsio.h>
28 #include <dev/wscons/wsdisplayvar.h>
29
30 #include <machine/apmvar.h>
31
32 #include "audio.h"
33 #include "wskbd.h"
34
35 /* #define ACPITHINKPAD_DEBUG */
36
37 #ifdef ACPITHINKPAD_DEBUG
38 #define DPRINTF(x) printf x
39 #else
40 #define DPRINTF(x)
41 #endif
42
43 #define THINKPAD_HKEY_VERSION1 0x0100
44 #define THINKPAD_HKEY_VERSION2 0x0200
45
46 #define THINKPAD_CMOS_VOLUME_DOWN 0x00
47 #define THINKPAD_CMOS_VOLUME_UP 0x01
48 #define THINKPAD_CMOS_VOLUME_MUTE 0x02
49 #define THINKPAD_CMOS_BRIGHTNESS_UP 0x04
50 #define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05
51
52 #define THINKPAD_BLUETOOTH_PRESENT 0x01
53 #define THINKPAD_BLUETOOTH_ENABLED 0x02
54
55 /* wan (not wifi) card */
56 #define THINKPAD_WAN_PRESENT 0x01
57 #define THINKPAD_WAN_ENABLED 0x02
58
59 #define THINKPAD_BUTTON_FN_F1 0x1001
60 #define THINKPAD_BUTTON_LOCK_SCREEN 0x1002
61 #define THINKPAD_BUTTON_BATTERY_INFO 0x1003
62 #define THINKPAD_BUTTON_SUSPEND 0x1004
63 #define THINKPAD_BUTTON_WIRELESS 0x1005
64 #define THINKPAD_BUTTON_FN_F6 0x1006
65 #define THINKPAD_BUTTON_EXTERNAL_SCREEN 0x1007
66 #define THINKPAD_BUTTON_POINTER_SWITCH 0x1008
67 #define THINKPAD_BUTTON_EJECT 0x1009
68 #define THINKPAD_BUTTON_FN_F11 0x100b
69 #define THINKPAD_BUTTON_HIBERNATE 0x100c
70 #define THINKPAD_BUTTON_BRIGHTNESS_UP 0x1010
71 #define THINKPAD_BUTTON_BRIGHTNESS_DOWN 0x1011
72 #define THINKPAD_BUTTON_THINKLIGHT 0x1012
73 #define THINKPAD_BUTTON_FN_SPACE 0x1014
74 #define THINKPAD_BUTTON_VOLUME_UP 0x1015
75 #define THINKPAD_BUTTON_VOLUME_DOWN 0x1016
76 #define THINKPAD_BUTTON_VOLUME_MUTE 0x1017
77 #define THINKPAD_BUTTON_THINKVANTAGE 0x1018
78 #define THINKPAD_BUTTON_BLACK 0x101a
79 #define THINKPAD_BUTTON_MICROPHONE_MUTE 0x101b
80 #define THINKPAD_KEYLIGHT_CHANGED 0x101c
81 #define THINKPAD_BUTTON_CONFIG 0x101d
82 #define THINKPAD_BUTTON_FIND 0x101e
83 #define THINKPAD_BUTTON_ALL_ACTIVEPROGS 0x101f
84 #define THINKPAD_BUTTON_ALL_PROGS 0x1020
85
86 #define THINKPAD_ADAPTIVE_NEXT 0x1101
87 #define THINKPAD_ADAPTIVE_QUICK 0x1102
88 #define THINKPAD_ADAPTIVE_SNIP 0x1105
89 #define THINKPAD_ADAPTIVE_VOICE 0x1108
90 #define THINKPAD_ADAPTIVE_GESTURES 0x110a
91 #define THINKPAD_ADAPTIVE_SETTINGS 0x110e
92 #define THINKPAD_ADAPTIVE_TAB 0x110f
93 #define THINKPAD_ADAPTIVE_REFRESH 0x1110
94 #define THINKPAD_ADAPTIVE_BACK 0x1111
95 #define THINKPAD_PORT_REPL_DOCKED 0x4010
96 #define THINKPAD_PORT_REPL_UNDOCKED 0x4011
97 #define THINKPAD_TABLET_DOCKED 0x4012
98 #define THINKPAD_TABLET_UNDOCKED 0x4013
99 #define THINKPAD_LID_OPEN 0x5001
100 #define THINKPAD_LID_CLOSED 0x5002
101 #define THINKPAD_TABLET_SCREEN_NORMAL 0x500a
102 #define THINKPAD_TABLET_SCREEN_ROTATED 0x5009
103 #define THINKPAD_BRIGHTNESS_CHANGED 0x5010
104 #define THINKPAD_TABLET_PEN_INSERTED 0x500b
105 #define THINKPAD_TABLET_PEN_REMOVED 0x500c
106 #define THINKPAD_SWITCH_NUMLOCK 0x6000
107 #define THINKPAD_BUTTON_ROTATION_LOCK 0x6020
108 #define THINKPAD_THERMAL_TABLE_CHANGED 0x6030
109 #define THINKPAD_POWER_CHANGED 0x6040
110 #define THINKPAD_BACKLIGHT_CHANGED 0x6050
111 #define THINKPAD_BUTTON_FN_TOGGLE 0x6060
112 #define THINKPAD_TABLET_SCREEN_CHANGED 0x60c0
113 #define THINKPAD_SWITCH_WIRELESS 0x7000
114
115 #define THINKPAD_SENSOR_FANRPM 0
116 #define THINKPAD_SENSOR_PORTREPL 1
117 #define THINKPAD_SENSOR_TMP0 2
118 #define THINKPAD_NSENSORS 10
119
120 #define THINKPAD_ECOFFSET_VOLUME 0x30
121 #define THINKPAD_ECOFFSET_VOLUME_MUTE_MASK 0x40
122 #define THINKPAD_ECOFFSET_FANLO 0x84
123 #define THINKPAD_ECOFFSET_FANHI 0x85
124
125 #define THINKPAD_ADAPTIVE_MODE_HOME 1
126 #define THINKPAD_ADAPTIVE_MODE_FUNCTION 3
127
128 #define THINKPAD_MASK_MIC_MUTE (1 << 14)
129 #define THINKPAD_MASK_BRIGHTNESS_UP (1 << 15)
130 #define THINKPAD_MASK_BRIGHTNESS_DOWN (1 << 16)
131 #define THINKPAD_MASK_KBD_BACKLIGHT (1 << 17)
132
133 #define THINKPAD_BATTERY_ERROR 0x80000000
134 #define THINKPAD_BATTERY_SUPPORT 0x00000100
135 #define THINKPAD_BATTERY_SUPPORT_BICG 0x00000020
136 #define THINKPAD_BATTERY_SHIFT 8
137
138 struct acpithinkpad_softc {
139 struct device sc_dev;
140
141 struct acpiec_softc *sc_ec;
142 struct acpi_softc *sc_acpi;
143 struct aml_node *sc_devnode;
144
145 struct ksensor sc_sens[THINKPAD_NSENSORS];
146 struct ksensordev sc_sensdev;
147 int sc_ntempsens;
148
149 uint64_t sc_hkey_version;
150
151 uint64_t sc_thinklight;
152 const char *sc_thinklight_get;
153 const char *sc_thinklight_set;
154
155 uint64_t sc_brightness;
156 };
157
158 extern void acpiec_read(struct acpiec_softc *, uint8_t, int, uint8_t *);
159
160 int thinkpad_match(struct device *, void *, void *);
161 void thinkpad_attach(struct device *, struct device *, void *);
162 int thinkpad_hotkey(struct aml_node *, int, void *);
163 int thinkpad_enable_events(struct acpithinkpad_softc *);
164 int thinkpad_toggle_bluetooth(struct acpithinkpad_softc *);
165 int thinkpad_toggle_wan(struct acpithinkpad_softc *);
166 int thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t);
167 int thinkpad_volume_down(struct acpithinkpad_softc *);
168 int thinkpad_volume_up(struct acpithinkpad_softc *);
169 int thinkpad_volume_mute(struct acpithinkpad_softc *);
170 int thinkpad_brightness_up(struct acpithinkpad_softc *);
171 int thinkpad_brightness_down(struct acpithinkpad_softc *);
172 int thinkpad_adaptive_change(struct acpithinkpad_softc *);
173 int thinkpad_activate(struct device *, int);
174
175 /* wscons hook functions */
176 void thinkpad_get_thinklight(struct acpithinkpad_softc *);
177 void thinkpad_set_thinklight(void *, int);
178 int thinkpad_get_kbd_backlight(struct wskbd_backlight *);
179 int thinkpad_set_kbd_backlight(struct wskbd_backlight *);
180 extern int (*wskbd_get_backlight)(struct wskbd_backlight *);
181 extern int (*wskbd_set_backlight)(struct wskbd_backlight *);
182 int thinkpad_get_brightness(struct acpithinkpad_softc *);
183 int thinkpad_set_brightness(void *, int);
184 int thinkpad_get_param(struct wsdisplay_param *);
185 int thinkpad_set_param(struct wsdisplay_param *);
186 int thinkpad_get_temp(struct acpithinkpad_softc *, int, int64_t *);
187
188 void thinkpad_sensor_attach(struct acpithinkpad_softc *sc);
189 void thinkpad_sensor_refresh(void *);
190
191 #if NAUDIO > 0 && NWSKBD > 0
192 void thinkpad_attach_deferred(void *);
193 int thinkpad_get_volume_mute(struct acpithinkpad_softc *);
194 extern int wskbd_set_mixermute(long, long);
195 extern int wskbd_set_mixervolume(long, long);
196 #endif
197
198 int thinkpad_battery_setchargemode(int);
199 int thinkpad_battery_setchargestart(int);
200 int thinkpad_battery_setchargestop(int);
201
202 extern int (*hw_battery_setchargemode)(int);
203 extern int (*hw_battery_setchargestart)(int);
204 extern int (*hw_battery_setchargestop)(int);
205 extern int hw_battery_chargemode;
206 extern int hw_battery_chargestart;
207 extern int hw_battery_chargestop;
208
209 const struct cfattach acpithinkpad_ca = {
210 sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
211 NULL, thinkpad_activate
212 };
213
214 struct cfdriver acpithinkpad_cd = {
215 NULL, "acpithinkpad", DV_DULL
216 };
217
218 const char *acpithinkpad_hids[] = {
219 "IBM0068",
220 "LEN0068",
221 "LEN0268",
222 NULL
223 };
224
225 int
thinkpad_match(struct device * parent,void * match,void * aux)226 thinkpad_match(struct device *parent, void *match, void *aux)
227 {
228 struct acpi_attach_args *aa = aux;
229 struct cfdata *cf = match;
230 int64_t res;
231
232 if (!acpi_matchhids(aa, acpithinkpad_hids, cf->cf_driver->cd_name))
233 return (0);
234
235 if (aml_evalinteger((struct acpi_softc *)parent, aa->aaa_node,
236 "MHKV", 0, NULL, &res))
237 return (0);
238
239 if (!(res == THINKPAD_HKEY_VERSION1 || res == THINKPAD_HKEY_VERSION2))
240 return (0);
241
242 return (1);
243 }
244
245 void
thinkpad_sensor_attach(struct acpithinkpad_softc * sc)246 thinkpad_sensor_attach(struct acpithinkpad_softc *sc)
247 {
248 int64_t tmp;
249 int i;
250
251 if (sc->sc_acpi->sc_ec == NULL)
252 return;
253 sc->sc_ec = sc->sc_acpi->sc_ec;
254
255 /* Add temperature probes */
256 strlcpy(sc->sc_sensdev.xname, DEVNAME(sc),
257 sizeof(sc->sc_sensdev.xname));
258 sc->sc_ntempsens = 0;
259 for (i = 0; i < THINKPAD_NSENSORS - THINKPAD_SENSOR_TMP0; i++) {
260 if (thinkpad_get_temp(sc, i, &tmp) != 0)
261 break;
262
263 sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].type = SENSOR_TEMP;
264 sensor_attach(&sc->sc_sensdev,
265 &sc->sc_sens[THINKPAD_SENSOR_TMP0 + i]);
266 sc->sc_ntempsens++;
267 }
268
269 /* Add fan probe */
270 sc->sc_sens[THINKPAD_SENSOR_FANRPM].type = SENSOR_FANRPM;
271 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_FANRPM]);
272
273 /* Add port replicator indicator */
274 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].type = SENSOR_INDICATOR;
275 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status = SENSOR_S_UNKNOWN;
276 strlcpy(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc, "port replicator",
277 sizeof(sc->sc_sens[THINKPAD_SENSOR_PORTREPL].desc));
278 sensor_attach(&sc->sc_sensdev, &sc->sc_sens[THINKPAD_SENSOR_PORTREPL]);
279
280 sensordev_install(&sc->sc_sensdev);
281 }
282
283 void
thinkpad_sensor_refresh(void * arg)284 thinkpad_sensor_refresh(void *arg)
285 {
286 struct acpithinkpad_softc *sc = arg;
287 uint8_t lo, hi, i;
288 int64_t tmp;
289
290 /* Refresh sensor readings */
291 for (i = 0; i < sc->sc_ntempsens; i++) {
292 if (thinkpad_get_temp(sc, i, &tmp) != 0) {
293 sc->sc_sens[i].flags = SENSOR_FINVALID;
294 continue;
295 }
296
297 sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].value =
298 (tmp * 1000000) + 273150000;
299 sc->sc_sens[THINKPAD_SENSOR_TMP0 + i].flags =
300 (tmp > 127 || tmp < -127) ? SENSOR_FINVALID : 0;
301 }
302
303 /* Read fan RPM */
304 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANLO, 1, &lo);
305 acpiec_read(sc->sc_ec, THINKPAD_ECOFFSET_FANHI, 1, &hi);
306 if (hi == 0xff && lo == 0xff) {
307 sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = SENSOR_FINVALID;
308 } else {
309 sc->sc_sens[THINKPAD_SENSOR_FANRPM].value = ((hi << 8L) + lo);
310 sc->sc_sens[THINKPAD_SENSOR_FANRPM].flags = 0;
311 }
312 }
313
314 void
thinkpad_attach(struct device * parent,struct device * self,void * aux)315 thinkpad_attach(struct device *parent, struct device *self, void *aux)
316 {
317 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
318 struct acpi_attach_args *aa = aux;
319 struct aml_value arg;
320 uint64_t ret;
321
322 sc->sc_acpi = (struct acpi_softc *)parent;
323 sc->sc_devnode = aa->aaa_node;
324
325 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKV", 0, NULL,
326 &sc->sc_hkey_version))
327 sc->sc_hkey_version = THINKPAD_HKEY_VERSION1;
328
329 printf(": version %lld.%lld\n", sc->sc_hkey_version >> 8,
330 sc->sc_hkey_version & 0xff);
331
332 #if NAUDIO > 0 && NWSKBD > 0
333 /* Defer speaker mute */
334 if (thinkpad_get_volume_mute(sc) == 1)
335 startuphook_establish(thinkpad_attach_deferred, sc);
336 #endif
337
338 /* Set event mask to receive everything */
339 thinkpad_enable_events(sc);
340 thinkpad_sensor_attach(sc);
341
342 /* Check for ThinkLight or keyboard backlight */
343 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "KLCG",
344 0, NULL, &sc->sc_thinklight) == 0) {
345 sc->sc_thinklight_get = "KLCG";
346 sc->sc_thinklight_set = "KLCS";
347 wskbd_get_backlight = thinkpad_get_kbd_backlight;
348 wskbd_set_backlight = thinkpad_set_kbd_backlight;
349 } else if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MLCG",
350 0, NULL, &sc->sc_thinklight) == 0) {
351 sc->sc_thinklight_get = "MLCG";
352 sc->sc_thinklight_set = "MLCS";
353 wskbd_get_backlight = thinkpad_get_kbd_backlight;
354 wskbd_set_backlight = thinkpad_set_kbd_backlight;
355 }
356
357 /* On version 2 and newer, let *drm or acpivout control brightness */
358 if (sc->sc_hkey_version == THINKPAD_HKEY_VERSION1 &&
359 (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG",
360 0, NULL, &sc->sc_brightness) == 0)) {
361 ws_get_param = thinkpad_get_param;
362 ws_set_param = thinkpad_set_param;
363 }
364
365 memset(&arg, 0, sizeof(arg));
366 arg.type = AML_OBJTYPE_INTEGER;
367 arg.v_integer = 1;
368
369 hw_battery_chargemode = 1;
370 hw_battery_chargestart = 0;
371 hw_battery_chargestop = 100;
372
373 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCTG",
374 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
375 if (ret & THINKPAD_BATTERY_SUPPORT) {
376 hw_battery_chargestart = ret & 0xff;
377 hw_battery_setchargestart =
378 thinkpad_battery_setchargestart;
379 }
380 }
381 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSG",
382 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
383 if (ret & THINKPAD_BATTERY_SUPPORT) {
384 if ((ret & 0xff) == 0)
385 hw_battery_chargestop = 100;
386 else
387 hw_battery_chargestop = ret & 0xff;
388 hw_battery_setchargestop =
389 thinkpad_battery_setchargestop;
390 }
391 }
392 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSG",
393 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
394 if (ret & THINKPAD_BATTERY_SUPPORT) {
395 if (ret & 0x1)
396 hw_battery_chargemode = -1;
397 hw_battery_setchargemode =
398 thinkpad_battery_setchargemode;
399 }
400 }
401 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICG",
402 1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
403 if (ret & THINKPAD_BATTERY_SUPPORT_BICG) {
404 if (ret & 0x1)
405 hw_battery_chargemode = 0;
406 hw_battery_setchargemode =
407 thinkpad_battery_setchargemode;
408 }
409 }
410
411 /* Run thinkpad_hotkey on button presses */
412 aml_register_notify(sc->sc_devnode, aa->aaa_dev,
413 thinkpad_hotkey, sc, ACPIDEV_POLL);
414 }
415
416 int
thinkpad_enable_events(struct acpithinkpad_softc * sc)417 thinkpad_enable_events(struct acpithinkpad_softc *sc)
418 {
419 struct aml_value arg, args[2];
420 int64_t mask;
421 int i;
422
423 /* Get the default event mask */
424 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKA",
425 0, NULL, &mask)) {
426 printf("%s: no MHKA\n", DEVNAME(sc));
427 return (1);
428 }
429
430 /* Enable events we need to know about */
431 mask |= (THINKPAD_MASK_MIC_MUTE |
432 THINKPAD_MASK_BRIGHTNESS_UP |
433 THINKPAD_MASK_BRIGHTNESS_DOWN |
434 THINKPAD_MASK_KBD_BACKLIGHT);
435
436 DPRINTF(("%s: setting event mask to 0x%llx\n", DEVNAME(sc), mask));
437
438 /* Update hotkey mask */
439 bzero(args, sizeof(args));
440 args[0].type = args[1].type = AML_OBJTYPE_INTEGER;
441 for (i = 0; i < 32; i++) {
442 args[0].v_integer = i + 1;
443 args[1].v_integer = (((1 << i) & mask) != 0);
444
445 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKM",
446 2, args, NULL)) {
447 printf("%s: couldn't toggle MHKM\n", DEVNAME(sc));
448 return (1);
449 }
450 }
451
452 /* Enable hotkeys */
453 bzero(&arg, sizeof(arg));
454 arg.type = AML_OBJTYPE_INTEGER;
455 arg.v_integer = 1;
456 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "MHKC",
457 1, &arg, NULL)) {
458 printf("%s: couldn't enable hotkeys\n", DEVNAME(sc));
459 return (1);
460 }
461
462 return (0);
463 }
464
465 int
thinkpad_hotkey(struct aml_node * node,int notify_type,void * arg)466 thinkpad_hotkey(struct aml_node *node, int notify_type, void *arg)
467 {
468 struct acpithinkpad_softc *sc = arg;
469 int64_t event;
470
471 if (notify_type == 0x00) {
472 /* Poll sensors */
473 thinkpad_sensor_refresh(sc);
474 return (0);
475 }
476
477 if (notify_type != 0x80)
478 return (1);
479
480 for (;;) {
481 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "MHKP",
482 0, NULL, &event))
483 break;
484
485 DPRINTF(("%s: event 0x%03llx\n", DEVNAME(sc), event));
486 if (event == 0)
487 break;
488
489 switch (event) {
490 case THINKPAD_BUTTON_BRIGHTNESS_UP:
491 thinkpad_brightness_up(sc);
492 break;
493 case THINKPAD_BUTTON_BRIGHTNESS_DOWN:
494 thinkpad_brightness_down(sc);
495 break;
496 case THINKPAD_BUTTON_WIRELESS:
497 thinkpad_toggle_bluetooth(sc);
498 break;
499 case THINKPAD_BUTTON_SUSPEND:
500 #ifndef SMALL_KERNEL
501 if (acpi_record_event(sc->sc_acpi, APM_USER_SUSPEND_REQ))
502 acpi_addtask(sc->sc_acpi, acpi_sleep_task,
503 sc->sc_acpi, SLEEP_SUSPEND);
504 #endif
505 break;
506 case THINKPAD_BUTTON_VOLUME_MUTE:
507 thinkpad_volume_mute(sc);
508 break;
509 case THINKPAD_BUTTON_VOLUME_DOWN:
510 thinkpad_volume_down(sc);
511 break;
512 case THINKPAD_BUTTON_VOLUME_UP:
513 thinkpad_volume_up(sc);
514 break;
515 case THINKPAD_BUTTON_MICROPHONE_MUTE:
516 #if NAUDIO > 0 && NWSKBD > 0
517 wskbd_set_mixervolume(0, 0);
518 #endif
519 break;
520 case THINKPAD_BUTTON_HIBERNATE:
521 #if defined(HIBERNATE) && !defined(SMALL_KERNEL)
522 if (acpi_record_event(sc->sc_acpi, APM_USER_HIBERNATE_REQ))
523 acpi_addtask(sc->sc_acpi, acpi_sleep_task,
524 sc->sc_acpi, SLEEP_HIBERNATE);
525 #endif
526 break;
527 case THINKPAD_BUTTON_THINKLIGHT:
528 thinkpad_get_thinklight(sc);
529 break;
530 case THINKPAD_ADAPTIVE_NEXT:
531 case THINKPAD_ADAPTIVE_QUICK:
532 thinkpad_adaptive_change(sc);
533 break;
534 case THINKPAD_BACKLIGHT_CHANGED:
535 thinkpad_get_brightness(sc);
536 break;
537 case THINKPAD_PORT_REPL_DOCKED:
538 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 1;
539 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
540 SENSOR_S_OK;
541 break;
542 case THINKPAD_PORT_REPL_UNDOCKED:
543 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].value = 0;
544 sc->sc_sens[THINKPAD_SENSOR_PORTREPL].status =
545 SENSOR_S_OK;
546 break;
547 default:
548 /* unknown or boring event */
549 DPRINTF(("%s: unhandled event 0x%03llx\n", DEVNAME(sc),
550 event));
551 break;
552 }
553 }
554
555 return (0);
556 }
557
558 int
thinkpad_toggle_bluetooth(struct acpithinkpad_softc * sc)559 thinkpad_toggle_bluetooth(struct acpithinkpad_softc *sc)
560 {
561 struct aml_value arg;
562 int64_t bluetooth;
563
564 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GBDC",
565 0, NULL, &bluetooth))
566 return (1);
567
568 if (!(bluetooth & THINKPAD_BLUETOOTH_PRESENT))
569 return (1);
570
571 bzero(&arg, sizeof(arg));
572 arg.type = AML_OBJTYPE_INTEGER;
573 arg.v_integer = bluetooth ^ THINKPAD_BLUETOOTH_ENABLED;
574 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SBDC",
575 1, &arg, NULL)) {
576 printf("%s: couldn't toggle bluetooth\n", DEVNAME(sc));
577 return (1);
578 }
579
580 return (0);
581 }
582
583 int
thinkpad_toggle_wan(struct acpithinkpad_softc * sc)584 thinkpad_toggle_wan(struct acpithinkpad_softc *sc)
585 {
586 struct aml_value arg;
587 int64_t wan;
588
589 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GWAN",
590 0, NULL, &wan))
591 return (1);
592
593 if (!(wan & THINKPAD_WAN_PRESENT))
594 return (1);
595
596 bzero(&arg, sizeof(arg));
597 arg.type = AML_OBJTYPE_INTEGER;
598 arg.v_integer = wan ^ THINKPAD_WAN_ENABLED;
599 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "SWAN",
600 1, &arg, NULL)) {
601 printf("%s: couldn't toggle wan\n", DEVNAME(sc));
602 return (1);
603 }
604
605 return (0);
606 }
607
608 int
thinkpad_cmos(struct acpithinkpad_softc * sc,uint8_t cmd)609 thinkpad_cmos(struct acpithinkpad_softc *sc, uint8_t cmd)
610 {
611 struct aml_value arg;
612
613 bzero(&arg, sizeof(arg));
614 arg.type = AML_OBJTYPE_INTEGER;
615 arg.v_integer = cmd;
616 aml_evalname(sc->sc_acpi, sc->sc_devnode, "\\UCMS", 1, &arg, NULL);
617 return (0);
618 }
619
620 int
thinkpad_volume_down(struct acpithinkpad_softc * sc)621 thinkpad_volume_down(struct acpithinkpad_softc *sc)
622 {
623 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_DOWN));
624 }
625
626 int
thinkpad_volume_up(struct acpithinkpad_softc * sc)627 thinkpad_volume_up(struct acpithinkpad_softc *sc)
628 {
629 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_UP));
630 }
631
632 int
thinkpad_volume_mute(struct acpithinkpad_softc * sc)633 thinkpad_volume_mute(struct acpithinkpad_softc *sc)
634 {
635 return (thinkpad_cmos(sc, THINKPAD_CMOS_VOLUME_MUTE));
636 }
637
638 int
thinkpad_brightness_up(struct acpithinkpad_softc * sc)639 thinkpad_brightness_up(struct acpithinkpad_softc *sc)
640 {
641 int b;
642
643 if (thinkpad_get_brightness(sc) == 0) {
644 b = sc->sc_brightness & 0xff;
645 if (b < ((sc->sc_brightness >> 8) & 0xff)) {
646 sc->sc_brightness = b + 1;
647 thinkpad_set_brightness(sc, 0);
648 }
649
650 return (0);
651 } else
652 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_UP));
653 }
654
655 int
thinkpad_brightness_down(struct acpithinkpad_softc * sc)656 thinkpad_brightness_down(struct acpithinkpad_softc *sc)
657 {
658 int b;
659
660 if (thinkpad_get_brightness(sc) == 0) {
661 b = sc->sc_brightness & 0xff;
662 if (b > 0) {
663 sc->sc_brightness = b - 1;
664 thinkpad_set_brightness(sc, 0);
665 }
666
667 return (0);
668 } else
669 return (thinkpad_cmos(sc, THINKPAD_CMOS_BRIGHTNESS_DOWN));
670 }
671
672 int
thinkpad_adaptive_change(struct acpithinkpad_softc * sc)673 thinkpad_adaptive_change(struct acpithinkpad_softc *sc)
674 {
675 struct aml_value arg;
676 int64_t mode;
677
678 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
679 0, NULL, &mode)) {
680 printf("%s: couldn't get adaptive keyboard mode\n", DEVNAME(sc));
681 return (1);
682 }
683
684 bzero(&arg, sizeof(arg));
685 arg.type = AML_OBJTYPE_INTEGER;
686
687 if (mode == THINKPAD_ADAPTIVE_MODE_FUNCTION)
688 arg.v_integer = THINKPAD_ADAPTIVE_MODE_HOME;
689 else
690 arg.v_integer = THINKPAD_ADAPTIVE_MODE_FUNCTION;
691
692 if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "STRW",
693 1, &arg, NULL)) {
694 printf("%s: couldn't set adaptive keyboard mode\n", DEVNAME(sc));
695 return (1);
696 }
697
698 return (0);
699 }
700
701 int
thinkpad_activate(struct device * self,int act)702 thinkpad_activate(struct device *self, int act)
703 {
704
705 struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
706 int64_t res;
707 #if NAUDIO > 0 && NWSKBD > 0
708 int mute;
709 #endif
710
711 switch (act) {
712 case DVACT_WAKEUP:
713 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "GTRW",
714 0, NULL, &res) == 0)
715 thinkpad_adaptive_change(sc);
716 #if NAUDIO > 0 && NWSKBD > 0
717 mute = thinkpad_get_volume_mute(sc);
718 if (mute != -1)
719 wskbd_set_mixermute(mute, 1);
720 #endif
721 break;
722 }
723 return (0);
724 }
725
726 void
thinkpad_get_thinklight(struct acpithinkpad_softc * sc)727 thinkpad_get_thinklight(struct acpithinkpad_softc *sc)
728 {
729 if (sc->sc_thinklight_get)
730 aml_evalinteger(sc->sc_acpi, sc->sc_devnode,
731 sc->sc_thinklight_get, 0, NULL, &sc->sc_thinklight);
732 }
733
734 void
thinkpad_set_thinklight(void * arg0,int arg1)735 thinkpad_set_thinklight(void *arg0, int arg1)
736 {
737 struct acpithinkpad_softc *sc = arg0;
738 struct aml_value arg;
739
740 memset(&arg, 0, sizeof(arg));
741 arg.type = AML_OBJTYPE_INTEGER;
742 arg.v_integer = sc->sc_thinklight & 0x0f;
743 aml_evalname(sc->sc_acpi, sc->sc_devnode,
744 sc->sc_thinklight_set, 1, &arg, NULL);
745 }
746
747 int
thinkpad_get_kbd_backlight(struct wskbd_backlight * kbl)748 thinkpad_get_kbd_backlight(struct wskbd_backlight *kbl)
749 {
750 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
751
752 KASSERT(sc != NULL);
753
754 kbl->min = 0;
755 kbl->max = (sc->sc_thinklight >> 8) & 0x0f;
756 kbl->curval = sc->sc_thinklight & 0x0f;
757
758 if (kbl->max == 0)
759 return (ENOTTY);
760
761 return 0;
762 }
763
764 int
thinkpad_set_kbd_backlight(struct wskbd_backlight * kbl)765 thinkpad_set_kbd_backlight(struct wskbd_backlight *kbl)
766 {
767 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
768 int maxval;
769
770 KASSERT(sc != NULL);
771
772 maxval = (sc->sc_thinklight >> 8) & 0x0f;
773
774 if (maxval == 0)
775 return (ENOTTY);
776
777 if (kbl->curval > maxval)
778 return EINVAL;
779
780 sc->sc_thinklight &= ~0xff;
781 sc->sc_thinklight |= kbl->curval;
782 acpi_addtask(sc->sc_acpi, thinkpad_set_thinklight, sc, 0);
783 acpi_wakeup(sc->sc_acpi);
784 return 0;
785 }
786
787 int
thinkpad_get_brightness(struct acpithinkpad_softc * sc)788 thinkpad_get_brightness(struct acpithinkpad_softc *sc)
789 {
790 int ret;
791
792 ret = aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "PBLG", 0, NULL,
793 &sc->sc_brightness);
794
795 DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
796
797 return ret;
798 }
799
800 int
thinkpad_set_brightness(void * arg0,int arg1)801 thinkpad_set_brightness(void *arg0, int arg1)
802 {
803 struct acpithinkpad_softc *sc = arg0;
804 struct aml_value arg;
805 int ret;
806
807 DPRINTF(("%s: %s: 0x%llx\n", DEVNAME(sc), __func__, sc->sc_brightness));
808
809 memset(&arg, 0, sizeof(arg));
810 arg.type = AML_OBJTYPE_INTEGER;
811 arg.v_integer = sc->sc_brightness & 0xff;
812 ret = aml_evalname(sc->sc_acpi, sc->sc_devnode, "PBLS", 1, &arg, NULL);
813
814 if (ret)
815 return ret;
816
817 thinkpad_get_brightness(sc);
818
819 return 0;
820 }
821
822 int
thinkpad_get_param(struct wsdisplay_param * dp)823 thinkpad_get_param(struct wsdisplay_param *dp)
824 {
825 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
826
827 if (sc == NULL)
828 return -1;
829
830 switch (dp->param) {
831 case WSDISPLAYIO_PARAM_BRIGHTNESS:
832 dp->min = 0;
833 dp->max = (sc->sc_brightness >> 8) & 0xff;
834 dp->curval = sc->sc_brightness & 0xff;
835 return 0;
836 default:
837 return -1;
838 }
839 }
840
841 int
thinkpad_set_param(struct wsdisplay_param * dp)842 thinkpad_set_param(struct wsdisplay_param *dp)
843 {
844 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
845 int maxval;
846
847 if (sc == NULL)
848 return -1;
849
850 maxval = (sc->sc_brightness >> 8) & 0xff;
851
852 switch (dp->param) {
853 case WSDISPLAYIO_PARAM_BRIGHTNESS:
854 if (dp->curval < 0)
855 dp->curval = 0;
856 if (dp->curval > maxval)
857 dp->curval = maxval;
858 sc->sc_brightness &= ~0xff;
859 sc->sc_brightness |= dp->curval;
860 acpi_addtask(sc->sc_acpi, (void *)thinkpad_set_brightness, sc,
861 0);
862 acpi_wakeup(sc->sc_acpi);
863 return 0;
864 default:
865 return -1;
866 }
867 }
868
869 int
thinkpad_get_temp(struct acpithinkpad_softc * sc,int idx,int64_t * temp)870 thinkpad_get_temp(struct acpithinkpad_softc *sc, int idx, int64_t *temp)
871 {
872 char sname[5];
873
874 snprintf(sname, sizeof(sname), "TMP%d", idx);
875
876 if (aml_evalinteger(sc->sc_acpi, sc->sc_ec->sc_devnode, sname, 0, 0,
877 temp) != 0)
878 return (1);
879
880 return (0);
881 }
882
883 #if NAUDIO > 0 && NWSKBD > 0
884 void
thinkpad_attach_deferred(void * v __unused)885 thinkpad_attach_deferred(void *v __unused)
886 {
887 wskbd_set_mixermute(1, 1);
888 }
889
890 int
thinkpad_get_volume_mute(struct acpithinkpad_softc * sc)891 thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
892 {
893 uint8_t vol = 0;
894
895 if (sc->sc_acpi->sc_ec == NULL)
896 return (-1);
897
898 acpiec_read(sc->sc_acpi->sc_ec, THINKPAD_ECOFFSET_VOLUME, 1, &vol);
899 return ((vol & THINKPAD_ECOFFSET_VOLUME_MUTE_MASK) ==
900 THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
901 }
902 #endif
903
904 int
thinkpad_battery_inhibit_charge(int state)905 thinkpad_battery_inhibit_charge(int state)
906 {
907 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
908 struct aml_value arg;
909 int battery, count;
910 uint64_t ret;
911
912 count = acpi_batcount(sc->sc_acpi);
913 for (battery = 1; battery <= count; battery++) {
914 memset(&arg, 0, sizeof(arg));
915 arg.type = AML_OBJTYPE_INTEGER;
916 arg.v_integer = (0xffff << 8) | (battery << 4) | state;
917 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICS",
918 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
919 return EIO;
920 }
921 return 0;
922 }
923
924 int
thinkpad_battery_force_discharge(int state)925 thinkpad_battery_force_discharge(int state)
926 {
927 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
928 struct aml_value arg;
929 int battery, count;
930 uint64_t ret;
931
932 count = acpi_batcount(sc->sc_acpi);
933 for (battery = 1; battery <= count; battery++) {
934 memset(&arg, 0, sizeof(arg));
935 arg.type = AML_OBJTYPE_INTEGER;
936 arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | state;
937 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSS",
938 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
939 return EIO;
940 }
941 return 0;
942 }
943
944 int
thinkpad_battery_setchargemode(int mode)945 thinkpad_battery_setchargemode(int mode)
946 {
947 int error;
948
949 switch (mode) {
950 case -1:
951 error = thinkpad_battery_inhibit_charge(1);
952 if (error)
953 return error;
954 error = thinkpad_battery_force_discharge(1);
955 if (error)
956 return error;
957 break;
958 case 0:
959 error = thinkpad_battery_force_discharge(0);
960 if (error)
961 return error;
962 error = thinkpad_battery_inhibit_charge(1);
963 if (error)
964 return error;
965 break;
966 case 1:
967 error = thinkpad_battery_force_discharge(0);
968 if (error)
969 return error;
970 error = thinkpad_battery_inhibit_charge(0);
971 if (error)
972 return error;
973 break;
974 default:
975 return EOPNOTSUPP;
976 }
977
978 hw_battery_chargemode = mode;
979 return 0;
980 }
981
982 int
thinkpad_battery_setchargestart(int start)983 thinkpad_battery_setchargestart(int start)
984 {
985 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
986 struct aml_value arg;
987 int battery, count;
988 uint64_t ret;
989
990 if (start >= hw_battery_chargestop)
991 return EINVAL;
992
993 count = acpi_batcount(sc->sc_acpi);
994 for (battery = 1; battery <= count; battery++) {
995 memset(&arg, 0, sizeof(arg));
996 arg.type = AML_OBJTYPE_INTEGER;
997 arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | start;
998 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCCS",
999 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1000 return EIO;
1001 }
1002 hw_battery_chargestart = start;
1003 return 0;
1004 }
1005
1006 int
thinkpad_battery_setchargestop(int stop)1007 thinkpad_battery_setchargestop(int stop)
1008 {
1009 struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
1010 struct aml_value arg;
1011 int battery, count;
1012 uint64_t ret;
1013
1014 if (stop <= hw_battery_chargestart)
1015 return EINVAL;
1016
1017 if (stop == 100)
1018 stop = 0;
1019
1020 count = acpi_batcount(sc->sc_acpi);
1021 for (battery = 1; battery <= count; battery++) {
1022 memset(&arg, 0, sizeof(arg));
1023 arg.type = AML_OBJTYPE_INTEGER;
1024 arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | stop;
1025 if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSS",
1026 1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
1027 return EIO;
1028 }
1029 hw_battery_chargestop = (stop == 0) ? 100 : stop;
1030 return 0;
1031 }
1032