commit 3e48f554423d7428b79e10b6ddc8e1ef8de4bf35 Author: Kevin Zheng Date: Mon Dec 24 19:14:58 2018 -0600 Support ThinkPad battery charge start/stop control Informed by the Linux driver: https://lore.kernel.org/patchwork/patch/858076/ And this driver: https://github.com/teleshoes/tpacpi-bat/blob/master/battery_asl diff --git a/share/man/man4/acpi_ibm.4 b/share/man/man4/acpi_ibm.4 index 503fb9b06f2..87ae0b1eca6 100644 --- a/share/man/man4/acpi_ibm.4 +++ b/share/man/man4/acpi_ibm.4 @@ -341,6 +341,28 @@ Built-in battery .It UltraBay battery .El +.It Va dev.acpi_ibm.0.bat0_charge_start +Primary battery charge below which charging starts. +.Pp +.Bl -tag -width indent-two -compact +.It Li 0 +Use battery firmware's default setting +.It Li 1-99 +Threshold in percent +.It Li -4 +Not supported +.El +.It Va dev.acpi_ibm.0.bat0_charge_stop +Primary battery charge above which charging stops. +.Pp +.Bl -tag -width indent-two -compact +.It Li 0 +Use battery firmware's default setting +.It Li 1-99 +Threshold in percent +.It Li -4 +Not supported +.El .It Va dev.acpi_ibm.0.handlerevents .Xr devd 8 events handled by diff --git a/sys/dev/acpi_support/acpi_ibm.c b/sys/dev/acpi_support/acpi_ibm.c index 4c76ffa6043..62a1631db50 100644 --- a/sys/dev/acpi_support/acpi_ibm.c +++ b/sys/dev/acpi_support/acpi_ibm.c @@ -77,6 +77,8 @@ ACPI_MODULE_NAME("IBM") #define ACPI_IBM_METHOD_HANDLEREVENTS 14 #define ACPI_IBM_METHOD_MIC_LED 15 #define ACPI_IBM_METHOD_PRIVACYGUARD 16 +#define ACPI_IBM_METHOD_BAT0_CHRG_START 17 +#define ACPI_IBM_METHOD_BAT0_CHRG_STOP 18 /* Hotkeys/Buttons */ #define IBM_RTC_HOTKEY1 0x64 @@ -154,6 +156,9 @@ ACPI_MODULE_NAME("IBM") #define IBM_FLAG_PRIVACYGUARD_DEVICE_PRESENT 0x10000 #define IBM_FLAG_PRIVACYGUARD_ON 0x1 +/* Batteries */ +#define IBM_BAT_PRIMARY 1 + #define ABS(x) (((x) < 0)? -(x) : (x)) struct acpi_ibm_softc { @@ -282,6 +287,16 @@ static struct { .method = ACPI_IBM_METHOD_PRIVACYGUARD, .description = "PrivacyGuard enable", }, + { + .name = "bat0_charge_start", + .method = ACPI_IBM_METHOD_BAT0_CHRG_START, + .description = "Primary battery charge threshold", + }, + { + .name = "bat0_charge_stop", + .method = ACPI_IBM_METHOD_BAT0_CHRG_STOP, + .description = "Primary battery charge stop threshold", + }, { NULL, 0, NULL, 0 } }; @@ -365,8 +380,7 @@ static driver_t acpi_ibm_driver = { static devclass_t acpi_ibm_devclass; -DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, - 0, 0); +DRIVER_MODULE(acpi_ibm, acpi, acpi_ibm_driver, acpi_ibm_devclass, 0, 0); MODULE_DEPEND(acpi_ibm, acpi, 1, 1, 1); static char *ibm_ids[] = {"IBM0068", "LEN0068", "LEN0268", NULL}; @@ -440,6 +454,71 @@ acpi_ibm_mic_led_set (struct acpi_ibm_softc *sc, int arg) return (0); } +/* + * Evaluate an object with one integer parameter that returns one integer. + * If result is NULL, the return value is discarded. + */ +static ACPI_STATUS +acpi_EvalIntToInt(ACPI_HANDLE handle, char *path, UINT32 param, UINT32 *result) +{ + ACPI_STATUS status; + ACPI_OBJECT_LIST params; + ACPI_OBJECT pbuf[1]; + ACPI_BUFFER results; + ACPI_OBJECT rbuf[1]; + + params.Count = 1; + params.Pointer = pbuf; + pbuf[0].Type = ACPI_TYPE_INTEGER; + pbuf[0].Integer.Value = param; + + if (result != NULL) { + results.Length = sizeof(rbuf); + results.Pointer = rbuf; + status = AcpiEvaluateObject(handle, path, ¶ms, &results); + if (ACPI_SUCCESS(status)) { + if (rbuf[0].Type == ACPI_TYPE_INTEGER) + *result = rbuf[0].Integer.Value; + else + status = AE_TYPE; + } + } else + status = AcpiEvaluateObject(handle, path, ¶ms, NULL); + return (status); +} + +/* + * Get battery charge start (BCTG) and stop (BCSG) thresholds. + */ +static int +acpi_ibm_bat_get(ACPI_HANDLE handle, char *path, int bat) +{ + int val; + if (ACPI_FAILURE(acpi_EvalIntToInt(handle, path, bat & 0x3, &val))) + return -2; /* failed to eval object */ + if (val & (1 << 31)) + return -3; /* got error flag */ + if (!(val & (1 << 8))) + return -4; /* not supported */ + return val & 0xff; +} + +/* + * Set battery charge start (BCCS) and stop (BCSS) thresholds. + */ +static int +acpi_ibm_bat_set(ACPI_HANDLE handle, char *path, int bat, int arg) +{ + ACPI_STATUS status; + int val; + if (arg < 0 || arg > 99) + return (EINVAL); + status = acpi_EvalIntToInt(handle, path, (bat & 0x3) << 8 | (arg & 0xff), &val); + if (ACPI_FAILURE(status) || val & 0x8000) + return (-1); + return (0); +} + static int acpi_ibm_probe(device_t dev) { @@ -858,6 +937,11 @@ acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) val = acpi_ibm_privacyguard_get(sc); break; + case ACPI_IBM_METHOD_BAT0_CHRG_START: + return acpi_ibm_bat_get(sc->handle, "BCTG", IBM_BAT_PRIMARY); + + case ACPI_IBM_METHOD_BAT0_CHRG_STOP: + return acpi_ibm_bat_get(sc->handle, "BCSG", IBM_BAT_PRIMARY); } return (val); @@ -951,6 +1035,12 @@ acpi_ibm_sysctl_set(struct acpi_ibm_softc *sc, int method, int arg) (arg == 1) ? (val_ec | IBM_EC_MASK_FANSTATUS) : (val_ec & (~IBM_EC_MASK_FANSTATUS)), 1); } break; + + case ACPI_IBM_METHOD_BAT0_CHRG_START: + return acpi_ibm_bat_set(sc->handle, "BCCS", IBM_BAT_PRIMARY, arg); + + case ACPI_IBM_METHOD_BAT0_CHRG_STOP: + return acpi_ibm_bat_set(sc->handle, "BCSS", IBM_BAT_PRIMARY, arg); } return (0); @@ -1061,6 +1151,13 @@ acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) case ACPI_IBM_METHOD_PRIVACYGUARD: return (acpi_ibm_privacyguard_get(sc) != -1); + case ACPI_IBM_METHOD_BAT0_CHRG_START: + if (acpi_ibm_bat_get(sc->handle, "BCTG", IBM_BAT_PRIMARY) >= 0) + return (TRUE); + + case ACPI_IBM_METHOD_BAT0_CHRG_STOP: + if (acpi_ibm_bat_get(sc->handle, "BCSG", IBM_BAT_PRIMARY) >= 0) + return (TRUE); } return (FALSE); }