Module Name:    src
Committed By:   macallan
Date:           Fri Sep 29 22:38:18 UTC 2017

Modified Files:
        src/sys/arch/macppc/dev: smu.c

Log Message:
provide crude but working fan control based on sensor readings


To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/sys/arch/macppc/dev/smu.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/macppc/dev/smu.c
diff -u src/sys/arch/macppc/dev/smu.c:1.1 src/sys/arch/macppc/dev/smu.c:1.2
--- src/sys/arch/macppc/dev/smu.c:1.1	Tue Jun  6 15:58:17 2017
+++ src/sys/arch/macppc/dev/smu.c	Fri Sep 29 22:38:18 2017
@@ -34,6 +34,7 @@
 #include <sys/time.h>
 #include <sys/reboot.h>
 #include <sys/sysctl.h>
+#include <sys/kthread.h>
 
 #include <machine/autoconf.h>
 
@@ -81,12 +82,29 @@ struct smu_iicbus {
 #define SMU_MAX_IICBUS		3
 #define SMU_MAX_SME_SENSORS	SMU_MAX_FANS
 
+struct smu_zone {
+	bool (*filter)(const envsys_data_t *);
+	int nfans;
+	int fans[SMU_MAX_FANS];
+	int threshold, step;
+	int duty;
+};
+
+
+#define SMU_ZONE_CPUS	0
+#define SMU_ZONE_DRIVES	1
+#define SMU_ZONE_SLOTS	2
+#define SMU_ZONES	3
+
+#define C_TO_uK(n) (n * 1000000 + 273150000)
+
 struct smu_softc {
 	device_t sc_dev;
 	int sc_node;
 	struct sysctlnode *sc_sysctl_me;
 
 	kmutex_t sc_cmd_lock;
+	kmutex_t sc_msg_lock;
 	struct smu_cmd *sc_cmd;
 	paddr_t sc_cmd_paddr;
 	int sc_dbell_mbox;
@@ -103,6 +121,10 @@ struct smu_softc {
 
 	struct sysmon_envsys *sc_sme;
 	envsys_data_t sc_sme_sensors[SMU_MAX_SME_SENSORS];
+
+	struct smu_zone sc_zones[SMU_ZONES];
+	lwp_t *sc_thread;
+	bool sc_dying;
 };
 
 #define SMU_CMD_FAN	0x4a
@@ -137,6 +159,13 @@ static int smu_iicbus_exec(void *, i2c_o
     size_t, void *, size_t, int);
 static int smu_sysctl_fan_rpm(SYSCTLFN_ARGS);
 
+static void smu_setup_zones(struct smu_softc *);
+static void smu_adjust_zone(struct smu_softc *, int);
+static void smu_adjust(void *);
+static bool is_cpu_sensor(const envsys_data_t *);
+static bool is_drive_sensor(const envsys_data_t *);
+static bool is_slots_sensor(const envsys_data_t *);
+
 CFATTACH_DECL_NEW(smu, sizeof(struct smu_softc),
     smu_match, smu_attach, NULL, NULL);
 
@@ -187,6 +216,7 @@ smu_attach(device_t parent, device_t sel
 		smu0 = sc;
 
 	printf("\n");
+	smu_setup_zones(sc);
 }
 
 static int
@@ -498,7 +528,7 @@ smu_sme_refresh(struct sysmon_envsys *sm
 static int
 smu_do_cmd(struct smu_softc *sc, struct smu_cmd *cmd, int timo)
 {
-	int gpio, ret;
+	int gpio, ret, bail;
 	u_char ack;
 
 	mutex_enter(&sc->sc_cmd_lock);
@@ -517,15 +547,23 @@ smu_do_cmd(struct smu_softc *sc, struct 
 	obio_write_4(sc->sc_dbell_mbox, sc->sc_cmd_paddr);
 	obio_write_1(sc->sc_dbell_gpio, 0x04);
 
-	do {
-		ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", (timo * hz) / 1000);
+	bail = 0;
+
+	gpio = obio_read_1(sc->sc_dbell_gpio);
+
+	while (((gpio & 0x07) != 0x07) && (bail < timo)) {
+		ret = tsleep(sc->sc_cmd, PWAIT, "smu_cmd", mstohz(10));
 		if (ret != 0) {
-			mutex_exit(&sc->sc_cmd_lock);
-			return (ret);
+			bail++;
 		}
 		gpio = obio_read_1(sc->sc_dbell_gpio);
-	} while ((gpio & 0x07) != 0x07);
+	}
 
+	if ((gpio & 0x07) != 0x07) {
+		mutex_exit(&sc->sc_cmd_lock);
+		return EWOULDBLOCK;
+	}
+		
 	__asm volatile ("dcbf 0,%0; sync" :: "r"(sc->sc_cmd) : "memory");
 
 	ack = (~cmd->cmd) & 0xff;
@@ -825,3 +863,149 @@ SYSCTL_SETUP(smu_sysctl_setup, "SMU sysc
 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
 }
+
+static void
+smu_setup_zones(struct smu_softc *sc)
+{
+	struct smu_zone *z;
+	struct smu_fan *f;
+	int i;
+
+	/* find CPU fans */
+	z = &sc->sc_zones[SMU_ZONE_CPUS];
+	z->nfans = 0;
+	for (i = 0; i < SMU_MAX_FANS; i++) {
+		f = &sc->sc_fans[i];
+		if (strstr(f->location, "CPU") != NULL) {
+			z->fans[z->nfans] = i;
+			z->nfans++;
+		}
+	}
+	printf("using %d fans for CPU zone\n", z->nfans);
+	z->threshold = C_TO_uK(45);
+	z->duty = 150;
+	z->step = 3;	
+	z->filter = is_cpu_sensor;
+
+	z = &sc->sc_zones[SMU_ZONE_DRIVES];
+	z->nfans = 0;
+	for (i = 0; i < SMU_MAX_FANS; i++) {
+		f = &sc->sc_fans[i];
+		if (strstr(f->location, "DRIVE") != NULL) {
+			z->fans[z->nfans] = i;
+			z->nfans++;
+		}
+	}
+	printf("using %d fans for drive bay zone\n", z->nfans);
+	z->threshold = C_TO_uK(40);
+	z->duty = 150;
+	z->step = 2;
+	z->filter = is_drive_sensor;
+
+	z = &sc->sc_zones[SMU_ZONE_SLOTS];
+	z->nfans = 0;
+	for (i = 0; i < SMU_MAX_FANS; i++) {
+		f = &sc->sc_fans[i];
+		if ((strstr(f->location, "BACKSIDE") != NULL) ||
+		    (strstr(f->location, "SLOTS") != NULL)) {
+			z->fans[z->nfans] = i;
+			z->nfans++;
+		}
+	}
+	printf("using %d fans for expansion slots zone\n", z->nfans);
+	z->threshold = C_TO_uK(40);
+	z->duty = 150;
+	z->step = 2;
+	z->filter = is_slots_sensor;
+
+	sc->sc_dying = false;
+	kthread_create(PRI_NONE, 0, curcpu(), smu_adjust, sc, &sc->sc_thread,
+	    "fan control"); 
+}
+
+static void
+smu_adjust_zone(struct smu_softc *sc, int which)
+{
+	struct smu_zone *z = &sc->sc_zones[which];
+	struct smu_fan *f;
+	long temp, newduty, i, speed, diff;
+
+	DPRINTF("%s %d\n", __func__, which);
+
+	temp = sysmon_envsys_get_max_value(z->filter, true);
+	if (temp == 0) {
+		/* no sensor data - leave fan alone */
+		DPRINTF("nodata\n");
+		return;
+	}
+	DPRINTF("temp %ld ", (temp - 273150000) / 1000000);
+	diff = ((temp - z->threshold) / 1000000) * z->step;
+
+	if (diff < 0) newduty = 0;
+	else if (diff > 100) newduty = 100;
+	else newduty = diff;
+
+	DPRINTF("newduty %ld diff %ld \n", newduty, diff);
+	if (newduty == z->duty) {
+		DPRINTF("no change\n");
+		return;
+	}
+	z->duty = newduty;
+	/* now adjust each fan to the new duty cycle */
+	for (i = 0; i < z->nfans; i++) {
+		f = &sc->sc_fans[z->fans[i]];
+		speed = f->min_rpm + ((f->max_rpm - f->min_rpm) * newduty) / 100;
+		DPRINTF("fan %d speed %ld ", z->fans[i], speed);
+		smu_fan_set_rpm(f, speed);
+	}
+	DPRINTF("\n");
+}
+
+static void
+smu_adjust(void *cookie)
+{
+	struct smu_softc *sc = cookie;
+	int i;
+
+	while (!sc->sc_dying) {
+		for (i = 0; i < SMU_ZONES; i++)
+			smu_adjust_zone(sc, i);
+		kpause("fanctrl", true, mstohz(30000), NULL);
+	}
+	kthread_exit(0);
+}
+
+static bool is_cpu_sensor(const envsys_data_t *edata)
+{
+	if (edata->units != ENVSYS_STEMP)
+		return false;
+	if ((strstr(edata->desc, "CPU") != NULL) &&
+	    (strstr(edata->desc, "DIODE") != NULL))
+		return TRUE;
+	if (strstr(edata->desc, "TUNNEL") != NULL)
+		return TRUE;
+	return false;
+}
+
+static bool is_drive_sensor(const envsys_data_t *edata)
+{
+	if (edata->units != ENVSYS_STEMP)
+		return false;
+	if (strstr(edata->desc, "DRIVE BAY") != NULL)
+		return TRUE;
+	/* XXX until we support the actual drive bay sensor */
+	if (strstr(edata->desc, "BACKSIDE") != NULL)
+		return TRUE;
+	return false;
+}
+
+static bool is_slots_sensor(const envsys_data_t *edata)
+{
+	if (edata->units != ENVSYS_STEMP)
+		return false;
+	if (strstr(edata->desc, "BACKSIDE") != NULL)
+		return TRUE;
+	if (strstr(edata->desc, "INLET") != NULL)
+		return TRUE;
+	return false;
+}

Reply via email to