The branch main has been updated by markj:

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

commit 1ecac45cfc5f0886907c34ec0da2c1b32618665d
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-01-05 18:03:23 +0000
Commit:     Mark Johnston <[email protected]>
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.
  */

Reply via email to