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