Sat Apr 27 14:50:18 2024 UTC (12d)
Expose a sysctl interface hw.acpi.thinkpad<M>.bat[<N>].<behavior> to control
some aspects of battery charging behavior on supported systems:

    charge_start
	threshold below which to start charging (in %, 0-99)

    charge_stop
	threshold above which to stop charging (in %, 1-100)

    force_discharge
	discharge while on AC power, e.g., for calibration

    charge_inhibit
	inhibit charging while on AC power

From Malte Dehling


(christos)
diff -r1.56 -r1.57 src/sys/dev/acpi/thinkpad_acpi.c

cvs diff -r1.56 -r1.57 src/sys/dev/acpi/thinkpad_acpi.c (expand / switch to unified diff)

--- src/sys/dev/acpi/thinkpad_acpi.c 2024/04/27 14:45:11 1.56
+++ src/sys/dev/acpi/thinkpad_acpi.c 2024/04/27 14:50:18 1.57
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: thinkpad_acpi.c,v 1.56 2024/04/27 14:45:11 christos Exp $ */ 1/* $NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca> 4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -17,52 +17,70 @@ @@ -17,52 +17,70 @@
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 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 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 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 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 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 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29#include <sys/cdefs.h> 29#include <sys/cdefs.h>
30__KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.56 2024/04/27 14:45:11 christos Exp $"); 30__KERNEL_RCSID(0, "$NetBSD: thinkpad_acpi.c,v 1.57 2024/04/27 14:50:18 christos Exp $");
31 31
32#include <sys/param.h> 32#include <sys/param.h>
33#include <sys/device.h> 33#include <sys/device.h>
34#include <sys/module.h> 34#include <sys/module.h>
35#include <sys/sdt.h> 35#include <sys/sdt.h>
36#include <sys/systm.h> 36#include <sys/systm.h>
 37#include <sys/sysctl.h>
37 38
38#include <dev/acpi/acpireg.h> 39#include <dev/acpi/acpireg.h>
39#include <dev/acpi/acpivar.h> 40#include <dev/acpi/acpivar.h>
40#include <dev/acpi/acpi_ecvar.h> 41#include <dev/acpi/acpi_ecvar.h>
41#include <dev/acpi/acpi_power.h> 42#include <dev/acpi/acpi_power.h>
42 43
43#include <dev/isa/isareg.h> 44#include <dev/isa/isareg.h>
44 45
45#define _COMPONENT ACPI_RESOURCE_COMPONENT 46#define _COMPONENT ACPI_RESOURCE_COMPONENT
46ACPI_MODULE_NAME ("thinkpad_acpi") 47ACPI_MODULE_NAME ("thinkpad_acpi")
47 48
48#define THINKPAD_NTEMPSENSORS 8 49#define THINKPAD_NTEMPSENSORS 8
49#define THINKPAD_NFANSENSORS 1 50#define THINKPAD_NFANSENSORS 1
50#define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS) 51#define THINKPAD_NSENSORS (THINKPAD_NTEMPSENSORS + THINKPAD_NFANSENSORS)
51 52
 53typedef struct tp_sysctl_param {
 54 device_t sp_dev;
 55 int sp_bat;
 56} tp_sysctl_param_t;
 57
 58typedef 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
52typedef struct thinkpad_softc { 69typedef struct thinkpad_softc {
53 device_t sc_dev; 70 device_t sc_dev;
54 device_t sc_ecdev; 71 device_t sc_ecdev;
55 struct acpi_devnode *sc_node; 72 struct acpi_devnode *sc_node;
 73 struct sysctllog *sc_log;
56 ACPI_HANDLE sc_powhdl; 74 ACPI_HANDLE sc_powhdl;
57 ACPI_HANDLE sc_cmoshdl; 75 ACPI_HANDLE sc_cmoshdl;
58 ACPI_INTEGER sc_ver; 76 ACPI_INTEGER sc_ver;
59 77
60#define TP_PSW_SLEEP 0 /* FnF4 */ 78#define TP_PSW_SLEEP 0 /* FnF4 */
61#define TP_PSW_HIBERNATE 1 /* FnF12 */ 79#define TP_PSW_HIBERNATE 1 /* FnF12 */
62#define TP_PSW_DISPLAY_CYCLE 2 /* FnF7 */ 80#define TP_PSW_DISPLAY_CYCLE 2 /* FnF7 */
63#define TP_PSW_LOCK_SCREEN 3 /* FnF2 */ 81#define TP_PSW_LOCK_SCREEN 3 /* FnF2 */
64#define TP_PSW_BATTERY_INFO 4 /* FnF3 */ 82#define TP_PSW_BATTERY_INFO 4 /* FnF3 */
65#define TP_PSW_EJECT_BUTTON 5 /* FnF9 */ 83#define TP_PSW_EJECT_BUTTON 5 /* FnF9 */
66#define TP_PSW_ZOOM_BUTTON 6 /* FnSPACE */ 84#define TP_PSW_ZOOM_BUTTON 6 /* FnSPACE */
67#define TP_PSW_VENDOR_BUTTON 7 /* ThinkVantage */ 85#define TP_PSW_VENDOR_BUTTON 7 /* ThinkVantage */
68#define TP_PSW_FNF1_BUTTON 8 /* FnF1 */ 86#define TP_PSW_FNF1_BUTTON 8 /* FnF1 */
@@ -80,26 +98,34 @@ typedef struct thinkpad_softc { @@ -80,26 +98,34 @@ typedef struct thinkpad_softc {
80#define TP_PSW_STAR_BUTTON 20 98#define TP_PSW_STAR_BUTTON 20
81#define TP_PSW_SCISSORS_BUTTON 21 99#define TP_PSW_SCISSORS_BUTTON 21
82#define TP_PSW_BLUETOOTH_BUTTON 22 100#define TP_PSW_BLUETOOTH_BUTTON 22
83#define TP_PSW_KEYBOARD_BUTTON 23 101#define TP_PSW_KEYBOARD_BUTTON 23
84#define TP_PSW_LAST 24 102#define TP_PSW_LAST 24
85 103
86 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST]; 104 struct sysmon_pswitch sc_smpsw[TP_PSW_LAST];
87 bool sc_smpsw_valid; 105 bool sc_smpsw_valid;
88 106
89 struct sysmon_envsys *sc_sme; 107 struct sysmon_envsys *sc_sme;
90 envsys_data_t sc_sensor[THINKPAD_NSENSORS]; 108 envsys_data_t sc_sensor[THINKPAD_NSENSORS];
91 109
92 int sc_display_state; 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];
93} thinkpad_softc_t; 119} thinkpad_softc_t;
94 120
95/* Hotkey events */ 121/* Hotkey events */
96#define THINKPAD_NOTIFY_FnF1 0x001 122#define THINKPAD_NOTIFY_FnF1 0x001
97#define THINKPAD_NOTIFY_LockScreen 0x002 123#define THINKPAD_NOTIFY_LockScreen 0x002
98#define THINKPAD_NOTIFY_BatteryInfo 0x003 124#define THINKPAD_NOTIFY_BatteryInfo 0x003
99#define THINKPAD_NOTIFY_SleepButton 0x004 125#define THINKPAD_NOTIFY_SleepButton 0x004
100#define THINKPAD_NOTIFY_WirelessSwitch 0x005 126#define THINKPAD_NOTIFY_WirelessSwitch 0x005
101#define THINKPAD_NOTIFY_wWANSwitch 0x006 127#define THINKPAD_NOTIFY_wWANSwitch 0x006
102#define THINKPAD_NOTIFY_DisplayCycle 0x007 128#define THINKPAD_NOTIFY_DisplayCycle 0x007
103#define THINKPAD_NOTIFY_PointerSwitch 0x008 129#define THINKPAD_NOTIFY_PointerSwitch 0x008
104#define THINKPAD_NOTIFY_EjectButton 0x009 130#define THINKPAD_NOTIFY_EjectButton 0x009
105#define THINKPAD_NOTIFY_FnF10 0x00a /* XXX: Not seen on T61 */ 131#define THINKPAD_NOTIFY_FnF10 0x00a /* XXX: Not seen on T61 */
@@ -120,26 +146,37 @@ typedef struct thinkpad_softc { @@ -120,26 +146,37 @@ typedef struct thinkpad_softc {
120 146
121#define THINKPAD_CMOS_BRIGHTNESS_UP 0x04 147#define THINKPAD_CMOS_BRIGHTNESS_UP 0x04
122#define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05 148#define THINKPAD_CMOS_BRIGHTNESS_DOWN 0x05
123 149
124#define THINKPAD_HKEY_VERSION_1 0x0100 150#define THINKPAD_HKEY_VERSION_1 0x0100
125#define THINKPAD_HKEY_VERSION_2 0x0200 151#define THINKPAD_HKEY_VERSION_2 0x0200
126 152
127#define THINKPAD_DISPLAY_LCD 0x01 153#define THINKPAD_DISPLAY_LCD 0x01
128#define THINKPAD_DISPLAY_CRT 0x02 154#define THINKPAD_DISPLAY_CRT 0x02
129#define THINKPAD_DISPLAY_DVI 0x08 155#define THINKPAD_DISPLAY_DVI 0x08
130#define THINKPAD_DISPLAY_ALL \ 156#define THINKPAD_DISPLAY_ALL \
131 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI) 157 (THINKPAD_DISPLAY_LCD | THINKPAD_DISPLAY_CRT | THINKPAD_DISPLAY_DVI)
132 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
133#define THINKPAD_BLUETOOTH_HWPRESENT 0x01 170#define THINKPAD_BLUETOOTH_HWPRESENT 0x01
134#define THINKPAD_BLUETOOTH_RADIOSSW 0x02 171#define THINKPAD_BLUETOOTH_RADIOSSW 0x02
135#define THINKPAD_BLUETOOTH_RESUMECTRL 0x04 172#define THINKPAD_BLUETOOTH_RESUMECTRL 0x04
136 173
137#define THINKPAD_WWAN_HWPRESENT 0x01 174#define THINKPAD_WWAN_HWPRESENT 0x01
138#define THINKPAD_WWAN_RADIOSSW 0x02 175#define THINKPAD_WWAN_RADIOSSW 0x02
139#define THINKPAD_WWAN_RESUMECTRL 0x04 176#define THINKPAD_WWAN_RESUMECTRL 0x04
140 177
141#define THINKPAD_UWB_HWPRESENT 0x01 178#define THINKPAD_UWB_HWPRESENT 0x01
142#define THINKPAD_UWB_RADIOSSW 0x02 179#define THINKPAD_UWB_RADIOSSW 0x02
143 180
144#define THINKPAD_RFK_BLUETOOTH 0 181#define THINKPAD_RFK_BLUETOOTH 0
145#define THINKPAD_RFK_WWAN 1 182#define THINKPAD_RFK_WWAN 1
@@ -158,26 +195,29 @@ static void thinkpad_sensors_refresh(str @@ -158,26 +195,29 @@ static void thinkpad_sensors_refresh(str
158static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *); 195static void thinkpad_temp_refresh(struct sysmon_envsys *, envsys_data_t *);
159static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *); 196static void thinkpad_fan_refresh(struct sysmon_envsys *, envsys_data_t *);
160 197
161static void thinkpad_uwb_toggle(thinkpad_softc_t *); 198static void thinkpad_uwb_toggle(thinkpad_softc_t *);
162static void thinkpad_wwan_toggle(thinkpad_softc_t *); 199static void thinkpad_wwan_toggle(thinkpad_softc_t *);
163static void thinkpad_bluetooth_toggle(thinkpad_softc_t *); 200static void thinkpad_bluetooth_toggle(thinkpad_softc_t *);
164 201
165static bool thinkpad_resume(device_t, const pmf_qual_t *); 202static bool thinkpad_resume(device_t, const pmf_qual_t *);
166static void thinkpad_brightness_up(device_t); 203static void thinkpad_brightness_up(device_t);
167static void thinkpad_brightness_down(device_t); 204static void thinkpad_brightness_down(device_t);
168static uint8_t thinkpad_brightness_read(thinkpad_softc_t *); 205static uint8_t thinkpad_brightness_read(thinkpad_softc_t *);
169static void thinkpad_cmos(thinkpad_softc_t *, uint8_t); 206static void thinkpad_cmos(thinkpad_softc_t *, uint8_t);
170 207
 208static void thinkpad_battery_probe_support(device_t);
 209static void thinkpad_battery_sysctl_setup(device_t);
 210
171CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t), 211CFATTACH_DECL3_NEW(thinkpad, sizeof(thinkpad_softc_t),
172 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL, 212 thinkpad_match, thinkpad_attach, thinkpad_detach, NULL, NULL, NULL,
173 0); 213 0);
174 214
175static const struct device_compatible_entry compat_data[] = { 215static const struct device_compatible_entry compat_data[] = {
176 { .compat = "IBM0068" }, 216 { .compat = "IBM0068" },
177 { .compat = "LEN0068" }, 217 { .compat = "LEN0068" },
178 { .compat = "LEN0268" }, 218 { .compat = "LEN0268" },
179 DEVICE_COMPAT_EOL 219 DEVICE_COMPAT_EOL
180}; 220};
181 221
182static int 222static int
183thinkpad_match(device_t parent, cfdata_t match, void *opaque) 223thinkpad_match(device_t parent, cfdata_t match, void *opaque)
@@ -210,26 +250,27 @@ thinkpad_match(device_t parent, cfdata_t @@ -210,26 +250,27 @@ thinkpad_match(device_t parent, cfdata_t
210static void 250static void
211thinkpad_attach(device_t parent, device_t self, void *opaque) 251thinkpad_attach(device_t parent, device_t self, void *opaque)
212{ 252{
213 thinkpad_softc_t *sc = device_private(self); 253 thinkpad_softc_t *sc = device_private(self);
214 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque; 254 struct acpi_attach_args *aa = (struct acpi_attach_args *)opaque;
215 struct sysmon_pswitch *psw; 255 struct sysmon_pswitch *psw;
216 device_t curdev; 256 device_t curdev;
217 deviter_t di; 257 deviter_t di;
218 ACPI_STATUS rv; 258 ACPI_STATUS rv;
219 ACPI_INTEGER val; 259 ACPI_INTEGER val;
220 int i; 260 int i;
221 261
222 sc->sc_dev = self; 262 sc->sc_dev = self;
 263 sc->sc_log = NULL;
223 sc->sc_powhdl = NULL; 264 sc->sc_powhdl = NULL;
224 sc->sc_cmoshdl = NULL; 265 sc->sc_cmoshdl = NULL;
225 sc->sc_node = aa->aa_node; 266 sc->sc_node = aa->aa_node;
226 sc->sc_display_state = THINKPAD_DISPLAY_LCD; 267 sc->sc_display_state = THINKPAD_DISPLAY_LCD;
227 268
228 aprint_naive("\n"); 269 aprint_naive("\n");
229 aprint_normal("\n"); 270 aprint_normal("\n");
230 271
231 sc->sc_ecdev = NULL; 272 sc->sc_ecdev = NULL;
232 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST); 273 for (curdev = deviter_first(&di, DEVITER_F_ROOT_FIRST);
233 curdev != NULL; curdev = deviter_next(&di)) 274 curdev != NULL; curdev = deviter_next(&di))
234 if (device_is_a(curdev, "acpiecdt") || 275 if (device_is_a(curdev, "acpiecdt") ||
235 device_is_a(curdev, "acpiec")) { 276 device_is_a(curdev, "acpiec")) {
@@ -361,51 +402,65 @@ thinkpad_attach(device_t parent, device_ @@ -361,51 +402,65 @@ thinkpad_attach(device_t parent, device_
361 if (i == TP_PSW_HIBERNATE) 402 if (i == TP_PSW_HIBERNATE)
362 continue; 403 continue;
363 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) { 404 if (sysmon_pswitch_register(&sc->sc_smpsw[i]) != 0) {
364 aprint_error_dev(self, 405 aprint_error_dev(self,
365 "couldn't register with sysmon\n"); 406 "couldn't register with sysmon\n");
366 sc->sc_smpsw_valid = false; 407 sc->sc_smpsw_valid = false;
367 break; 408 break;
368 } 409 }
369 } 410 }
370 411
371 /* Register temperature and fan sensors with envsys */ 412 /* Register temperature and fan sensors with envsys */
372 thinkpad_sensors_init(sc); 413 thinkpad_sensors_init(sc);
373 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
374fail: 426fail:
375 if (!pmf_device_register(self, NULL, thinkpad_resume)) 427 if (!pmf_device_register(self, NULL, thinkpad_resume))
376 aprint_error_dev(self, "couldn't establish power handler\n"); 428 aprint_error_dev(self, "couldn't establish power handler\n");
377 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP, 429 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_UP,
378 thinkpad_brightness_up, true)) 430 thinkpad_brightness_up, true))
379 aprint_error_dev(self, "couldn't register event handler\n"); 431 aprint_error_dev(self, "couldn't register event handler\n");
380 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 432 if (!pmf_event_register(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
381 thinkpad_brightness_down, true)) 433 thinkpad_brightness_down, true))
382 aprint_error_dev(self, "couldn't register event handler\n"); 434 aprint_error_dev(self, "couldn't register event handler\n");
383} 435}
384 436
385static int 437static int
386thinkpad_detach(device_t self, int flags) 438thinkpad_detach(device_t self, int flags)
387{ 439{
388 struct thinkpad_softc *sc = device_private(self); 440 struct thinkpad_softc *sc = device_private(self);
389 int i; 441 int i;
390 442
391 acpi_deregister_notify(sc->sc_node); 443 acpi_deregister_notify(sc->sc_node);
392 444
393 for (i = 0; i < TP_PSW_LAST; i++) 445 for (i = 0; i < TP_PSW_LAST; i++)
394 sysmon_pswitch_unregister(&sc->sc_smpsw[i]); 446 sysmon_pswitch_unregister(&sc->sc_smpsw[i]);
395 447
396 if (sc->sc_sme != NULL) 448 if (sc->sc_sme != NULL)
397 sysmon_envsys_unregister(sc->sc_sme); 449 sysmon_envsys_unregister(sc->sc_sme);
398 450
 451 if (sc->sc_log != NULL)
 452 sysctl_teardown(&sc->sc_log);
 453
399 pmf_device_deregister(self); 454 pmf_device_deregister(self);
400 455
401 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP, 456 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_UP,
402 thinkpad_brightness_up, true); 457 thinkpad_brightness_up, true);
403 458
404 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN, 459 pmf_event_deregister(self, PMFE_DISPLAY_BRIGHTNESS_DOWN,
405 thinkpad_brightness_down, true); 460 thinkpad_brightness_down, true);
406 461
407 return 0; 462 return 0;
408} 463}
409 464
410static void 465static void
411thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque) 466thinkpad_notify_handler(ACPI_HANDLE hdl, uint32_t notify, void *opaque)
@@ -938,26 +993,310 @@ thinkpad_cmos(thinkpad_softc_t *sc, uint @@ -938,26 +993,310 @@ thinkpad_cmos(thinkpad_softc_t *sc, uint
938{ 993{
939 ACPI_STATUS rv; 994 ACPI_STATUS rv;
940 995
941 if (sc->sc_cmoshdl == NULL) 996 if (sc->sc_cmoshdl == NULL)
942 return; 997 return;
943 998
944 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd); 999 rv = acpi_eval_set_integer(sc->sc_cmoshdl, NULL, cmd);
945 1000
946 if (ACPI_FAILURE(rv)) 1001 if (ACPI_FAILURE(rv))
947 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n", 1002 aprint_error_dev(sc->sc_dev, "couldn't evaluate CMOS: %s\n",
948 AcpiFormatException(rv)); 1003 AcpiFormatException(rv));
949} 1004}
950 1005
 1006static uint32_t
 1007thinkpad_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
 1039static void
 1040thinkpad_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
 1089static int
 1090thinkpad_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
 1115static int
 1116thinkpad_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
 1147static int
 1148thinkpad_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
 1170static int
 1171thinkpad_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
 1193static void
 1194thinkpad_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
 1232static void
 1233thinkpad_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
 1286fail:
 1287 aprint_error_dev(self, "unable to add sysctl nodes (%d)\n", err);
 1288}
 1289
951static bool 1290static bool
952thinkpad_resume(device_t dv, const pmf_qual_t *qual) 1291thinkpad_resume(device_t dv, const pmf_qual_t *qual)
953{ 1292{
954 thinkpad_softc_t *sc = device_private(dv); 1293 thinkpad_softc_t *sc = device_private(dv);
955 1294
956 if (sc->sc_powhdl == NULL) 1295 if (sc->sc_powhdl == NULL)
957 return true; 1296 return true;
958 1297
959 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true); 1298 (void)acpi_power_res(sc->sc_powhdl, sc->sc_node->ad_handle, true);
960 1299
961 return true; 1300 return true;
962} 1301}
963 1302