git: f87ba4522ec9 - main - acpi_system76: Add support for battary charge thresholds

From: Pouria Mousavizadeh Tehrani <pouria_at_FreeBSD.org>
Date: Mon, 09 Mar 2026 09:21:49 UTC
The branch main has been updated by pouria:

URL: https://cgit.FreeBSD.org/src/commit/?id=f87ba4522ec9e7b2227b8f20f3a4d7c6a129da1c

commit f87ba4522ec9e7b2227b8f20f3a4d7c6a129da1c
Author:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
AuthorDate: 2026-03-07 18:33:43 +0000
Commit:     Pouria Mousavizadeh Tehrani <pouria@FreeBSD.org>
CommitDate: 2026-03-09 09:11:56 +0000

    acpi_system76: Add support for battary charge thresholds
    
    Reviewed by: wulf
    Differential Revision: https://reviews.freebsd.org/D55710
---
 sys/dev/acpi_support/acpi_system76.c | 147 +++++++++++++++++++++++++++--------
 1 file changed, 116 insertions(+), 31 deletions(-)

diff --git a/sys/dev/acpi_support/acpi_system76.c b/sys/dev/acpi_support/acpi_system76.c
index c20725f0174e..9ae7d116be0b 100644
--- a/sys/dev/acpi_support/acpi_system76.c
+++ b/sys/dev/acpi_support/acpi_system76.c
@@ -54,7 +54,9 @@ struct acpi_system76_softc {
 	ACPI_HANDLE	handle;
 
 	struct acpi_ctrl	kbb,	/* S76_CTRL_KBB */
-				kbc;	/* S76_CTRL_KBC */
+				kbc,	/* S76_CTRL_KBC */
+				bctl,	/* S76_CTRL_BCTL */
+				bcth;	/* S76_CTRL_BCTH */
 
 	struct sysctl_ctx_list	sysctl_ctx;
 	struct sysctl_oid	*sysctl_tree;
@@ -72,19 +74,25 @@ static void	acpi_system76_notify_handler(ACPI_HANDLE, uint32_t, void *);
 static void	acpi_system76_check(struct acpi_system76_softc *);
 
 /* methods */
-#define	S76_CTRL_KBB	1	/* Keyboard Brightness */
-#define	S76_CTRL_KBC	2	/* Keyboard Color */
-#define	S76_CTRL_MAX	3
+enum {
+	S76_CTRL_KBB	= 1,	/* Keyboard Brightness */
+	S76_CTRL_KBC	= 2,	/* Keyboard Color */
+	S76_CTRL_BCTL	= 3,	/* Battary Charging Start Thresholds */
+	S76_CTRL_BCTH	= 4,	/* Battary Charging End Thresholds */
+};
+#define	S76_CTRL_MAX	5
 
 struct s76_ctrl_table {
 	char	*name;
 	char	*get_method;
 #define S76_CTRL_GKBB	"\\_SB.S76D.GKBB"
 #define S76_CTRL_GKBC	"\\_SB.S76D.GKBC"
+#define S76_CTRL_GBCT	"\\_SB.PCI0.LPCB.EC0.GBCT"
 
 	char	*set_method;
 #define S76_CTRL_SKBB	"\\_SB.S76D.SKBB"
 #define S76_CTRL_SKBC	"\\_SB.S76D.SKBC"
+#define S76_CTRL_SBCT	"\\_SB.PCI0.LPCB.EC0.SBCT"
 
 	char	*desc;
 };
@@ -102,6 +110,18 @@ static const struct s76_ctrl_table s76_sysctl_table[] = {
 		.set_method = S76_CTRL_SKBC,
 		.desc = "Keyboard Color",
 	},
+	[S76_CTRL_BCTL] = {
+		.name = "battary_thresholds_low",
+		.get_method = S76_CTRL_GBCT,
+		.set_method = S76_CTRL_SBCT,
+		.desc = "Battary charging start thresholds",
+	},
+	[S76_CTRL_BCTH] = {
+		.name = "battary_thresholds_high",
+		.get_method = S76_CTRL_GBCT,
+		.set_method = S76_CTRL_SBCT,
+		.desc = "Battary charging end thresholds",
+	},
 };
 
 static device_method_t acpi_system76_methods[] = {
@@ -135,10 +155,12 @@ acpi_system76_ctrl_map(struct acpi_system76_softc *sc, int method)
 	switch (method) {
 	case S76_CTRL_KBB:
 		return (&sc->kbb);
-		break;
 	case S76_CTRL_KBC:
 		return (&sc->kbc);
-		break;
+	case S76_CTRL_BCTL:
+		return (&sc->bctl);
+	case S76_CTRL_BCTH:
+		return (&sc->bcth);
 	default:
 		device_printf(sc->dev, "Driver received unknown method\n");
 		return (NULL);
@@ -150,6 +172,9 @@ acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set)
 {
 	struct acpi_ctrl *ctrl;
 	ACPI_STATUS status;
+	ACPI_BUFFER Buf;
+	ACPI_OBJECT Arg[2], Obj;
+	ACPI_OBJECT_LIST Args;
 
 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 	ACPI_SERIAL_ASSERT(system76);
@@ -157,12 +182,41 @@ acpi_system76_update(struct acpi_system76_softc *sc, int method, bool set)
 	if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
 		return (EINVAL);
 
-	if (set)
-		status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method,
-		    ctrl->val);
-	else
-		status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method,
-		    &ctrl->val);
+	switch (method) {
+	case S76_CTRL_BCTL:
+	case S76_CTRL_BCTH:
+		Arg[0].Type = ACPI_TYPE_INTEGER;
+		Arg[0].Integer.Value = method == S76_CTRL_BCTH ? 1 : 0;
+		Args.Count = set ? 2 : 1;
+		Args.Pointer = Arg;
+		Buf.Length = sizeof(Obj);
+		Buf.Pointer = &Obj;
+
+		if (set) {
+			Arg[1].Type = ACPI_TYPE_INTEGER;
+			Arg[1].Integer.Value = ctrl->val;
+
+			status = AcpiEvaluateObject(sc->handle,
+			    s76_sysctl_table[method].set_method, &Args, &Buf);
+		} else {
+			status = AcpiEvaluateObject(sc->handle,
+			    s76_sysctl_table[method].get_method, &Args, &Buf);
+			if (ACPI_SUCCESS(status) &&
+			    Obj.Type == ACPI_TYPE_INTEGER)
+				ctrl->val = Obj.Integer.Value;
+		}
+		break;
+	case S76_CTRL_KBB:
+	case S76_CTRL_KBC:
+		if (set)
+			status = acpi_SetInteger(sc->handle, s76_sysctl_table[method].set_method,
+			    ctrl->val);
+		else
+			status = acpi_GetInteger(sc->handle, s76_sysctl_table[method].get_method,
+			    &ctrl->val);
+		break;
+	}
+
 	if (ACPI_FAILURE(status)) {
 		device_printf(sc->dev, "Couldn't query method (%s)\n",
 		    s76_sysctl_table[method].name);
@@ -183,8 +237,12 @@ acpi_system76_notify_update(void *arg)
 	sc = (struct acpi_system76_softc *)device_get_softc(arg);
 
 	ACPI_SERIAL_BEGIN(system76);
-	for (method = 1; method < S76_CTRL_MAX; method++)
+	for (method = 1; method < S76_CTRL_MAX; method++) {
+		if (method == S76_CTRL_BCTL ||
+		    method == S76_CTRL_BCTH)
+			continue;
 		acpi_system76_update(sc, method, false);
+	}
 	ACPI_SERIAL_END(system76);
 }
 
