Path: blob/main/sys/powerpc/powermac/powermac_thermal.c
39507 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2009-2011 Nathan Whitehorn4* All rights reserved.5*6* Redistribution and use in source and binary forms, with or without7* modification, are permitted provided that the following conditions8* are met:9* 1. Redistributions of source code must retain the above copyright10* notice, this list of conditions and the following disclaimer.11* 2. Redistributions in binary form must reproduce the above copyright12* notice, this list of conditions and the following disclaimer in the13* documentation and/or other materials provided with the distribution.14*15* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND16* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE17* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE18* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE19* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL20* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS21* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)22* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT23* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY24* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF25* SUCH DAMAGE.26*/2728#include <sys/param.h>29#include <sys/kernel.h>30#include <sys/lock.h>31#include <sys/mutex.h>32#include <sys/systm.h>3334#include <sys/types.h>35#include <sys/kthread.h>36#include <sys/malloc.h>37#include <sys/reboot.h>38#include <sys/sysctl.h>39#include <sys/queue.h>4041#include "powermac_thermal.h"4243/* A 10 second timer for spinning down fans. */44#define FAN_HYSTERESIS_TIMER 104546static void fan_management_proc(void);47static void pmac_therm_manage_fans(void);4849static struct proc *pmac_them_proc;50static int enable_pmac_thermal = 1;5152static struct kproc_desc pmac_therm_kp = {53"pmac_thermal",54fan_management_proc,55&pmac_them_proc56};5758SYSINIT(pmac_therm_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start,59&pmac_therm_kp);60SYSCTL_INT(_machdep, OID_AUTO, manage_fans, CTLFLAG_RWTUN,61&enable_pmac_thermal, 1, "Enable automatic fan management");62static MALLOC_DEFINE(M_PMACTHERM, "pmactherm", "Powermac Thermal Management");6364struct pmac_fan_le {65struct pmac_fan *fan;66int last_val;67int timer;68SLIST_ENTRY(pmac_fan_le) entries;69};70struct pmac_sens_le {71struct pmac_therm *sensor;72int last_val;73#define MAX_CRITICAL_COUNT 674int critical_count;75SLIST_ENTRY(pmac_sens_le) entries;76};77static SLIST_HEAD(pmac_fans, pmac_fan_le) fans = SLIST_HEAD_INITIALIZER(fans);78static SLIST_HEAD(pmac_sensors, pmac_sens_le) sensors =79SLIST_HEAD_INITIALIZER(sensors);8081static void82fan_management_proc(void)83{84/* Nothing to manage? */85if (SLIST_EMPTY(&fans))86kproc_exit(0);8788while (1) {89pmac_therm_manage_fans();90pause("pmac_therm", hz);91}92}9394static void95pmac_therm_manage_fans(void)96{97struct pmac_sens_le *sensor;98struct pmac_fan_le *fan;99int average_excess, max_excess_zone, frac_excess;100int fan_speed;101int nsens, nsens_zone;102int temp;103104if (!enable_pmac_thermal)105return;106107/* Read all the sensors */108SLIST_FOREACH(sensor, &sensors, entries) {109temp = sensor->sensor->read(sensor->sensor);110if (temp > 0) /* Use the previous temp in case of error */111sensor->last_val = temp;112113if (sensor->last_val > sensor->sensor->max_temp) {114sensor->critical_count++;115printf("WARNING: Current temperature (%s: %d.%d C) "116"exceeds critical temperature (%d.%d C); "117"count=%d\n",118sensor->sensor->name,119(sensor->last_val - ZERO_C_TO_K) / 10,120(sensor->last_val - ZERO_C_TO_K) % 10,121(sensor->sensor->max_temp - ZERO_C_TO_K) / 10,122(sensor->sensor->max_temp - ZERO_C_TO_K) % 10,123sensor->critical_count);124if (sensor->critical_count >= MAX_CRITICAL_COUNT) {125printf("WARNING: %s temperature exceeded "126"critical temperature %d times in a row; "127"shutting down!\n",128sensor->sensor->name,129sensor->critical_count);130shutdown_nice(RB_POWEROFF);131}132} else {133if (sensor->critical_count > 0)134sensor->critical_count--;135}136}137138/* Set all the fans */139SLIST_FOREACH(fan, &fans, entries) {140nsens = nsens_zone = 0;141average_excess = max_excess_zone = 0;142SLIST_FOREACH(sensor, &sensors, entries) {143temp = imin(sensor->last_val,144sensor->sensor->max_temp);145frac_excess = (temp -146sensor->sensor->target_temp)*100 /147(sensor->sensor->max_temp - temp + 1);148if (frac_excess < 0)149frac_excess = 0;150if (sensor->sensor->zone == fan->fan->zone) {151max_excess_zone = imax(max_excess_zone,152frac_excess);153nsens_zone++;154}155average_excess += frac_excess;156nsens++;157}158average_excess /= nsens;159160/* If there are no sensors in this zone, use the average */161if (nsens_zone == 0)162max_excess_zone = average_excess;163/* No sensors at all? Use default */164if (nsens == 0) {165fan->fan->set(fan->fan, fan->fan->default_rpm);166continue;167}168169/*170* Scale the fan linearly in the max temperature in its171* thermal zone.172*/173max_excess_zone = imin(max_excess_zone, 100);174fan_speed = max_excess_zone *175(fan->fan->max_rpm - fan->fan->min_rpm)/100 +176fan->fan->min_rpm;177if (fan_speed >= fan->last_val) {178fan->timer = FAN_HYSTERESIS_TIMER;179fan->last_val = fan_speed;180} else {181fan->timer--;182if (fan->timer == 0) {183fan->last_val = fan_speed;184fan->timer = FAN_HYSTERESIS_TIMER;185}186}187fan->fan->set(fan->fan, fan->last_val);188}189}190191void192pmac_thermal_fan_register(struct pmac_fan *fan)193{194struct pmac_fan_le *list_entry;195196list_entry = malloc(sizeof(struct pmac_fan_le), M_PMACTHERM,197M_ZERO | M_WAITOK);198list_entry->fan = fan;199200SLIST_INSERT_HEAD(&fans, list_entry, entries);201}202203void204pmac_thermal_sensor_register(struct pmac_therm *sensor)205{206struct pmac_sens_le *list_entry;207208list_entry = malloc(sizeof(struct pmac_sens_le), M_PMACTHERM,209M_ZERO | M_WAITOK);210list_entry->sensor = sensor;211list_entry->last_val = 0;212list_entry->critical_count = 0;213214SLIST_INSERT_HEAD(&sensors, list_entry, entries);215}216217218