dm_fan_ctrl.c 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /*
  2. * Copyright 2018 Duan Hao
  3. * Copyright 2018 Con Kolivas <kernel@kolivas.org>
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the Free
  7. * Software Foundation; either version 3 of the License, or (at your option)
  8. * any later version. See COPYING for more details.
  9. */
  10. /******************************************************************************
  11. * Description: fan control using simple PID
  12. ******************************************************************************/
  13. #include <stdio.h>
  14. #include <stdint.h>
  15. #include <unistd.h>
  16. #include "dragonmint_t1.h"
  17. #include "dm_temp_ctrl.h"
  18. #include "dm_fan_ctrl.h"
  19. /******************************************************************************
  20. * Macros & Constants
  21. ******************************************************************************/
  22. #define FAN_MODE_DEF FAN_MODE_AUTO // default fan control mode
  23. #define WORK_CYCLE_DEF (2) // default time interval between temperature checks
  24. #define DEV_TMP_CHK_CNT (3)
  25. #define DEV_TMP_CHK_SPAN (6)
  26. #define TIMEOUT_GET_TMP (3)
  27. /******************************************************************************
  28. * Global variables
  29. ******************************************************************************/
  30. volatile c_fan_cfg g_fan_cfg; // fan config
  31. volatile int g_fan_profile; // fan profile: normal / overheat / preheat
  32. static c_temp g_dev_tmp; // device temperature sequence
  33. static c_temp g_dev_last_tmp; // device temperature sequence
  34. extern int chain_flag[MAX_CHAIN_NUM];
  35. /******************************************************************************
  36. * Prototypes
  37. ******************************************************************************/
  38. static bool dm_fanctrl_get_tmp(void);
  39. static void dm_fanctrl_update_fan_speed(void);
  40. static bool dm_fanctrl_check_overheat(void);
  41. static bool dm_fanctrl_check_preheat(void);
  42. /******************************************************************************
  43. * Implementations
  44. ******************************************************************************/
  45. void dm_fanctrl_get_defcfg(c_fan_cfg *p_cfg)
  46. {
  47. p_cfg->fan_mode = FAN_MODE_DEF;
  48. p_cfg->fan_speed = FAN_SPEED_DEF;
  49. p_cfg->fan_speed_preheat = FAN_SPEED_PREHEAT;
  50. p_cfg->fan_ctrl_cycle = WORK_CYCLE_DEF;
  51. p_cfg->preheat = true;
  52. }
  53. void dm_fanctrl_init(c_fan_cfg *p_cfg)
  54. {
  55. if (NULL == p_cfg) {
  56. c_fan_cfg cfg;
  57. dm_fanctrl_get_defcfg(&cfg); // avoid to pass volatile pointer directly
  58. g_fan_cfg = cfg;
  59. } else
  60. g_fan_cfg = *p_cfg;
  61. g_fan_profile = FAN_PF_NORMAL;
  62. g_dev_tmp.tmp_avg = g_dev_last_tmp.tmp_avg = g_tmp_cfg.tmp_target;
  63. }
  64. void *dm_fanctrl_thread(void __maybe_unused *argv)
  65. {
  66. int timeout_get_tmp = 0;
  67. // set default fan speed
  68. // dm_fanctrl_set_fan_speed(g_fan_cfg.fan_speed);
  69. while(true) {
  70. if (dm_fanctrl_get_tmp()) {
  71. dm_fanctrl_update_fan_speed();
  72. timeout_get_tmp = 0;
  73. } else
  74. timeout_get_tmp++;
  75. // force fan speed to 100% when failed to get temperature
  76. if (timeout_get_tmp >= TIMEOUT_GET_TMP && g_fan_cfg.fan_speed < FAN_SPEED_MAX) {
  77. applog(LOG_WARNING,
  78. "WARNING: unable to read temperature, force fan speed to %d", FAN_SPEED_MAX);
  79. dm_fanctrl_set_fan_speed(FAN_SPEED_MAX);
  80. timeout_get_tmp = 0;
  81. }
  82. sleep(g_fan_cfg.fan_ctrl_cycle);
  83. }
  84. return NULL;
  85. }
  86. void dm_fanctrl_set_fan_speed(char speed)
  87. {
  88. if (speed > FAN_SPEED_MAX)
  89. speed = FAN_SPEED_MAX;
  90. else if (speed < g_fan_cfg.fan_speed_preheat)
  91. speed = g_fan_cfg.fan_speed_preheat;
  92. if (speed != g_fan_cfg.fan_speed) {
  93. g_fan_cfg.fan_speed = speed;
  94. mcompat_fan_speed_set(0, g_fan_cfg.fan_speed); // fan id is ignored
  95. applog(LOG_ERR, "fan speed set to %d", g_fan_cfg.fan_speed);
  96. }
  97. }
  98. static bool dm_fanctrl_get_tmp(void)
  99. {
  100. bool retval = false;
  101. int i, chain_num = 0;
  102. c_temp dev_temp;
  103. // init
  104. chain_num = 0;
  105. dev_temp.tmp_hi = g_tmp_cfg.tmp_min;
  106. dev_temp.tmp_lo = g_tmp_cfg.tmp_max;
  107. dev_temp.tmp_avg = 0;
  108. for(i = 0; i < MAX_CHAIN_NUM; ++i) {
  109. if (chain_flag[i]
  110. && g_chain_tmp[i].tmp_avg > g_tmp_cfg.tmp_min
  111. && g_chain_tmp[i].tmp_avg < g_tmp_cfg.tmp_max) {
  112. // temperature stat.
  113. dev_temp.tmp_lo = MIN(dev_temp.tmp_lo, g_chain_tmp[i].tmp_lo);
  114. dev_temp.tmp_hi = MAX(dev_temp.tmp_hi, g_chain_tmp[i].tmp_hi);
  115. dev_temp.tmp_avg = MAX(dev_temp.tmp_avg, g_chain_tmp[i].tmp_avg);
  116. chain_num++;
  117. }
  118. }
  119. if (chain_num > 0) {
  120. g_dev_tmp = dev_temp;
  121. retval = true;
  122. }
  123. return retval;
  124. }
  125. static bool dm_fanctrl_check_overheat(void)
  126. {
  127. int tmp_tolerance = 0;
  128. // if already in overheat mode, apply a small tolerance
  129. if (FAN_PF_OVERHEAT == g_fan_profile)
  130. tmp_tolerance = TEMP_TOLERANCE;
  131. // overheat mode: force to max fan speed while tmp_hi >= tmp_thr_hi
  132. if (g_dev_tmp.tmp_hi >= g_tmp_cfg.tmp_thr_hi - tmp_tolerance) {
  133. dm_fanctrl_set_fan_speed(FAN_SPEED_MAX);
  134. if (FAN_PF_OVERHEAT != g_fan_profile) {
  135. g_fan_profile = FAN_PF_OVERHEAT;
  136. applog(LOG_ERR, "OVERHEAT: temp_hi over %d, force fan speed to %d",
  137. g_tmp_cfg.tmp_thr_hi, FAN_SPEED_MAX);
  138. }
  139. return true;
  140. }
  141. g_fan_profile = FAN_PF_NORMAL;
  142. return false;
  143. }
  144. static bool dm_fanctrl_check_preheat(void)
  145. {
  146. int tmp_tolerance = 0;
  147. // preheat mode: do preheating when tmp_avg < tmp_thr_lo
  148. if (FAN_PF_PREHEAT != g_fan_profile)
  149. tmp_tolerance = TEMP_TOLERANCE;
  150. if (g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_thr_lo - tmp_tolerance) {
  151. dm_fanctrl_set_fan_speed(FAN_SPEED_PREHEAT);
  152. g_fan_profile = FAN_PF_PREHEAT;
  153. applog(LOG_ERR, "PREHEAT: tmp_avg under %d, force fan speed to %d",
  154. g_tmp_cfg.tmp_thr_lo, FAN_SPEED_PREHEAT);
  155. return true;
  156. }
  157. g_fan_profile = FAN_PF_NORMAL;
  158. return false;
  159. }
  160. static int8_t last_tmp_rise[8];
  161. static int64_t *last_tmp_int = (int64_t *)last_tmp_rise;
  162. static int tmp_rise_cnt;
  163. static void dm_fanctrl_update_fan_speed(void)
  164. {
  165. int fan_speed;
  166. int delta_tmp_avg, delta_tmp_hi;
  167. int tmp_rise, hi_raise;
  168. // detect overheat first
  169. if (dm_fanctrl_check_overheat())
  170. return;
  171. // preheat
  172. if (g_fan_cfg.preheat && dm_fanctrl_check_preheat())
  173. return;
  174. // check average temperature rising to determining fan speed target
  175. tmp_rise = g_dev_tmp.tmp_avg - g_dev_last_tmp.tmp_avg;
  176. delta_tmp_avg = g_dev_tmp.tmp_avg - g_tmp_cfg.tmp_target;
  177. hi_raise = g_dev_tmp.tmp_hi - g_dev_last_tmp.tmp_hi;
  178. delta_tmp_hi = g_dev_tmp.tmp_hi - g_tmp_cfg.tmp_thr_hi;
  179. /* If we have a hot spot, use that for fan speed control
  180. * instead of the average temperature */
  181. if (hi_raise > tmp_rise || delta_tmp_hi > delta_tmp_avg) {
  182. tmp_rise = hi_raise;
  183. delta_tmp_avg = delta_tmp_hi;
  184. }
  185. g_dev_last_tmp.tmp_avg = g_dev_tmp.tmp_avg;
  186. g_dev_last_tmp.tmp_hi = g_dev_tmp.tmp_hi;
  187. g_dev_last_tmp.tmp_lo = g_dev_tmp.tmp_lo;
  188. if (delta_tmp_avg > 0) {
  189. /* Over target temperature */
  190. /* Is the temp already coming down */
  191. if (tmp_rise < 0)
  192. goto out;
  193. /* Adjust fanspeed by temperature over and any further rise */
  194. fan_speed = g_fan_cfg.fan_speed + delta_tmp_avg + tmp_rise;
  195. } else {
  196. /* Below target temperature */
  197. int diff = tmp_rise;
  198. if (tmp_rise > 0) {
  199. int divisor = -delta_tmp_avg / TEMP_TOLERANCE + 1;
  200. /* Adjust fanspeed by temperature change proportional to
  201. * diff from optimal. */
  202. diff /= divisor;
  203. } else if (!tmp_rise) {
  204. /* Is the temp below optimal and unchanging, gently
  205. * lower speed. Allow tighter temperature tolerance if
  206. * temperature is unchanged for longer. */
  207. if ((g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_target - TEMP_TOLERANCE) ||
  208. (!(*last_tmp_int) && (g_dev_tmp.tmp_avg < g_tmp_cfg.tmp_target))) {
  209. *last_tmp_int = 0xFFFFFFFFFFFFFFFF;
  210. diff -= 1;
  211. }
  212. }
  213. fan_speed = g_fan_cfg.fan_speed + diff;
  214. }
  215. // set fan speed
  216. dm_fanctrl_set_fan_speed(fan_speed);
  217. out:
  218. last_tmp_rise[(tmp_rise_cnt++) % 8] = tmp_rise;
  219. }