I've recently noticed that two of five fans in my G5 don't spin up.
That's because smu(4) currently just supports RPM fans.
The attached diff adds initial support for PWM fans as well.

sysctl before:
# sysctl -a | grep fan
hw.sensors.smu0.fan0=994 RPM (Rear Fan 0)
hw.sensors.smu0.fan1=994 RPM (Rear fan 1)
hw.sensors.smu0.fan2=994 RPM (Front Fan)

sysctl after:
# sysctl -a | grep fan
hw.sensors.smu0.fan0=994 RPM (Rear Fan 0)
hw.sensors.smu0.fan1=994 RPM (Rear fan 1)
hw.sensors.smu0.fan2=994 RPM (Front Fan)
hw.sensors.smu0.fan3=589 RPM (Slots Fan)
hw.sensors.smu0.fan4=589 RPM (Drive Bay)

I was first thinking to introduce a new sensor type in sys/sensor.h
called SENSOR_FANPWM, but finally I think it's more intuitive to
display the RPM value for all fans in general.

In case this makes it in I would like to split the RPM read which
is currently done in smu_fan_refresh() in a own function as next,
same as for the PWM read.  Also with the background that there seems
to be another set/read method for new style fans which may fail
currently which we could implement.


Index: sys/arch/macppc/dev/smu.c
===================================================================
RCS file: /cvs/src/sys/arch/macppc/dev/smu.c,v
retrieving revision 1.28
diff -u -p -u -p -r1.28 smu.c
--- sys/arch/macppc/dev/smu.c   4 May 2016 08:20:58 -0000       1.28
+++ sys/arch/macppc/dev/smu.c   10 May 2016 21:26:39 -0000
@@ -41,6 +41,13 @@ void    smu_attach(struct device *, stru
 
 struct smu_fan {
        u_int8_t        reg;
+       enum {
+                       SMU_SENSOR_FANRPM,
+                       SMU_SENSOR_FANPWM
+       } type;
+       u_int16_t       min_pwm;
+       u_int16_t       max_pwm;
+       u_int16_t       unmanaged_pwm;
        u_int16_t       min_rpm;
        u_int16_t       max_rpm;
        u_int16_t       unmanaged_rpm;
@@ -144,6 +151,9 @@ int smu_time_read(time_t *);
 int    smu_time_write(time_t);
 int    smu_get_datablock(struct smu_softc *sc, u_int8_t, u_int8_t *, size_t);
 int    smu_fan_set_rpm(struct smu_softc *, struct smu_fan *, u_int16_t);
+int    smu_fan_set_pwm(struct smu_softc *, struct smu_fan *, u_int16_t);
+int    smu_fan_read_pwm(struct smu_softc *, struct smu_fan *, u_int16_t *,
+           u_int16_t *);
 int    smu_fan_refresh(struct smu_softc *, struct smu_fan *);
 int    smu_sensor_refresh(struct smu_softc *, struct smu_sensor *);
 void   smu_refresh_sensors(void *);
@@ -250,7 +260,7 @@ smu_attach(struct device *parent, struct
        time_read = smu_time_read;
        time_write = smu_time_write;
 
-       /* Fans */
+       /* RPM Fans */
        node = OF_getnodebyname(ca->ca_node, "rpm-fans");
        if (node == 0)
                node = OF_getnodebyname(ca->ca_node, "fans");
@@ -260,7 +270,7 @@ smu_attach(struct device *parent, struct
                        continue;
 
                if (strcmp(type, "fan-rpm-control") != 0) {
-                       printf(": unsupported fan type: %s\n", type);
+                       printf(": unsupported rpm-fan type: %s\n", type);
                        return;
                }
 
@@ -273,6 +283,7 @@ smu_attach(struct device *parent, struct
                fan->sensor.type = SENSOR_FANRPM;
                fan->sensor.flags = SENSOR_FINVALID;
                fan->reg = reg;
+               fan->type = SMU_SENSOR_FANRPM;
 
                if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
                        val = 0;
@@ -299,6 +310,54 @@ smu_attach(struct device *parent, struct
 #endif
        }
 
+       /* PWM Fans */
+       node = OF_getnodebyname(ca->ca_node, "pwm-fans");
+       for (node = OF_child(node); node; node = OF_peer(node)) {
+               if (OF_getprop(node, "reg", &reg, sizeof reg) <= 0 ||
+                   OF_getprop(node, "device_type", type, sizeof type) <= 0)
+                       continue;
+
+               if (strcmp(type, "fan-pwm-control") != 0) {
+                       printf(": unsupported pwm-fan type: %s\n", type);
+                       return;
+               }
+
+               if (sc->sc_num_fans >= SMU_MAXFANS) {
+                       printf(": too many fans\n");
+                       return;
+               }
+
+               fan = &sc->sc_fans[sc->sc_num_fans++];
+               fan->sensor.type = SENSOR_FANRPM;
+               fan->sensor.flags = SENSOR_FINVALID;
+               fan->reg = reg;
+               fan->type = SMU_SENSOR_FANPWM;
+
+               if (OF_getprop(node, "min-value", &val, sizeof val) <= 0)
+                       val = 0;
+               fan->min_pwm = val;
+               if (OF_getprop(node, "max-value", &val, sizeof val) <= 0)
+                       val = 0xffff;
+               fan->max_pwm = val;
+               if (OF_getprop(node, "unmanage-value", &val, sizeof val) > 0)
+                       fan->unmanaged_pwm = val;
+               else if (OF_getprop(node, "safe-value", &val, sizeof val) > 0)
+                       fan->unmanaged_pwm = val;
+               else
+                       fan->unmanaged_pwm = fan->min_pwm;
+
+               if (OF_getprop(node, "location", loc, sizeof loc) <= 0)
+                       strlcpy(loc, "Unknown", sizeof loc);
+               strlcpy(fan->sensor.desc, loc, sizeof sensor->sensor.desc);
+
+               /* Start running fans at their "unmanaged" speed. */
+               smu_fan_set_pwm(sc, fan, fan->unmanaged_pwm);
+
+#ifndef SMALL_KERNEL
+               sensor_attach(&sc->sc_sensordev, &fan->sensor);
+#endif
+       }
+
        /*
         * Bail out if we didn't find any fans.  If we don't set the
         * fans to a safe speed, but tickle the SMU periodically by
@@ -559,10 +618,66 @@ smu_fan_set_rpm(struct smu_softc *sc, st
 }
 
 int
+smu_fan_set_pwm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t pwm)
+{
+       struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
+
+       cmd->cmd = SMU_FAN;
+       cmd->len = 14;
+       cmd->data[0] = 0x10;    /* fan-pwm-control */
+       cmd->data[1] = 0x01 << fan->reg;
+       cmd->data[2] = cmd->data[2 + fan->reg * 2] = (pwm >> 8) & 0xff;
+       cmd->data[3] = cmd->data[3 + fan->reg * 2] = (pwm & 0xff);
+       return smu_do_cmd(sc, 800);
+}
+
+int
+smu_fan_read_pwm(struct smu_softc *sc, struct smu_fan *fan, u_int16_t *pwm,
+    u_int16_t *rpm)
+{
+       struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
+       int error;
+
+       /* read PWM value */
+       cmd->cmd = SMU_FAN;
+       cmd->len = 14;
+       cmd->data[0] = 0x12;
+       cmd->data[1] = 0x01 << fan->reg;
+       error = smu_do_cmd(sc, 800);
+       if (error)
+               return (error);
+       *pwm = cmd->data[fan->reg * 2 + 2];
+
+       /* read RPM value */
+       cmd->cmd = SMU_FAN;
+       cmd->len = 1;
+       cmd->data[0] = 0x11;
+       error = smu_do_cmd(sc, 800);
+       if (error)
+               return (error);
+       *rpm = (cmd->data[fan->reg * 2 + 1] << 8) | cmd->data[fan->reg * 2 + 2];
+
+       return (0);
+}
+
+int
 smu_fan_refresh(struct smu_softc *sc, struct smu_fan *fan)
 {
        struct smu_cmd *cmd = (struct smu_cmd *)sc->sc_cmd;
        int error;
+       u_int16_t rpm, pwm;
+
+       if (fan->type == SMU_SENSOR_FANPWM) {
+               error = smu_fan_read_pwm(sc, fan, &pwm, &rpm);
+               if (error) {
+                       fan->sensor.flags = SENSOR_FINVALID;
+                       return (error);
+               }
+               /* pass the RPM value to sysctl(8) */
+               fan->sensor.value = rpm;
+               fan->sensor.flags = 0;
+               return (0);
+       }
 
        cmd->cmd = SMU_FAN;
        cmd->len = 2;

Reply via email to