1222429Snwhitehorn/*- 2222429Snwhitehorn * Copyright (c) 2009-2011 Nathan Whitehorn 3222429Snwhitehorn * All rights reserved. 4222429Snwhitehorn * 5222429Snwhitehorn * Redistribution and use in source and binary forms, with or without 6222429Snwhitehorn * modification, are permitted provided that the following conditions 7222429Snwhitehorn * are met: 8222429Snwhitehorn * 1. Redistributions of source code must retain the above copyright 9222429Snwhitehorn * notice, this list of conditions and the following disclaimer. 10222429Snwhitehorn * 2. Redistributions in binary form must reproduce the above copyright 11222429Snwhitehorn * notice, this list of conditions and the following disclaimer in the 12222429Snwhitehorn * documentation and/or other materials provided with the distribution. 13222429Snwhitehorn * 14222429Snwhitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15222429Snwhitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16222429Snwhitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17222429Snwhitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18222429Snwhitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19222429Snwhitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20222429Snwhitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21222429Snwhitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22222429Snwhitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23222429Snwhitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24222429Snwhitehorn * SUCH DAMAGE. 25222429Snwhitehorn */ 26222429Snwhitehorn 27222429Snwhitehorn#include <sys/cdefs.h> 28222429Snwhitehorn__FBSDID("$FreeBSD$"); 29222429Snwhitehorn 30222429Snwhitehorn#include <sys/param.h> 31222429Snwhitehorn#include <sys/kernel.h> 32222429Snwhitehorn#include <sys/lock.h> 33222429Snwhitehorn#include <sys/mutex.h> 34222429Snwhitehorn#include <sys/systm.h> 35222429Snwhitehorn 36222429Snwhitehorn#include <sys/types.h> 37222429Snwhitehorn#include <sys/kthread.h> 38222429Snwhitehorn#include <sys/malloc.h> 39222429Snwhitehorn#include <sys/reboot.h> 40222429Snwhitehorn#include <sys/sysctl.h> 41222429Snwhitehorn#include <sys/queue.h> 42222429Snwhitehorn 43222429Snwhitehorn#include "powermac_thermal.h" 44222429Snwhitehorn 45279045Sjhibbits/* A 10 second timer for spinning down fans. */ 46279045Sjhibbits#define FAN_HYSTERESIS_TIMER 10 47279045Sjhibbits 48222429Snwhitehornstatic void fan_management_proc(void); 49222429Snwhitehornstatic void pmac_therm_manage_fans(void); 50222429Snwhitehorn 51222429Snwhitehornstatic struct proc *pmac_them_proc; 52222429Snwhitehornstatic int enable_pmac_thermal = 1; 53222429Snwhitehorn 54222429Snwhitehornstatic struct kproc_desc pmac_therm_kp = { 55222429Snwhitehorn "pmac_thermal", 56222429Snwhitehorn fan_management_proc, 57222429Snwhitehorn &pmac_them_proc 58222429Snwhitehorn}; 59222429Snwhitehorn 60222429SnwhitehornSYSINIT(pmac_therm_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, 61222429Snwhitehorn &pmac_therm_kp); 62222429SnwhitehornSYSCTL_INT(_machdep, OID_AUTO, manage_fans, CTLFLAG_RW | CTLFLAG_TUN, 63222429Snwhitehorn &enable_pmac_thermal, 1, "Enable automatic fan management"); 64227293Sedstatic MALLOC_DEFINE(M_PMACTHERM, "pmactherm", "Powermac Thermal Management"); 65222429Snwhitehorn 66222429Snwhitehornstruct pmac_fan_le { 67222429Snwhitehorn struct pmac_fan *fan; 68222429Snwhitehorn int last_val; 69279045Sjhibbits int timer; 70222429Snwhitehorn SLIST_ENTRY(pmac_fan_le) entries; 71222429Snwhitehorn}; 72222429Snwhitehornstruct pmac_sens_le { 73222429Snwhitehorn struct pmac_therm *sensor; 74222429Snwhitehorn int last_val; 75257093Snwhitehorn#define MAX_CRITICAL_COUNT 6 76257093Snwhitehorn int critical_count; 77222429Snwhitehorn SLIST_ENTRY(pmac_sens_le) entries; 78222429Snwhitehorn}; 79222429Snwhitehornstatic SLIST_HEAD(pmac_fans, pmac_fan_le) fans = SLIST_HEAD_INITIALIZER(fans); 80222429Snwhitehornstatic SLIST_HEAD(pmac_sensors, pmac_sens_le) sensors = 81222429Snwhitehorn SLIST_HEAD_INITIALIZER(sensors); 82222429Snwhitehorn 83222429Snwhitehornstatic void 84222429Snwhitehornfan_management_proc(void) 85222429Snwhitehorn{ 86222429Snwhitehorn /* Nothing to manage? */ 87222429Snwhitehorn if (SLIST_EMPTY(&fans)) 88222469Snwhitehorn kproc_exit(0); 89222429Snwhitehorn 90222429Snwhitehorn while (1) { 91222429Snwhitehorn pmac_therm_manage_fans(); 92222429Snwhitehorn pause("pmac_therm", hz); 93222429Snwhitehorn } 94222429Snwhitehorn} 95222429Snwhitehorn 96222429Snwhitehornstatic void 97222429Snwhitehornpmac_therm_manage_fans(void) 98222429Snwhitehorn{ 99222429Snwhitehorn struct pmac_sens_le *sensor; 100222429Snwhitehorn struct pmac_fan_le *fan; 101222429Snwhitehorn int average_excess, max_excess_zone, frac_excess; 102279045Sjhibbits int fan_speed; 103222429Snwhitehorn int nsens, nsens_zone; 104222463Snwhitehorn int temp; 105222429Snwhitehorn 106222429Snwhitehorn if (!enable_pmac_thermal) 107222429Snwhitehorn return; 108222429Snwhitehorn 109222429Snwhitehorn /* Read all the sensors */ 110222429Snwhitehorn SLIST_FOREACH(sensor, &sensors, entries) { 111222463Snwhitehorn temp = sensor->sensor->read(sensor->sensor); 112222463Snwhitehorn if (temp > 0) /* Use the previous temp in case of error */ 113222463Snwhitehorn sensor->last_val = temp; 114222463Snwhitehorn 115222429Snwhitehorn if (sensor->last_val > sensor->sensor->max_temp) { 116257093Snwhitehorn sensor->critical_count++; 117222429Snwhitehorn printf("WARNING: Current temperature (%s: %d.%d C) " 118257093Snwhitehorn "exceeds critical temperature (%d.%d C); " 119257093Snwhitehorn "count=%d\n", 120257093Snwhitehorn sensor->sensor->name, 121257093Snwhitehorn (sensor->last_val - ZERO_C_TO_K) / 10, 122257093Snwhitehorn (sensor->last_val - ZERO_C_TO_K) % 10, 123257093Snwhitehorn (sensor->sensor->max_temp - ZERO_C_TO_K) / 10, 124257093Snwhitehorn (sensor->sensor->max_temp - ZERO_C_TO_K) % 10, 125257093Snwhitehorn sensor->critical_count); 126257093Snwhitehorn if (sensor->critical_count >= MAX_CRITICAL_COUNT) { 127257093Snwhitehorn printf("WARNING: %s temperature exceeded " 128257093Snwhitehorn "critical temperature %d times in a row; " 129257093Snwhitehorn "shutting down!\n", 130257093Snwhitehorn sensor->sensor->name, 131257093Snwhitehorn sensor->critical_count); 132257093Snwhitehorn shutdown_nice(RB_POWEROFF); 133257093Snwhitehorn } 134257093Snwhitehorn } else { 135257093Snwhitehorn if (sensor->critical_count > 0) 136257093Snwhitehorn sensor->critical_count--; 137222429Snwhitehorn } 138222429Snwhitehorn } 139222429Snwhitehorn 140222429Snwhitehorn /* Set all the fans */ 141222429Snwhitehorn SLIST_FOREACH(fan, &fans, entries) { 142222429Snwhitehorn nsens = nsens_zone = 0; 143222429Snwhitehorn average_excess = max_excess_zone = 0; 144222429Snwhitehorn SLIST_FOREACH(sensor, &sensors, entries) { 145279045Sjhibbits temp = imin(sensor->last_val, 146279045Sjhibbits sensor->sensor->max_temp); 147279045Sjhibbits frac_excess = (temp - 148222429Snwhitehorn sensor->sensor->target_temp)*100 / 149279045Sjhibbits (sensor->sensor->max_temp - temp + 1); 150222460Snwhitehorn if (frac_excess < 0) 151222460Snwhitehorn frac_excess = 0; 152222429Snwhitehorn if (sensor->sensor->zone == fan->fan->zone) { 153222429Snwhitehorn max_excess_zone = imax(max_excess_zone, 154222429Snwhitehorn frac_excess); 155222429Snwhitehorn nsens_zone++; 156222429Snwhitehorn } 157222429Snwhitehorn average_excess += frac_excess; 158222429Snwhitehorn nsens++; 159222429Snwhitehorn } 160222429Snwhitehorn average_excess /= nsens; 161222429Snwhitehorn 162222429Snwhitehorn /* If there are no sensors in this zone, use the average */ 163222429Snwhitehorn if (nsens_zone == 0) 164222429Snwhitehorn max_excess_zone = average_excess; 165222429Snwhitehorn /* No sensors at all? Use default */ 166222429Snwhitehorn if (nsens == 0) { 167222429Snwhitehorn fan->fan->set(fan->fan, fan->fan->default_rpm); 168222429Snwhitehorn continue; 169222429Snwhitehorn } 170222429Snwhitehorn 171222429Snwhitehorn /* 172222429Snwhitehorn * Scale the fan linearly in the max temperature in its 173222429Snwhitehorn * thermal zone. 174222429Snwhitehorn */ 175279045Sjhibbits max_excess_zone = imin(max_excess_zone, 100); 176279045Sjhibbits fan_speed = max_excess_zone * 177222429Snwhitehorn (fan->fan->max_rpm - fan->fan->min_rpm)/100 + 178279045Sjhibbits fan->fan->min_rpm; 179279045Sjhibbits if (fan_speed >= fan->last_val) { 180279045Sjhibbits fan->timer = FAN_HYSTERESIS_TIMER; 181279045Sjhibbits fan->last_val = fan_speed; 182279045Sjhibbits } else { 183279045Sjhibbits fan->timer--; 184279045Sjhibbits if (fan->timer == 0) { 185279045Sjhibbits fan->last_val = fan_speed; 186279045Sjhibbits fan->timer = FAN_HYSTERESIS_TIMER; 187279045Sjhibbits } 188279045Sjhibbits } 189279045Sjhibbits fan->fan->set(fan->fan, fan->last_val); 190222429Snwhitehorn } 191222429Snwhitehorn} 192222429Snwhitehorn 193222429Snwhitehornvoid 194222429Snwhitehornpmac_thermal_fan_register(struct pmac_fan *fan) 195222429Snwhitehorn{ 196222429Snwhitehorn struct pmac_fan_le *list_entry; 197222429Snwhitehorn 198222429Snwhitehorn list_entry = malloc(sizeof(struct pmac_fan_le), M_PMACTHERM, 199222429Snwhitehorn M_ZERO | M_WAITOK); 200222429Snwhitehorn list_entry->fan = fan; 201222429Snwhitehorn 202222429Snwhitehorn SLIST_INSERT_HEAD(&fans, list_entry, entries); 203222429Snwhitehorn} 204222429Snwhitehorn 205222429Snwhitehornvoid 206222429Snwhitehornpmac_thermal_sensor_register(struct pmac_therm *sensor) 207222429Snwhitehorn{ 208222429Snwhitehorn struct pmac_sens_le *list_entry; 209222429Snwhitehorn 210222429Snwhitehorn list_entry = malloc(sizeof(struct pmac_sens_le), M_PMACTHERM, 211222429Snwhitehorn M_ZERO | M_WAITOK); 212222429Snwhitehorn list_entry->sensor = sensor; 213257093Snwhitehorn list_entry->last_val = 0; 214257093Snwhitehorn list_entry->critical_count = 0; 215222429Snwhitehorn 216222429Snwhitehorn SLIST_INSERT_HEAD(&sensors, list_entry, entries); 217222429Snwhitehorn} 218222429Snwhitehorn 219