Implement battery charge control.
authorkettenis <kettenis@openbsd.org>
Sat, 20 May 2023 12:02:46 +0000 (12:02 +0000)
committerkettenis <kettenis@openbsd.org>
Sat, 20 May 2023 12:02:46 +0000 (12:02 +0000)
ok patrick@

sys/dev/acpi/acpithinkpad.c

index d749588..13ea8d8 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: acpithinkpad.c,v 1.72 2023/04/27 19:06:57 miod Exp $  */
+/*     $OpenBSD: acpithinkpad.c,v 1.73 2023/05/20 12:02:46 kettenis Exp $      */
 /*
  * Copyright (c) 2008 joshua stein <jcs@openbsd.org>
  *
 #define THINKPAD_MASK_BRIGHTNESS_DOWN  (1 << 16)
 #define THINKPAD_MASK_KBD_BACKLIGHT    (1 << 17)
 
+#define THINKPAD_BATTERY_ERROR         0x80000000
+#define THINKPAD_BATTERY_SUPPORT       0x00000100
+#define THINKPAD_BATTERY_SUPPORT_BICG  0x00000020
+#define THINKPAD_BATTERY_SHIFT         8
+
 struct acpithinkpad_softc {
        struct device            sc_dev;
 
@@ -190,6 +195,17 @@ extern int wskbd_set_mixermute(long, long);
 extern int wskbd_set_mixervolume(long, long);
 #endif
 
+int    thinkpad_battery_setchargemode(int);
+int    thinkpad_battery_setchargestart(int);
+int    thinkpad_battery_setchargestop(int);
+
+extern int (*hw_battery_setchargemode)(int);
+extern int (*hw_battery_setchargestart)(int);
+extern int (*hw_battery_setchargestop)(int);
+extern int hw_battery_chargemode;
+extern int hw_battery_chargestart;
+extern int hw_battery_chargestop;
+
 const struct cfattach acpithinkpad_ca = {
        sizeof(struct acpithinkpad_softc), thinkpad_match, thinkpad_attach,
        NULL, thinkpad_activate
@@ -300,6 +316,8 @@ thinkpad_attach(struct device *parent, struct device *self, void *aux)
 {
        struct acpithinkpad_softc *sc = (struct acpithinkpad_softc *)self;
        struct acpi_attach_args *aa = aux;
+       struct aml_value arg;
+       uint64_t ret;
 
        sc->sc_acpi = (struct acpi_softc *)parent;
        sc->sc_devnode = aa->aaa_node;
@@ -344,6 +362,52 @@ thinkpad_attach(struct device *parent, struct device *self, void *aux)
                ws_set_param = thinkpad_set_param;
        }
 
+       memset(&arg, 0, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = 1;
+
+       hw_battery_chargemode = 1;
+       hw_battery_chargestart = 0;
+       hw_battery_chargestop = 100;
+
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCTG",
+           1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
+               if (ret & THINKPAD_BATTERY_SUPPORT) {
+                       hw_battery_chargestart = ret & 0xff;
+                       hw_battery_setchargestart =
+                               thinkpad_battery_setchargestart;
+               }
+       }
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSG",
+           1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
+               if (ret & THINKPAD_BATTERY_SUPPORT) {
+                       if ((ret & 0xff) == 0)
+                               hw_battery_chargestop = 100;
+                       else
+                               hw_battery_chargestop = ret & 0xff;
+                       hw_battery_setchargestop =
+                               thinkpad_battery_setchargestop;
+               }
+       }
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSG",
+           1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
+               if (ret & THINKPAD_BATTERY_SUPPORT) {
+                       if (ret & 0x1)
+                               hw_battery_chargemode = -1;
+                       hw_battery_setchargemode =
+                               thinkpad_battery_setchargemode;
+               }
+       }
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICG",
+           1, &arg, &ret) == 0 && (ret & THINKPAD_BATTERY_ERROR) == 0) {
+               if (ret & THINKPAD_BATTERY_SUPPORT_BICG) {
+                       if (ret & 0x1)
+                               hw_battery_chargemode = 0;
+                       hw_battery_setchargemode =
+                               thinkpad_battery_setchargemode;
+               }
+       }
+
        /* Run thinkpad_hotkey on button presses */
        aml_register_notify(sc->sc_devnode, aa->aaa_dev,
            thinkpad_hotkey, sc, ACPIDEV_POLL);
@@ -836,3 +900,124 @@ thinkpad_get_volume_mute(struct acpithinkpad_softc *sc)
            THINKPAD_ECOFFSET_VOLUME_MUTE_MASK);
 }
 #endif
+
+int
+thinkpad_battery_inhibit_charge(int state)
+{
+       struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
+       struct aml_value arg;
+       int battery = 1;
+       uint64_t ret;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = (0xffff << 8) | (battery << 4) | state;
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BICS",
+           1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
+               return EIO;
+
+       return 0;
+}
+
+int
+thinkpad_battery_force_discharge(int state)
+{
+       struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
+       struct aml_value arg;
+       int battery = 1;
+       uint64_t ret;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | state;
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BDSS",
+           1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
+               return EIO;
+
+       return 0;
+}
+
+int
+thinkpad_battery_setchargemode(int mode)
+{
+       int error;
+
+       switch (mode) {
+       case -1:
+               error = thinkpad_battery_inhibit_charge(1);
+               if (error)
+                       return error;
+               error = thinkpad_battery_force_discharge(1);
+               if (error)
+                       return error;
+               break;
+       case 0:
+               error = thinkpad_battery_force_discharge(0);
+               if (error)
+                       return error;
+               error = thinkpad_battery_inhibit_charge(1);
+               if (error)
+                       return error;
+               break;
+       case 1:
+               error = thinkpad_battery_force_discharge(0);
+               if (error)
+                       return error;
+               error = thinkpad_battery_inhibit_charge(0);
+               if (error)
+                       return error;
+               break;
+       default:
+               return EOPNOTSUPP;
+       }
+
+       hw_battery_chargemode = mode;
+       return 0;
+}
+
+int
+thinkpad_battery_setchargestart(int start)
+{
+       struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
+       struct aml_value arg;
+       int battery = 1;
+       uint64_t ret;
+
+       if (start >= hw_battery_chargestop)
+               return EINVAL;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | start;
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCCS",
+           1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
+               return EIO;
+
+       hw_battery_chargestart = start;
+       return 0;
+}
+
+int
+thinkpad_battery_setchargestop(int stop)
+{
+       struct acpithinkpad_softc *sc = acpithinkpad_cd.cd_devs[0];
+       struct aml_value arg;
+       int battery = 1;
+       uint64_t ret;
+
+       if (stop <= hw_battery_chargestart)
+               return EINVAL;
+
+       if (stop == 100)
+               stop = 0;
+
+       memset(&arg, 0, sizeof(arg));
+       arg.type = AML_OBJTYPE_INTEGER;
+       arg.v_integer = (battery << THINKPAD_BATTERY_SHIFT) | stop;
+       if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "BCSS",
+           1, &arg, &ret) || (ret & THINKPAD_BATTERY_ERROR))
+               return EIO;
+
+       hw_battery_chargestop = (stop == 0) ? 100 : stop;
+       return 0;
+}