summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcus Glocker <mglocker@cvs.openbsd.org>2016-05-20 21:45:05 +0000
committerMarcus Glocker <mglocker@cvs.openbsd.org>2016-05-20 21:45:05 +0000
commit5e340ff2e04f2212ad351e4c5eeb6243520ea31e (patch)
tree655711948f591da85926c6b7e4dfc47da09e6afe
parent574da5d85a3d32498ce10860f4dec19f7c8ecaff (diff)
Add a thermal management framework for macppc which controls the fan speed
based on the temperature sensor values. A driver can register it fans or temperature sensors there. Ported from FreeBSD. ok deraadt kettenis
-rw-r--r--sys/arch/macppc/conf/files.macppc3
-rw-r--r--sys/arch/macppc/dev/thermal.c221
-rw-r--r--sys/arch/macppc/dev/thermal.h51
3 files changed, 274 insertions, 1 deletions
diff --git a/sys/arch/macppc/conf/files.macppc b/sys/arch/macppc/conf/files.macppc
index af17c4a7adc..de66b63c4fe 100644
--- a/sys/arch/macppc/conf/files.macppc
+++ b/sys/arch/macppc/conf/files.macppc
@@ -1,4 +1,4 @@
-# $OpenBSD: files.macppc,v 1.86 2016/03/05 17:41:55 mpi Exp $
+# $OpenBSD: files.macppc,v 1.87 2016/05/20 21:45:04 mglocker Exp $
#
# macppc-specific configuration info
@@ -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
diff --git a/sys/arch/macppc/dev/thermal.c b/sys/arch/macppc/dev/thermal.c
new file mode 100644
index 00000000000..179d7c99b02
--- /dev/null
+++ b/sys/arch/macppc/dev/thermal.c
@@ -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 thermal_enable = 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 (thermal_enable)
+ return; /* we're already running */
+ thermal_enable = 1;
+
+ kthread_create_deferred(thermal_thread_create, &thermal_enable);
+}
+
+void
+thermal_thread_create(void *arg)
+{
+ if (kthread_create(thermal_thread_loop, &thermal_enable, NULL,
+ "thermal")) {
+ printf("thermal kernel thread can't be created!\n");
+ thermal_enable = 0;
+ }
+}
+
+void
+thermal_thread_loop(void *arg)
+{
+ while (thermal_enable) {
+ thermal_manage_fans();
+ tsleep(&thermal_enable, 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);
+}
diff --git a/sys/arch/macppc/dev/thermal.h b/sys/arch/macppc/dev/thermal.h
new file mode 100644
index 00000000000..ac512d0d8d5
--- /dev/null
+++ b/sys/arch/macppc/dev/thermal.h
@@ -0,0 +1,51 @@
+/* $OpenBSD: thermal.h,v 1.1 2016/05/20 21:45:04 mglocker Exp $ */
+
+/*-
+ * 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 *);