@@ -201,6 +259,14 @@ acpi_system76_check(struct acpi_system76_softc *sc)
 		if ((ctrl = acpi_system76_ctrl_map(sc, method)) == NULL)
 			continue;
 
+		/* available in all models */
+		if (method == S76_CTRL_BCTL ||
+		    method == S76_CTRL_BCTH) {
+			ctrl->exists = true;
+			acpi_system76_update(sc, method, false);
+			continue;
+		}
+
 		if (ACPI_FAILURE(acpi_GetInteger(sc->handle,
 		    s76_sysctl_table[method].get_method, &ctrl->val))) {
 			ctrl->exists = false;
@@ -236,9 +302,10 @@ acpi_system76_notify_handler(ACPI_HANDLE handle, uint32_t notify, void *ctx)
 static int
 acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS)
 {
-	struct acpi_ctrl *ctrl;
+	struct acpi_ctrl *ctrl, *ctrl_cmp;
 	struct acpi_system76_softc *sc;
 	int val, method, error;
+	bool update;
 
 	ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 
@@ -253,27 +320,45 @@ acpi_system76_sysctl_handler(SYSCTL_HANDLER_ARGS)
 		device_printf(sc->dev, "Driver query failed\n");
 		return (error);
 	}
-	if (req->newptr == NULL)
-		return (error);
 
-	/* Input validation */
-	switch (method) {
-	case S76_CTRL_KBB:
-		if (val > UINT8_MAX || val < 0)
-			return (EINVAL);
-		break;
-	case S76_CTRL_KBC:
-		if (val >= (1 << 24) || val < 0)
-			return (EINVAL);
-		break;
-	default:
-		break;
+	if (req->newptr == NULL) {
+		/*
+		 * ACPI will not notify us if battary thresholds changes
+		 * outside this module. Therefore, always fetch those values.
+		 */
+		if (method != S76_CTRL_BCTL && method != S76_CTRL_BCTH)
+			return (error);
+		update = false;
+	} else {
+		/* Input validation */
+		switch (method) {
+		case S76_CTRL_KBB:
+			if (val > UINT8_MAX || val < 0)
+				return (EINVAL);
+			break;
+		case S76_CTRL_KBC:
+			if (val >= (1 << 24) || val < 0)
+				return (EINVAL);
+			break;
+		case S76_CTRL_BCTL:
+			if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTH)) == NULL)
+				return (EINVAL);
+			if (val > 100 || val < 0 || val >= ctrl_cmp->val)
+				return (EINVAL);
+			break;
+		case S76_CTRL_BCTH:
+			if ((ctrl_cmp = acpi_system76_ctrl_map(sc, S76_CTRL_BCTL)) == NULL)
+				return (EINVAL);
+			if (val > 100 || val < 0 || val <= ctrl_cmp->val)
+				return (EINVAL);
+			break;
+		}
+		ctrl->val = val;
+		update = true;
 	}
 
-	ctrl->val = val;
-
 	ACPI_SERIAL_BEGIN(system76);
-	error = acpi_system76_update(sc, method, true);
+	error = acpi_system76_update(sc, method, update);
 	ACPI_SERIAL_END(system76);
 	return (error);
 }