xref: /openbsd-src/sys/arch/macppc/dev/thermal.c (revision 3bf93282e01e0f312a53b43451206fc5ebc0aa39)
1*3bf93282Scheloha /*	$OpenBSD: thermal.c,v 1.6 2019/10/08 13:21:38 cheloha Exp $ */
274409486Smglocker 
374409486Smglocker /*-
474409486Smglocker  * Copyright (c) 2009-2011 Nathan Whitehorn
574409486Smglocker  * All rights reserved.
674409486Smglocker  *
774409486Smglocker  * Redistribution and use in source and binary forms, with or without
874409486Smglocker  * modification, are permitted provided that the following conditions
974409486Smglocker  * are met:
1074409486Smglocker  * 1. Redistributions of source code must retain the above copyright
1174409486Smglocker  *    notice, this list of conditions and the following disclaimer.
1274409486Smglocker  * 2. Redistributions in binary form must reproduce the above copyright
1374409486Smglocker  *    notice, this list of conditions and the following disclaimer in the
1474409486Smglocker  *    documentation and/or other materials provided with the distribution.
1574409486Smglocker  *
1674409486Smglocker  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1774409486Smglocker  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1874409486Smglocker  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1974409486Smglocker  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2074409486Smglocker  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2174409486Smglocker  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2274409486Smglocker  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2374409486Smglocker  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2474409486Smglocker  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2574409486Smglocker  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2674409486Smglocker  * SUCH DAMAGE.
2774409486Smglocker  */
2874409486Smglocker 
2974409486Smglocker #include <sys/param.h>
3074409486Smglocker #include <sys/systm.h>
3174409486Smglocker #include <sys/kernel.h>
3274409486Smglocker 
3374409486Smglocker #include <sys/malloc.h>
3474409486Smglocker #include <sys/reboot.h>
3574409486Smglocker #include <sys/sensors.h>
3674409486Smglocker #include <sys/kthread.h>
3774409486Smglocker 
3874409486Smglocker #include <macppc/dev/thermal.h>
3974409486Smglocker 
4074409486Smglocker /* A 10 second timer for spinning down fans. */
4174409486Smglocker #define FAN_HYSTERESIS_TIMER	10
4274409486Smglocker 
4374409486Smglocker void thermal_thread_init(void);
4474409486Smglocker void thermal_thread_create(void *);
4574409486Smglocker void thermal_thread_loop(void *);
4674409486Smglocker void thermal_manage_fans(void);
4774409486Smglocker 
4874409486Smglocker int thermal_enable = 0;
4974409486Smglocker 
5074409486Smglocker struct thermal_fan_le {
5174409486Smglocker 	struct thermal_fan		*fan;
5274409486Smglocker 	int				last_val;
5374409486Smglocker 	int				timer;
5474409486Smglocker 	SLIST_ENTRY(thermal_fan_le)	entries;
5574409486Smglocker };
5674409486Smglocker struct thermal_sens_le {
5774409486Smglocker 	struct thermal_temp		*sensor;
5874409486Smglocker 	int				last_val;
5974409486Smglocker #define MAX_CRITICAL_COUNT 6
6074409486Smglocker 	int				critical_count;
6174409486Smglocker 	SLIST_ENTRY(thermal_sens_le)	entries;
6274409486Smglocker };
6374409486Smglocker 
6474409486Smglocker SLIST_HEAD(thermal_fans, thermal_fan_le) fans =
6574409486Smglocker     SLIST_HEAD_INITIALIZER(fans);
6674409486Smglocker SLIST_HEAD(thermal_sensors, thermal_sens_le) sensors =
6774409486Smglocker     SLIST_HEAD_INITIALIZER(sensors);
6874409486Smglocker 
6974409486Smglocker void
thermal_thread_init(void)7074409486Smglocker thermal_thread_init(void)
7174409486Smglocker {
7274409486Smglocker 	if (thermal_enable)
7374409486Smglocker 		return;		/* we're already running */
7474409486Smglocker 	thermal_enable = 1;
7574409486Smglocker 
7674409486Smglocker 	kthread_create_deferred(thermal_thread_create, &thermal_enable);
7774409486Smglocker }
7874409486Smglocker 
7974409486Smglocker void
thermal_thread_create(void * arg)8074409486Smglocker thermal_thread_create(void *arg)
8174409486Smglocker {
8274409486Smglocker 	if (kthread_create(thermal_thread_loop, &thermal_enable, NULL,
8374409486Smglocker 	    "thermal")) {
8474409486Smglocker 		printf("thermal kernel thread can't be created!\n");
8574409486Smglocker 		thermal_enable = 0;
8674409486Smglocker 	}
8774409486Smglocker }
8874409486Smglocker 
8974409486Smglocker void
thermal_thread_loop(void * arg)9074409486Smglocker thermal_thread_loop(void *arg)
9174409486Smglocker {
9274409486Smglocker 	while (thermal_enable) {
9374409486Smglocker 		thermal_manage_fans();
94*3bf93282Scheloha 		tsleep_nsec(&thermal_enable, 0, "thermal", SEC_TO_NSEC(1));
9574409486Smglocker 	}
9674409486Smglocker 	kthread_exit(0);
9774409486Smglocker }
9874409486Smglocker 
9974409486Smglocker void
thermal_manage_fans(void)10074409486Smglocker thermal_manage_fans(void)
10174409486Smglocker {
10274409486Smglocker 	struct thermal_sens_le *sensor;
10374409486Smglocker 	struct thermal_fan_le *fan;
104ede3af1dSmglocker 	int64_t average_excess, max_excess_zone, frac_excess;
10574409486Smglocker 	int fan_speed;
10674409486Smglocker 	int nsens, nsens_zone;
10774409486Smglocker 	int temp;
10874409486Smglocker 
10974409486Smglocker 	/* Read all the sensors */
11074409486Smglocker 	SLIST_FOREACH(sensor, &sensors, entries) {
11174409486Smglocker 		temp = sensor->sensor->read(sensor->sensor);
11274409486Smglocker 		if (temp > 0) /* Use the previous temp in case of error */
11374409486Smglocker 			sensor->last_val = temp;
11474409486Smglocker 
11574409486Smglocker 		if (sensor->last_val > sensor->sensor->max_temp) {
11674409486Smglocker 			sensor->critical_count++;
11774409486Smglocker 			printf("WARNING: Current temperature (%s: %d.%d C) "
118ede3af1dSmglocker 			    "exceeds critical temperature (%lld.%lld C); "
11974409486Smglocker 			    "count=%d\n",
12074409486Smglocker 			    sensor->sensor->name,
12174409486Smglocker 			    (sensor->last_val - ZERO_C_TO_MUK)/1000000,
12274409486Smglocker 			    (sensor->last_val - ZERO_C_TO_MUK)%1000000,
12374409486Smglocker 			    (sensor->sensor->max_temp - ZERO_C_TO_MUK)/1000000,
12474409486Smglocker 			    (sensor->sensor->max_temp - ZERO_C_TO_MUK)%1000000,
12574409486Smglocker 			    sensor->critical_count);
12674409486Smglocker 			if (sensor->critical_count >= MAX_CRITICAL_COUNT) {
12774409486Smglocker 				printf("WARNING: %s temperature exceeded "
12874409486Smglocker 				    "critical temperature %d times in a row; "
12974409486Smglocker 				    "shutting down!\n",
13074409486Smglocker 				    sensor->sensor->name,
13174409486Smglocker 				    sensor->critical_count);
132cfc062e6Smglocker 				reboot(RB_HALT | RB_POWERDOWN | RB_TIMEBAD);
13374409486Smglocker 			}
13474409486Smglocker 		} else {
13574409486Smglocker 			if (sensor->critical_count > 0)
13674409486Smglocker 				sensor->critical_count--;
13774409486Smglocker 		}
13874409486Smglocker 	}
13974409486Smglocker 
14074409486Smglocker 	/* Set all the fans */
14174409486Smglocker 	SLIST_FOREACH(fan, &fans, entries) {
14274409486Smglocker 		nsens = nsens_zone = 0;
14374409486Smglocker 		average_excess = max_excess_zone = 0;
14474409486Smglocker 		SLIST_FOREACH(sensor, &sensors, entries) {
145ede3af1dSmglocker 			temp = ulmin(sensor->last_val,
14674409486Smglocker 			    sensor->sensor->max_temp);
14774409486Smglocker 			frac_excess = (temp -
14874409486Smglocker 			    sensor->sensor->target_temp)*100 /
14974409486Smglocker 			    (sensor->sensor->max_temp - temp + 1);
15074409486Smglocker 			if (frac_excess < 0)
15174409486Smglocker 				frac_excess = 0;
15274409486Smglocker 			if (sensor->sensor->zone == fan->fan->zone) {
153ede3af1dSmglocker 				max_excess_zone = ulmax(max_excess_zone,
15474409486Smglocker 				    frac_excess);
15574409486Smglocker 				nsens_zone++;
15674409486Smglocker 			}
15774409486Smglocker 			average_excess += frac_excess;
15874409486Smglocker 			nsens++;
15974409486Smglocker 		}
16074409486Smglocker 
16174409486Smglocker 		/* No sensors at all? Use default */
16274409486Smglocker 		if (nsens == 0) {
16374409486Smglocker 			fan->fan->set(fan->fan, fan->fan->default_rpm);
16474409486Smglocker 			continue;
16574409486Smglocker 		}
16674409486Smglocker 
167ddc4b973Sjsg 		average_excess /= nsens;
168ddc4b973Sjsg 
169ddc4b973Sjsg 		/* If there are no sensors in this zone, use the average */
170ddc4b973Sjsg 		if (nsens_zone == 0)
171ddc4b973Sjsg 			max_excess_zone = average_excess;
172ddc4b973Sjsg 
17374409486Smglocker 		/*
17474409486Smglocker 		 * Scale the fan linearly in the max temperature in its
17574409486Smglocker 		 * thermal zone.
17674409486Smglocker 		 */
177ede3af1dSmglocker 		max_excess_zone = ulmin(max_excess_zone, 100);
17874409486Smglocker 		fan_speed = max_excess_zone *
17974409486Smglocker 		    (fan->fan->max_rpm - fan->fan->min_rpm)/100 +
18074409486Smglocker 		    fan->fan->min_rpm;
18174409486Smglocker 		if (fan_speed >= fan->last_val) {
18274409486Smglocker 		    fan->timer = FAN_HYSTERESIS_TIMER;
18374409486Smglocker 		    fan->last_val = fan_speed;
18474409486Smglocker 		} else {
18574409486Smglocker 		    fan->timer--;
18674409486Smglocker 		    if (fan->timer == 0) {
18774409486Smglocker 		    	fan->last_val = fan_speed;
18874409486Smglocker 		    	fan->timer = FAN_HYSTERESIS_TIMER;
18974409486Smglocker 		    }
19074409486Smglocker 		}
19174409486Smglocker 		fan->fan->set(fan->fan, fan->last_val);
19274409486Smglocker 	}
19374409486Smglocker }
19474409486Smglocker 
19574409486Smglocker void
thermal_fan_register(struct thermal_fan * fan)19674409486Smglocker thermal_fan_register(struct thermal_fan *fan)
19774409486Smglocker {
19874409486Smglocker 	struct thermal_fan_le *list_entry;
19974409486Smglocker 
20074409486Smglocker 	thermal_thread_init();	/* first caller inits our thread */
20174409486Smglocker 
20274409486Smglocker 	list_entry = malloc(sizeof(struct thermal_fan_le), M_DEVBUF,
20374409486Smglocker 	    M_ZERO | M_WAITOK);
20474409486Smglocker 	list_entry->fan = fan;
20574409486Smglocker 
20674409486Smglocker 	SLIST_INSERT_HEAD(&fans, list_entry, entries);
20774409486Smglocker }
20874409486Smglocker 
20974409486Smglocker void
thermal_sensor_register(struct thermal_temp * sensor)21074409486Smglocker thermal_sensor_register(struct thermal_temp *sensor)
21174409486Smglocker {
21274409486Smglocker 	struct thermal_sens_le *list_entry;
21374409486Smglocker 
21474409486Smglocker 	thermal_thread_init();	/* first caller inits our thread */
21574409486Smglocker 
21674409486Smglocker 	list_entry = malloc(sizeof(struct thermal_sens_le), M_DEVBUF,
21774409486Smglocker 	    M_ZERO | M_WAITOK);
21874409486Smglocker 	list_entry->sensor = sensor;
21974409486Smglocker 	list_entry->last_val = 0;
22074409486Smglocker 	list_entry->critical_count = 0;
22174409486Smglocker 
22274409486Smglocker 	SLIST_INSERT_HEAD(&sensors, list_entry, entries);
22374409486Smglocker }
224