git: 1ecac45cfc5f - main - asmc: add per-fan manual mode control via sysctl
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Mon, 05 Jan 2026 18:26:29 UTC
The branch main has been updated by markj:
URL: https://cgit.FreeBSD.org/src/commit/?id=1ecac45cfc5f0886907c34ec0da2c1b32618665d
commit 1ecac45cfc5f0886907c34ec0da2c1b32618665d
Author: Abdelkader Boudih <oss@seuros.com>
AuthorDate: 2026-01-05 18:03:23 +0000
Commit: Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-01-05 18:25:47 +0000
asmc: add per-fan manual mode control via sysctl
Add per-fan manual mode control via dev.asmc.0.fan.N.manual sysctl.
Apple SMCs support manual fan control via the FS! SMC key,
a 16-bit bitmask where each bit controls one fan (0=auto, 1=manual).
This change adds a new sysctl per fan:
dev.asmc.0.fan.N.manual (0=auto, 1=manual)
When set to manual mode (1), the fan runs at the speed set via
dev.asmc.0.fan.N.targetspeed instead of automatic thermal control. When
set to auto mode (0), the SMC controls fan speed automatically.
The FS! key was already defined in asmcvar.h but not accessible.
This exposes it for debugging, testing, and advanced fan control.
Implementation uses read-modify-write to allow independent control of
each fan without affecting others.
Reviewed by: adrian, markj
MFC after: 2 weeks
Differential Revision: https://reviews.freebsd.org/D54437
---
sys/dev/asmc/asmc.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 55 insertions(+)
diff --git a/sys/dev/asmc/asmc.c b/sys/dev/asmc/asmc.c
index bdb8ca7831c4..0572c8e663a7 100644
--- a/sys/dev/asmc/asmc.c
+++ b/sys/dev/asmc/asmc.c
@@ -97,6 +97,7 @@ static int asmc_mb_sysctl_fansafespeed(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_fanminspeed(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_fanmaxspeed(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS);
+static int asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS);
static int asmc_temp_sysctl(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_sms_x(SYSCTL_HANDLER_ARGS);
static int asmc_mb_sysctl_sms_y(SYSCTL_HANDLER_ARGS);
@@ -656,6 +657,13 @@ asmc_attach(device_t dev)
CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
dev, j, model->smc_fan_targetspeed, "I",
"Fan target speed in RPM");
+
+ SYSCTL_ADD_PROC(sysctlctx,
+ SYSCTL_CHILDREN(sc->sc_fan_tree[i]),
+ OID_AUTO, "manual",
+ CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT,
+ dev, j, asmc_mb_sysctl_fanmanual, "I",
+ "Fan manual mode (0=auto, 1=manual)");
}
/*
@@ -1347,6 +1355,53 @@ asmc_mb_sysctl_fantargetspeed(SYSCTL_HANDLER_ARGS)
return (error);
}
+static int
+asmc_mb_sysctl_fanmanual(SYSCTL_HANDLER_ARGS)
+{
+ device_t dev = (device_t) arg1;
+ int fan = arg2;
+ int error;
+ int32_t v;
+ uint8_t buf[2];
+ uint16_t val;
+
+ /* Read current FS! bitmask (asmc_key_read locks internally) */
+ error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf));
+ if (error != 0)
+ return (error);
+
+ /* Extract manual bit for this fan (big-endian) */
+ val = (buf[0] << 8) | buf[1];
+ v = (val >> fan) & 0x01;
+
+ /* Let sysctl handle the value */
+ error = sysctl_handle_int(oidp, &v, 0, req);
+
+ if (error == 0 && req->newptr != NULL) {
+ /* Validate input (0 = auto, 1 = manual) */
+ if (v != 0 && v != 1)
+ return (EINVAL);
+ /* Read-modify-write of FS! bitmask */
+ error = asmc_key_read(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf));
+ if (error == 0) {
+ val = (buf[0] << 8) | buf[1];
+
+ /* Modify single bit */
+ if (v)
+ val |= (1 << fan); /* Set to manual */
+ else
+ val &= ~(1 << fan); /* Set to auto */
+
+ /* Write back */
+ buf[0] = val >> 8;
+ buf[1] = val & 0xff;
+ error = asmc_key_write(dev, ASMC_KEY_FANMANUAL, buf, sizeof(buf));
+ }
+ }
+
+ return (error);
+}
+
/*
* Temperature functions.
*/