123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- /*
- * Copyright 2018 Duan Hao
- * Copyright 2018 Con Kolivas <kernel@kolivas.org>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by the Free
- * Software Foundation; either version 3 of the License, or (at your option)
- * any later version. See COPYING for more details.
- */
- /******************************************************************************
- * Description: fan control using simple PID
- ******************************************************************************/
- #include <stdio.h>
- #include <stdint.h>
- #include <unistd.h>
- #include "dragonmint_t1.h"
- #include "dm_temp_ctrl.h"
- #include "dm_fan_ctrl.h"
- /******************************************************************************
- * Macros & Constants
- ******************************************************************************/
- #define FAN_MODE_DEF FAN_MODE_AUTO // default fan control mode
- #define WORK_CYCLE_DEF (2) // default time interval between temperature checks
- #define DEV_TMP_CHK_CNT (3)
- #define DEV_TMP_CHK_SPAN (6)
- #define TIMEOUT_GET_TMP (3)
- /******************************************************************************
- * Global variables
- ******************************************************************************/
- volatile c_fan_cfg g_fan_cfg; // fan config
- volatile int g_fan_profile; // fan profile: normal / overheat / preheat
- static c_temp g_dev_tmp; // device temperature sequence
- static c_temp g_dev_last_tmp; // device temperature sequence
- extern int chain_flag[MAX_CHAIN_NUM];
- /******************************************************************************
- * Prototypes
- ******************************************************************************/
- static bool dm_fanctrl_get_tmp(void);
- static void dm_fanctrl_update_fan_speed(void);
- static bool dm_fanctrl_check_overheat(void);
- static bool dm_fanctrl_check_preheat(void);
- /******************************************************************************
- * Implementations
- ******************************************************************************/
- void dm_fanctrl_get_defcfg(c_fan_cfg *p_cfg)
- {
- p_cfg->fan_mode = FAN_MODE_DEF;
- p_cfg->fan_speed = FAN_SPEED_DEF;
- p_cfg->fan_speed_preheat = FAN_SPEED_PREHEAT;
- p_cfg->fan_ctrl_cycle = WORK_CYCLE_DEF;
- p_cfg->preheat = true;
- }
- void dm_fanctrl_init(c_fan_cfg *p_cfg)
- {
- if (NULL == p_cfg) {
- c_fan_cfg cfg;
- dm_fanctrl_get_defcfg(&cfg); // avoid to pass volatile pointer directly
- g_fan_cfg = cfg;
- } else
- g_fan_cfg = *p_cfg;
- g_fan_profile = FAN_PF_NORMAL;
- g_dev_tmp.tmp_avg = g_dev_last_tmp.tmp_avg = g_tmp_cfg.tmp_target;
- }
- void *dm_fanctrl_thread(void __maybe_unused *argv)
- {
- int timeout_get_tmp = 0;
- // set default fan speed
- // dm_fanctrl_set_fan_speed(g_fan_cfg.fan_speed);
- while(true) {
- if (dm_fanctrl_get_tmp()) {
- dm_fanctrl_update_fan_speed();
- timeout_get_tmp = 0;
- } else
- timeout_get_tmp++;
- // force fan speed to 100% when failed to get temperature
- if (timeout_get_tmp >= TIMEOUT_GET_TMP && g_fan_cfg.fan_speed < FAN_SPEED_MAX) {
- applog(LOG_WARNING,
- "WARNING: unable to read temperature, force fan speed to %d", FAN_SPEED_MAX);
- dm_fanctrl_set_fan_speed(FAN_SPEED_MAX);
- timeout_get_tmp = 0;
- }
- sleep(g_fan_cfg.fan_ctrl_cycle);
- }
- return NULL;
- }
- void dm_fanctrl_set_fan_speed(char speed)
- {
- if (speed > FAN_SPEED_MAX)
- speed = FAN_SPEED_MAX;
- else if (speed < g_fan_cfg.fan_speed_preheat)
- speed = g_fan_cfg.fan_speed_preheat;
- if (speed != g_fan_cfg.fan_speed) {
- g_fan_cfg.fan_speed = speed;
- mcompat_fan_speed_set(0, g_fan_cfg.fan_speed); // fan id is ignored
- applog(LOG_ERR, "fan speed set to %d", g_fan_cfg.fan_speed);
- }
- }
- static bool dm_fanctrl_get_tmp(void)
- {
- bool retval = false;
- int i, chain_num = 0;
- c_temp dev_temp;
- // init
- chain_num = 0;
- dev_temp.tmp_hi = g_tmp_cfg.tmp_min;
- dev_temp.tmp_lo = g_tmp_cfg.tmp_max;
- dev_temp.tmp_avg = 0;
- for(i = 0; i < MAX_CHAIN_NUM; ++i) {
- if (chain_flag[i]
- && g_chain_tmp[i].tmp_avg > g_tmp_cfg.tmp_min
- && g_chain_tmp[i].tmp_avg < g_tmp_cfg.tmp_max) {
- // temperature stat.
- dev_temp.tmp_lo = MIN(dev_temp.tmp_lo, g_chain_tmp[i].tmp_lo);
- dev_temp.tmp_hi = MAX(dev_temp.tmp_hi, g_chain_tmp[i].tmp_hi);
- dev_temp.tmp_avg = MAX(dev_temp.tmp_avg, g_chain_tmp[i].tmp_avg);
- chain_num++;
- }
- }
- if (chain_num > 0) {
- g_dev_tmp = dev_temp;
- retval = true;
- }
- return retval;
- }
- static bool dm_fanctrl_check_overheat(void)
- {
- int tmp_tolerance = 0;
- // if already in overheat mode, apply a small tolerance
- if (FAN_PF_OVERHEAT == g_fan_profile)
- tmp_tolerance = TEMP_TOLERANCE;
- // overheat mode: force to max fan speed while tmp_hi >= tmp_thr_hi
- if (g_dev_tmp.tmp_hi >= g_tmp_cfg.tmp_thr_hi - tmp_tolerance) {
- dm_fanctrl_set_fan_speed(FAN_SPEED_MAX);
- if (FAN_PF_OVERHEAT != g_fan_profile) {
- g_fan_profile = FAN_PF_OVERHEAT;
- applog(LOG_ERR, "OVERHEAT: temp_hi over %d, force fan speed to %d",
- g_tmp_cfg.tmp_thr_hi, FAN_SPEED_MAX);
- }
- return true;
- }
- g_fan_profile = FAN_PF_NORMAL;
- return false;
- }
- static bool dm_fanctrl_check_preheat(void)
- {
- int tmp_tolerance = 0;
- // preheat mode: do preheating when tmp_avg < tmp_thr_lo
- if (FAN_PF_PREHEAT != g_fan_profile)
- tmp_tolerance = TEMP_TOLERANCE;
- if (g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_thr_lo - tmp_tolerance) {
- dm_fanctrl_set_fan_speed(FAN_SPEED_PREHEAT);
- g_fan_profile = FAN_PF_PREHEAT;
- applog(LOG_ERR, "PREHEAT: tmp_avg under %d, force fan speed to %d",
- g_tmp_cfg.tmp_thr_lo, FAN_SPEED_PREHEAT);
- return true;
- }
- g_fan_profile = FAN_PF_NORMAL;
- return false;
- }
- static int8_t last_tmp_rise[8];
- static int64_t *last_tmp_int = (int64_t *)last_tmp_rise;
- static int tmp_rise_cnt;
- static void dm_fanctrl_update_fan_speed(void)
- {
- int fan_speed;
- int delta_tmp_avg, delta_tmp_hi;
- int tmp_rise, hi_raise;
- // detect overheat first
- if (dm_fanctrl_check_overheat())
- return;
-
- // preheat
- if (g_fan_cfg.preheat && dm_fanctrl_check_preheat())
- return;
- // check average temperature rising to determining fan speed target
- tmp_rise = g_dev_tmp.tmp_avg - g_dev_last_tmp.tmp_avg;
- delta_tmp_avg = g_dev_tmp.tmp_avg - g_tmp_cfg.tmp_target;
- hi_raise = g_dev_tmp.tmp_hi - g_dev_last_tmp.tmp_hi;
- delta_tmp_hi = g_dev_tmp.tmp_hi - g_tmp_cfg.tmp_thr_hi;
- /* If we have a hot spot, use that for fan speed control
- * instead of the average temperature */
- if (hi_raise > tmp_rise || delta_tmp_hi > delta_tmp_avg) {
- tmp_rise = hi_raise;
- delta_tmp_avg = delta_tmp_hi;
- }
- g_dev_last_tmp.tmp_avg = g_dev_tmp.tmp_avg;
- g_dev_last_tmp.tmp_hi = g_dev_tmp.tmp_hi;
- g_dev_last_tmp.tmp_lo = g_dev_tmp.tmp_lo;
- if (delta_tmp_avg > 0) {
- /* Over target temperature */
- /* Is the temp already coming down */
- if (tmp_rise < 0)
- goto out;
- /* Adjust fanspeed by temperature over and any further rise */
- fan_speed = g_fan_cfg.fan_speed + delta_tmp_avg + tmp_rise;
- } else {
- /* Below target temperature */
- int diff = tmp_rise;
- if (tmp_rise > 0) {
- int divisor = -delta_tmp_avg / TEMP_TOLERANCE + 1;
- /* Adjust fanspeed by temperature change proportional to
- * diff from optimal. */
- diff /= divisor;
- } else if (!tmp_rise) {
- /* Is the temp below optimal and unchanging, gently
- * lower speed. Allow tighter temperature tolerance if
- * temperature is unchanged for longer. */
- if ((g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_target - TEMP_TOLERANCE) ||
- (!(*last_tmp_int) && (g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_target))) {
- *last_tmp_int = 0xFFFFFFFFFFFFFFFF;
- diff -= 1;
- }
- }
- fan_speed = g_fan_cfg.fan_speed + diff;
- }
- // set fan speed
- dm_fanctrl_set_fan_speed(fan_speed);
- out:
- last_tmp_rise[(tmp_rise_cnt++) % 8] = tmp_rise;
- }
|