184d77676SNathan Whitehorn /*- 284d77676SNathan Whitehorn * Copyright (c) 2009-2011 Nathan Whitehorn 384d77676SNathan Whitehorn * All rights reserved. 484d77676SNathan Whitehorn * 584d77676SNathan Whitehorn * Redistribution and use in source and binary forms, with or without 684d77676SNathan Whitehorn * modification, are permitted provided that the following conditions 784d77676SNathan Whitehorn * are met: 884d77676SNathan Whitehorn * 1. Redistributions of source code must retain the above copyright 984d77676SNathan Whitehorn * notice, this list of conditions and the following disclaimer. 1084d77676SNathan Whitehorn * 2. Redistributions in binary form must reproduce the above copyright 1184d77676SNathan Whitehorn * notice, this list of conditions and the following disclaimer in the 1284d77676SNathan Whitehorn * documentation and/or other materials provided with the distribution. 1384d77676SNathan Whitehorn * 1484d77676SNathan Whitehorn * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1584d77676SNathan Whitehorn * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1684d77676SNathan Whitehorn * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1784d77676SNathan Whitehorn * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1884d77676SNathan Whitehorn * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1984d77676SNathan Whitehorn * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2084d77676SNathan Whitehorn * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2184d77676SNathan Whitehorn * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2284d77676SNathan Whitehorn * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2384d77676SNathan Whitehorn * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2484d77676SNathan Whitehorn * SUCH DAMAGE. 2584d77676SNathan Whitehorn */ 2684d77676SNathan Whitehorn 2784d77676SNathan Whitehorn #include <sys/cdefs.h> 2884d77676SNathan Whitehorn __FBSDID("$FreeBSD$"); 2984d77676SNathan Whitehorn 3084d77676SNathan Whitehorn #include <sys/param.h> 3184d77676SNathan Whitehorn #include <sys/kernel.h> 3284d77676SNathan Whitehorn #include <sys/lock.h> 3384d77676SNathan Whitehorn #include <sys/mutex.h> 3484d77676SNathan Whitehorn #include <sys/systm.h> 3584d77676SNathan Whitehorn 3684d77676SNathan Whitehorn #include <sys/types.h> 3784d77676SNathan Whitehorn #include <sys/kthread.h> 3884d77676SNathan Whitehorn #include <sys/malloc.h> 3984d77676SNathan Whitehorn #include <sys/reboot.h> 4084d77676SNathan Whitehorn #include <sys/sysctl.h> 4184d77676SNathan Whitehorn #include <sys/queue.h> 4284d77676SNathan Whitehorn 4384d77676SNathan Whitehorn #include "powermac_thermal.h" 4484d77676SNathan Whitehorn 4584d77676SNathan Whitehorn static void fan_management_proc(void); 4684d77676SNathan Whitehorn static void pmac_therm_manage_fans(void); 4784d77676SNathan Whitehorn 4884d77676SNathan Whitehorn static struct proc *pmac_them_proc; 4984d77676SNathan Whitehorn static int enable_pmac_thermal = 1; 5084d77676SNathan Whitehorn 5184d77676SNathan Whitehorn static struct kproc_desc pmac_therm_kp = { 5284d77676SNathan Whitehorn "pmac_thermal", 5384d77676SNathan Whitehorn fan_management_proc, 5484d77676SNathan Whitehorn &pmac_them_proc 5584d77676SNathan Whitehorn }; 5684d77676SNathan Whitehorn 5784d77676SNathan Whitehorn SYSINIT(pmac_therm_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start, 5884d77676SNathan Whitehorn &pmac_therm_kp); 5984d77676SNathan Whitehorn SYSCTL_INT(_machdep, OID_AUTO, manage_fans, CTLFLAG_RW | CTLFLAG_TUN, 6084d77676SNathan Whitehorn &enable_pmac_thermal, 1, "Enable automatic fan management"); 6184d77676SNathan Whitehorn MALLOC_DEFINE(M_PMACTHERM, "pmactherm", "Powermac Thermal Management"); 6284d77676SNathan Whitehorn 6384d77676SNathan Whitehorn struct pmac_fan_le { 6484d77676SNathan Whitehorn struct pmac_fan *fan; 6584d77676SNathan Whitehorn int last_val; 6684d77676SNathan Whitehorn SLIST_ENTRY(pmac_fan_le) entries; 6784d77676SNathan Whitehorn }; 6884d77676SNathan Whitehorn struct pmac_sens_le { 6984d77676SNathan Whitehorn struct pmac_therm *sensor; 7084d77676SNathan Whitehorn int last_val; 7184d77676SNathan Whitehorn SLIST_ENTRY(pmac_sens_le) entries; 7284d77676SNathan Whitehorn }; 7384d77676SNathan Whitehorn static SLIST_HEAD(pmac_fans, pmac_fan_le) fans = SLIST_HEAD_INITIALIZER(fans); 7484d77676SNathan Whitehorn static SLIST_HEAD(pmac_sensors, pmac_sens_le) sensors = 7584d77676SNathan Whitehorn SLIST_HEAD_INITIALIZER(sensors); 7684d77676SNathan Whitehorn 7784d77676SNathan Whitehorn static void 7884d77676SNathan Whitehorn fan_management_proc(void) 7984d77676SNathan Whitehorn { 8084d77676SNathan Whitehorn /* Nothing to manage? */ 8184d77676SNathan Whitehorn if (SLIST_EMPTY(&fans)) 82*9d2a3635SNathan Whitehorn kproc_exit(0); 8384d77676SNathan Whitehorn 8484d77676SNathan Whitehorn while (1) { 8584d77676SNathan Whitehorn pmac_therm_manage_fans(); 8684d77676SNathan Whitehorn pause("pmac_therm", hz); 8784d77676SNathan Whitehorn } 8884d77676SNathan Whitehorn } 8984d77676SNathan Whitehorn 9084d77676SNathan Whitehorn static void 9184d77676SNathan Whitehorn pmac_therm_manage_fans(void) 9284d77676SNathan Whitehorn { 9384d77676SNathan Whitehorn struct pmac_sens_le *sensor; 9484d77676SNathan Whitehorn struct pmac_fan_le *fan; 9584d77676SNathan Whitehorn int average_excess, max_excess_zone, frac_excess; 9684d77676SNathan Whitehorn int nsens, nsens_zone; 97d015abb7SNathan Whitehorn int temp; 9884d77676SNathan Whitehorn 9984d77676SNathan Whitehorn if (!enable_pmac_thermal) 10084d77676SNathan Whitehorn return; 10184d77676SNathan Whitehorn 10284d77676SNathan Whitehorn /* Read all the sensors */ 10384d77676SNathan Whitehorn SLIST_FOREACH(sensor, &sensors, entries) { 104d015abb7SNathan Whitehorn temp = sensor->sensor->read(sensor->sensor); 105d015abb7SNathan Whitehorn if (temp > 0) /* Use the previous temp in case of error */ 106d015abb7SNathan Whitehorn sensor->last_val = temp; 107d015abb7SNathan Whitehorn 10884d77676SNathan Whitehorn if (sensor->last_val > sensor->sensor->max_temp) { 10984d77676SNathan Whitehorn printf("WARNING: Current temperature (%s: %d.%d C) " 11084d77676SNathan Whitehorn "exceeds critical temperature (%d.%d C)! " 11184d77676SNathan Whitehorn "Shutting down!\n", sensor->sensor->name, 11284d77676SNathan Whitehorn sensor->last_val / 10, sensor->last_val % 10, 11384d77676SNathan Whitehorn sensor->sensor->max_temp / 10, 11484d77676SNathan Whitehorn sensor->sensor->max_temp % 10); 11584d77676SNathan Whitehorn shutdown_nice(RB_POWEROFF); 11684d77676SNathan Whitehorn } 11784d77676SNathan Whitehorn } 11884d77676SNathan Whitehorn 11984d77676SNathan Whitehorn /* Set all the fans */ 12084d77676SNathan Whitehorn SLIST_FOREACH(fan, &fans, entries) { 12184d77676SNathan Whitehorn nsens = nsens_zone = 0; 12284d77676SNathan Whitehorn average_excess = max_excess_zone = 0; 12384d77676SNathan Whitehorn SLIST_FOREACH(sensor, &sensors, entries) { 12484d77676SNathan Whitehorn frac_excess = (sensor->last_val - 12584d77676SNathan Whitehorn sensor->sensor->target_temp)*100 / 12684d77676SNathan Whitehorn (sensor->sensor->max_temp - 12784d77676SNathan Whitehorn sensor->sensor->target_temp); 128cbfd4d0cSNathan Whitehorn if (frac_excess < 0) 129cbfd4d0cSNathan Whitehorn frac_excess = 0; 13084d77676SNathan Whitehorn if (sensor->sensor->zone == fan->fan->zone) { 13184d77676SNathan Whitehorn max_excess_zone = imax(max_excess_zone, 13284d77676SNathan Whitehorn frac_excess); 13384d77676SNathan Whitehorn nsens_zone++; 13484d77676SNathan Whitehorn } 13584d77676SNathan Whitehorn average_excess += frac_excess; 13684d77676SNathan Whitehorn nsens++; 13784d77676SNathan Whitehorn } 13884d77676SNathan Whitehorn average_excess /= nsens; 13984d77676SNathan Whitehorn 14084d77676SNathan Whitehorn /* If there are no sensors in this zone, use the average */ 14184d77676SNathan Whitehorn if (nsens_zone == 0) 14284d77676SNathan Whitehorn max_excess_zone = average_excess; 14384d77676SNathan Whitehorn /* No sensors at all? Use default */ 14484d77676SNathan Whitehorn if (nsens == 0) { 14584d77676SNathan Whitehorn fan->fan->set(fan->fan, fan->fan->default_rpm); 14684d77676SNathan Whitehorn continue; 14784d77676SNathan Whitehorn } 14884d77676SNathan Whitehorn 14984d77676SNathan Whitehorn /* 15084d77676SNathan Whitehorn * Scale the fan linearly in the max temperature in its 15184d77676SNathan Whitehorn * thermal zone. 15284d77676SNathan Whitehorn */ 15384d77676SNathan Whitehorn fan->fan->set(fan->fan, max_excess_zone * 15484d77676SNathan Whitehorn (fan->fan->max_rpm - fan->fan->min_rpm)/100 + 15584d77676SNathan Whitehorn fan->fan->min_rpm); 15684d77676SNathan Whitehorn } 15784d77676SNathan Whitehorn } 15884d77676SNathan Whitehorn 15984d77676SNathan Whitehorn void 16084d77676SNathan Whitehorn pmac_thermal_fan_register(struct pmac_fan *fan) 16184d77676SNathan Whitehorn { 16284d77676SNathan Whitehorn struct pmac_fan_le *list_entry; 16384d77676SNathan Whitehorn 16484d77676SNathan Whitehorn list_entry = malloc(sizeof(struct pmac_fan_le), M_PMACTHERM, 16584d77676SNathan Whitehorn M_ZERO | M_WAITOK); 16684d77676SNathan Whitehorn list_entry->fan = fan; 16784d77676SNathan Whitehorn 16884d77676SNathan Whitehorn SLIST_INSERT_HEAD(&fans, list_entry, entries); 16984d77676SNathan Whitehorn } 17084d77676SNathan Whitehorn 17184d77676SNathan Whitehorn void 17284d77676SNathan Whitehorn pmac_thermal_sensor_register(struct pmac_therm *sensor) 17384d77676SNathan Whitehorn { 17484d77676SNathan Whitehorn struct pmac_sens_le *list_entry; 17584d77676SNathan Whitehorn 17684d77676SNathan Whitehorn list_entry = malloc(sizeof(struct pmac_sens_le), M_PMACTHERM, 17784d77676SNathan Whitehorn M_ZERO | M_WAITOK); 17884d77676SNathan Whitehorn list_entry->sensor = sensor; 17984d77676SNathan Whitehorn 18084d77676SNathan Whitehorn SLIST_INSERT_HEAD(&sensors, list_entry, entries); 18184d77676SNathan Whitehorn } 18284d77676SNathan Whitehorn 183