On Thu, May 19, 2016 at 09:43:21PM +0200, Marcus Glocker wrote:
> This diff adds fan management support for the macppc smu(4), ported
> from FreeBSD. Some test reports would be welcome by putting load
> on your system and watch the fans scale (hopefully). On my G5 it
> works fine so far.
>
> Later on more I2C temp. sensors could be added like, maxds(4) and
> lmtemp(4).
Ooops, we must check if we're already running one level above since
we don't want kthread_create_deferred() to be called two times.
Fixed version.
Index: sys/arch/macppc/conf/files.macppc
===================================================================
RCS file: /cvs/src/sys/arch/macppc/conf/files.macppc,v
retrieving revision 1.86
diff -u -p -u -p -r1.86 files.macppc
--- sys/arch/macppc/conf/files.macppc 5 Mar 2016 17:41:55 -0000 1.86
+++ sys/arch/macppc/conf/files.macppc 20 May 2016 06:16:45 -0000
@@ -29,6 +29,7 @@ include "dev/mii/files.mii"
# MAC generic
#
file arch/macppc/dev/dbdma.c
+file arch/macppc/dev/thermal.c
#
# Openfirmware support
Index: sys/arch/macppc/dev/thermal.c
===================================================================
RCS file: sys/arch/macppc/dev/thermal.c
diff -N sys/arch/macppc/dev/thermal.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/arch/macppc/dev/thermal.c 20 May 2016 06:16:45 -0000
@@ -0,0 +1,221 @@
+/* $ OpenBSD $ */
+
+/*-
+ * Copyright (c) 2009-2011 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+
+#include <sys/malloc.h>
+#include <sys/reboot.h>
+#include <sys/sensors.h>
+#include <sys/kthread.h>
+
+#include <macppc/dev/thermal.h>
+
+/* A 10 second timer for spinning down fans. */
+#define FAN_HYSTERESIS_TIMER 10
+
+void thermal_thread_init(void);
+void thermal_thread_create(void *);
+void thermal_thread_loop(void *);
+void thermal_manage_fans(void);
+
+int enable_thermal = 0;
+
+struct thermal_fan_le {
+ struct thermal_fan *fan;
+ int last_val;
+ int timer;
+ SLIST_ENTRY(thermal_fan_le) entries;
+};
+struct thermal_sens_le {
+ struct thermal_temp *sensor;
+ int last_val;
+#define MAX_CRITICAL_COUNT 6
+ int critical_count;
+ SLIST_ENTRY(thermal_sens_le) entries;
+};
+
+SLIST_HEAD(thermal_fans, thermal_fan_le) fans =
+ SLIST_HEAD_INITIALIZER(fans);
+SLIST_HEAD(thermal_sensors, thermal_sens_le) sensors =
+ SLIST_HEAD_INITIALIZER(sensors);
+
+void
+thermal_thread_init(void)
+{
+ if (enable_thermal)
+ return; /* we're already running */
+ enable_thermal = 1;
+
+ kthread_create_deferred(thermal_thread_create, &enable_thermal);
+}
+
+void
+thermal_thread_create(void *arg)
+{
+ if (kthread_create(thermal_thread_loop, &enable_thermal, NULL,
+ "thermal")) {
+ printf("thermal kernel thread can't be created!\n");
+ enable_thermal = 0;
+ }
+}
+
+void
+thermal_thread_loop(void *arg)
+{
+ while (enable_thermal) {
+ thermal_manage_fans();
+ tsleep(&enable_thermal, 0, "thermal", hz);
+ }
+ kthread_exit(0);
+}
+
+void
+thermal_manage_fans(void)
+{
+ struct thermal_sens_le *sensor;
+ struct thermal_fan_le *fan;
+ int average_excess, max_excess_zone, frac_excess;
+ int fan_speed;
+ int nsens, nsens_zone;
+ int temp;
+
+ /* Read all the sensors */
+ SLIST_FOREACH(sensor, &sensors, entries) {
+ temp = sensor->sensor->read(sensor->sensor);
+ if (temp > 0) /* Use the previous temp in case of error */
+ sensor->last_val = temp;
+
+ if (sensor->last_val > sensor->sensor->max_temp) {
+ sensor->critical_count++;
+ printf("WARNING: Current temperature (%s: %d.%d C) "
+ "exceeds critical temperature (%d.%d C); "
+ "count=%d\n",
+ sensor->sensor->name,
+ (sensor->last_val - ZERO_C_TO_MUK)/1000000,
+ (sensor->last_val - ZERO_C_TO_MUK)%1000000,
+ (sensor->sensor->max_temp - ZERO_C_TO_MUK)/1000000,
+ (sensor->sensor->max_temp - ZERO_C_TO_MUK)%1000000,
+ sensor->critical_count);
+ if (sensor->critical_count >= MAX_CRITICAL_COUNT) {
+ printf("WARNING: %s temperature exceeded "
+ "critical temperature %d times in a row; "
+ "shutting down!\n",
+ sensor->sensor->name,
+ sensor->critical_count);
+ boot(RB_POWERDOWN);
+ }
+ } else {
+ if (sensor->critical_count > 0)
+ sensor->critical_count--;
+ }
+ }
+
+ /* Set all the fans */
+ SLIST_FOREACH(fan, &fans, entries) {
+ nsens = nsens_zone = 0;
+ average_excess = max_excess_zone = 0;
+ SLIST_FOREACH(sensor, &sensors, entries) {
+ temp = imin(sensor->last_val,
+ sensor->sensor->max_temp);
+ frac_excess = (temp -
+ sensor->sensor->target_temp)*100 /
+ (sensor->sensor->max_temp - temp + 1);
+ if (frac_excess < 0)
+ frac_excess = 0;
+ if (sensor->sensor->zone == fan->fan->zone) {
+ max_excess_zone = imax(max_excess_zone,
+ frac_excess);
+ nsens_zone++;
+ }
+ average_excess += frac_excess;
+ nsens++;
+ }
+ average_excess /= nsens;
+
+ /* If there are no sensors in this zone, use the average */
+ if (nsens_zone == 0)
+ max_excess_zone = average_excess;
+ /* No sensors at all? Use default */
+ if (nsens == 0) {
+ fan->fan->set(fan->fan, fan->fan->default_rpm);
+ continue;
+ }
+
+ /*
+ * Scale the fan linearly in the max temperature in its
+ * thermal zone.
+ */
+ max_excess_zone = imin(max_excess_zone, 100);
+ fan_speed = max_excess_zone *
+ (fan->fan->max_rpm - fan->fan->min_rpm)/100 +
+ fan->fan->min_rpm;
+ if (fan_speed >= fan->last_val) {
+ fan->timer = FAN_HYSTERESIS_TIMER;
+ fan->last_val = fan_speed;
+ } else {
+ fan->timer--;
+ if (fan->timer == 0) {
+ fan->last_val = fan_speed;
+ fan->timer = FAN_HYSTERESIS_TIMER;
+ }
+ }
+ fan->fan->set(fan->fan, fan->last_val);
+ }
+}
+
+void
+thermal_fan_register(struct thermal_fan *fan)
+{
+ struct thermal_fan_le *list_entry;
+
+ thermal_thread_init(); /* first caller inits our thread */
+
+ list_entry = malloc(sizeof(struct thermal_fan_le), M_DEVBUF,
+ M_ZERO | M_WAITOK);
+ list_entry->fan = fan;
+
+ SLIST_INSERT_HEAD(&fans, list_entry, entries);
+}
+
+void
+thermal_sensor_register(struct thermal_temp *sensor)
+{
+ struct thermal_sens_le *list_entry;
+
+ thermal_thread_init(); /* first caller inits our thread */
+
+ list_entry = malloc(sizeof(struct thermal_sens_le), M_DEVBUF,
+ M_ZERO | M_WAITOK);
+ list_entry->sensor = sensor;
+ list_entry->last_val = 0;
+ list_entry->critical_count = 0;
+
+ SLIST_INSERT_HEAD(&sensors, list_entry, entries);
+}
Index: sys/arch/macppc/dev/thermal.h
===================================================================
RCS file: sys/arch/macppc/dev/thermal.h
diff -N sys/arch/macppc/dev/thermal.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/arch/macppc/dev/thermal.h 20 May 2016 06:16:45 -0000
@@ -0,0 +1,51 @@
+/* $OpenBSD$ */
+
+/*-
+ * Copyright (c) 2009-2011 Nathan Whitehorn
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define ZERO_C_TO_MUK 273150000
+
+struct thermal_fan {
+ int min_rpm, max_rpm, default_rpm;
+
+ char name[32];
+ int zone;
+
+ int (*read)(struct thermal_fan *);
+ int (*set)(struct thermal_fan *, int value);
+};
+
+struct thermal_temp {
+ int target_temp, max_temp; /* muK */
+
+ char name[32];
+ int zone;
+
+ int (*read)(struct thermal_temp *);
+};
+
+void thermal_fan_register(struct thermal_fan *);
+void thermal_sensor_register(struct thermal_temp *);