From ed2f38d4d6816834a7f14da8d9f6389adf044550 Mon Sep 17 00:00:00 2001 From: Kevin Zheng Date: Mon, 24 Dec 2018 19:14:58 -0600 Subject: [PATCH 2/2] 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 --- share/man/man4/acpi_ibm.4 | 22 +++++++ sys/dev/acpi_support/acpi_ibm.c | 101 +++++++++++++++++++++++++++++++- 2 files changed, 121 insertions(+), 2 deletions(-) diff --git a/share/man/man4/acpi_ibm.4 b/share/man/man4/acpi_ibm.4 index 8136b4ac774..4f00646af51 100644 --- a/share/man/man4/acpi_ibm.4 +++ b/share/man/man4/acpi_ibm.4 @@ -334,6 +334,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 cc4067d60d1..8c215c1ceb0 100644 --- a/sys/dev/acpi_support/acpi_ibm.c +++ b/sys/dev/acpi_support/acpi_ibm.c @@ -75,6 +75,8 @@ ACPI_MODULE_NAME("IBM") #define ACPI_IBM_METHOD_THERMAL 13 #define ACPI_IBM_METHOD_HANDLEREVENTS 14 #define ACPI_IBM_METHOD_MIC_LED 15 +#define ACPI_IBM_METHOD_BAT0_CHRG_START 16 +#define ACPI_IBM_METHOD_BAT0_CHRG_STOP 17 /* Hotkeys/Buttons */ #define IBM_RTC_HOTKEY1 0x64 @@ -146,6 +148,9 @@ ACPI_MODULE_NAME("IBM") #define IBM_EVENT_MUTE 0x17 #define IBM_EVENT_ACCESS_IBM_BUTTON 0x18 +/* Batteries */ +#define IBM_BAT_PRIMARY 1 + #define ABS(x) (((x) < 0)? -(x) : (x)) struct acpi_ibm_softc { @@ -268,6 +273,16 @@ static struct { .method = ACPI_IBM_METHOD_MIC_LED, .description = "Mic led", }, + { + .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 } }; @@ -346,8 +361,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", NULL}; @@ -408,6 +422,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) { @@ -786,6 +865,10 @@ acpi_ibm_sysctl_get(struct acpi_ibm_softc *sc, int method) else val = -1; 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); @@ -867,6 +950,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); @@ -973,6 +1062,14 @@ acpi_ibm_sysctl_init(struct acpi_ibm_softc *sc, int method) case ACPI_IBM_METHOD_HANDLEREVENTS: return (TRUE); + + 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); } -- 2.20.1