powermac_thermal.c revision 222469
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: head/sys/powerpc/powermac/powermac_thermal.c 222469 2011-05-29 22:37:23Z nwhitehorn $");
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
45222429Snwhitehornstatic void fan_management_proc(void);
46222429Snwhitehornstatic void pmac_therm_manage_fans(void);
47222429Snwhitehorn
48222429Snwhitehornstatic struct proc *pmac_them_proc;
49222429Snwhitehornstatic int enable_pmac_thermal = 1;
50222429Snwhitehorn
51222429Snwhitehornstatic struct kproc_desc pmac_therm_kp = {
52222429Snwhitehorn	"pmac_thermal",
53222429Snwhitehorn	fan_management_proc,
54222429Snwhitehorn	&pmac_them_proc
55222429Snwhitehorn};
56222429Snwhitehorn
57222429SnwhitehornSYSINIT(pmac_therm_setup, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, kproc_start,
58222429Snwhitehorn    &pmac_therm_kp);
59222429SnwhitehornSYSCTL_INT(_machdep, OID_AUTO, manage_fans, CTLFLAG_RW | CTLFLAG_TUN,
60222429Snwhitehorn    &enable_pmac_thermal, 1, "Enable automatic fan management");
61222429SnwhitehornMALLOC_DEFINE(M_PMACTHERM, "pmactherm", "Powermac Thermal Management");
62222429Snwhitehorn
63222429Snwhitehornstruct pmac_fan_le {
64222429Snwhitehorn	struct pmac_fan			*fan;
65222429Snwhitehorn	int				last_val;
66222429Snwhitehorn	SLIST_ENTRY(pmac_fan_le)	entries;
67222429Snwhitehorn};
68222429Snwhitehornstruct pmac_sens_le {
69222429Snwhitehorn	struct pmac_therm		*sensor;
70222429Snwhitehorn	int				last_val;
71222429Snwhitehorn	SLIST_ENTRY(pmac_sens_le)	entries;
72222429Snwhitehorn};
73222429Snwhitehornstatic SLIST_HEAD(pmac_fans, pmac_fan_le) fans = SLIST_HEAD_INITIALIZER(fans);
74222429Snwhitehornstatic SLIST_HEAD(pmac_sensors, pmac_sens_le) sensors =
75222429Snwhitehorn    SLIST_HEAD_INITIALIZER(sensors);
76222429Snwhitehorn
77222429Snwhitehornstatic void
78222429Snwhitehornfan_management_proc(void)
79222429Snwhitehorn{
80222429Snwhitehorn	/* Nothing to manage? */
81222429Snwhitehorn	if (SLIST_EMPTY(&fans))
82222469Snwhitehorn		kproc_exit(0);
83222429Snwhitehorn
84222429Snwhitehorn	while (1) {
85222429Snwhitehorn		pmac_therm_manage_fans();
86222429Snwhitehorn		pause("pmac_therm", hz);
87222429Snwhitehorn	}
88222429Snwhitehorn}
89222429Snwhitehorn
90222429Snwhitehornstatic void
91222429Snwhitehornpmac_therm_manage_fans(void)
92222429Snwhitehorn{
93222429Snwhitehorn	struct pmac_sens_le *sensor;
94222429Snwhitehorn	struct pmac_fan_le *fan;
95222429Snwhitehorn	int average_excess, max_excess_zone, frac_excess;
96222429Snwhitehorn	int nsens, nsens_zone;
97222463Snwhitehorn	int temp;
98222429Snwhitehorn
99222429Snwhitehorn	if (!enable_pmac_thermal)
100222429Snwhitehorn		return;
101222429Snwhitehorn
102222429Snwhitehorn	/* Read all the sensors */
103222429Snwhitehorn	SLIST_FOREACH(sensor, &sensors, entries) {
104222463Snwhitehorn		temp = sensor->sensor->read(sensor->sensor);
105222463Snwhitehorn		if (temp > 0) /* Use the previous temp in case of error */
106222463Snwhitehorn			sensor->last_val = temp;
107222463Snwhitehorn
108222429Snwhitehorn		if (sensor->last_val > sensor->sensor->max_temp) {
109222429Snwhitehorn			printf("WARNING: Current temperature (%s: %d.%d C) "
110222429Snwhitehorn			    "exceeds critical temperature (%d.%d C)! "
111222429Snwhitehorn			    "Shutting down!\n", sensor->sensor->name,
112222429Snwhitehorn			    sensor->last_val / 10, sensor->last_val % 10,
113222429Snwhitehorn			    sensor->sensor->max_temp / 10,
114222429Snwhitehorn			    sensor->sensor->max_temp % 10);
115222429Snwhitehorn			shutdown_nice(RB_POWEROFF);
116222429Snwhitehorn		}
117222429Snwhitehorn	}
118222429Snwhitehorn
119222429Snwhitehorn	/* Set all the fans */
120222429Snwhitehorn	SLIST_FOREACH(fan, &fans, entries) {
121222429Snwhitehorn		nsens = nsens_zone = 0;
122222429Snwhitehorn		average_excess = max_excess_zone = 0;
123222429Snwhitehorn		SLIST_FOREACH(sensor, &sensors, entries) {
124222429Snwhitehorn			frac_excess = (sensor->last_val -
125222429Snwhitehorn			    sensor->sensor->target_temp)*100 /
126222429Snwhitehorn			    (sensor->sensor->max_temp -
127222429Snwhitehorn			    sensor->sensor->target_temp);
128222460Snwhitehorn			if (frac_excess < 0)
129222460Snwhitehorn				frac_excess = 0;
130222429Snwhitehorn			if (sensor->sensor->zone == fan->fan->zone) {
131222429Snwhitehorn				max_excess_zone = imax(max_excess_zone,
132222429Snwhitehorn				    frac_excess);
133222429Snwhitehorn				nsens_zone++;
134222429Snwhitehorn			}
135222429Snwhitehorn			average_excess += frac_excess;
136222429Snwhitehorn			nsens++;
137222429Snwhitehorn		}
138222429Snwhitehorn		average_excess /= nsens;
139222429Snwhitehorn
140222429Snwhitehorn		/* If there are no sensors in this zone, use the average */
141222429Snwhitehorn		if (nsens_zone == 0)
142222429Snwhitehorn			max_excess_zone = average_excess;
143222429Snwhitehorn		/* No sensors at all? Use default */
144222429Snwhitehorn		if (nsens == 0) {
145222429Snwhitehorn			fan->fan->set(fan->fan, fan->fan->default_rpm);
146222429Snwhitehorn			continue;
147222429Snwhitehorn		}
148222429Snwhitehorn
149222429Snwhitehorn		/*
150222429Snwhitehorn		 * Scale the fan linearly in the max temperature in its
151222429Snwhitehorn		 * thermal zone.
152222429Snwhitehorn		 */
153222429Snwhitehorn		fan->fan->set(fan->fan, max_excess_zone *
154222429Snwhitehorn		    (fan->fan->max_rpm - fan->fan->min_rpm)/100 +
155222429Snwhitehorn		    fan->fan->min_rpm);
156222429Snwhitehorn	}
157222429Snwhitehorn}
158222429Snwhitehorn
159222429Snwhitehornvoid
160222429Snwhitehornpmac_thermal_fan_register(struct pmac_fan *fan)
161222429Snwhitehorn{
162222429Snwhitehorn	struct pmac_fan_le *list_entry;
163222429Snwhitehorn
164222429Snwhitehorn	list_entry = malloc(sizeof(struct pmac_fan_le), M_PMACTHERM,
165222429Snwhitehorn	    M_ZERO | M_WAITOK);
166222429Snwhitehorn	list_entry->fan = fan;
167222429Snwhitehorn
168222429Snwhitehorn	SLIST_INSERT_HEAD(&fans, list_entry, entries);
169222429Snwhitehorn}
170222429Snwhitehorn
171222429Snwhitehornvoid
172222429Snwhitehornpmac_thermal_sensor_register(struct pmac_therm *sensor)
173222429Snwhitehorn{
174222429Snwhitehorn	struct pmac_sens_le *list_entry;
175222429Snwhitehorn
176222429Snwhitehorn	list_entry = malloc(sizeof(struct pmac_sens_le), M_PMACTHERM,
177222429Snwhitehorn	    M_ZERO | M_WAITOK);
178222429Snwhitehorn	list_entry->sensor = sensor;
179222429Snwhitehorn
180222429Snwhitehorn	SLIST_INSERT_HEAD(&sensors, list_entry, entries);
181222429Snwhitehorn}
182222429Snwhitehorn
183