windfarm_pid.c 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. /*
  3. * Windfarm PowerMac thermal control. Generic PID helpers
  4. *
  5. * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
  6. * <benh@kernel.crashing.org>
  7. */
  8. #include <linux/types.h>
  9. #include <linux/errno.h>
  10. #include <linux/kernel.h>
  11. #include <linux/string.h>
  12. #include <linux/module.h>
  13. #include "windfarm_pid.h"
  14. #undef DEBUG
  15. #ifdef DEBUG
  16. #define DBG(args...) printk(args)
  17. #else
  18. #define DBG(args...) do { } while(0)
  19. #endif
  20. void wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param)
  21. {
  22. memset(st, 0, sizeof(struct wf_pid_state));
  23. st->param = *param;
  24. st->first = 1;
  25. }
  26. EXPORT_SYMBOL_GPL(wf_pid_init);
  27. s32 wf_pid_run(struct wf_pid_state *st, s32 new_sample)
  28. {
  29. s64 error, integ, deriv;
  30. s32 target;
  31. int i, hlen = st->param.history_len;
  32. /* Calculate error term */
  33. error = new_sample - st->param.itarget;
  34. /* Get samples into our history buffer */
  35. if (st->first) {
  36. for (i = 0; i < hlen; i++) {
  37. st->samples[i] = new_sample;
  38. st->errors[i] = error;
  39. }
  40. st->first = 0;
  41. st->index = 0;
  42. } else {
  43. st->index = (st->index + 1) % hlen;
  44. st->samples[st->index] = new_sample;
  45. st->errors[st->index] = error;
  46. }
  47. /* Calculate integral term */
  48. for (i = 0, integ = 0; i < hlen; i++)
  49. integ += st->errors[(st->index + hlen - i) % hlen];
  50. integ *= st->param.interval;
  51. /* Calculate derivative term */
  52. deriv = st->errors[st->index] -
  53. st->errors[(st->index + hlen - 1) % hlen];
  54. deriv /= st->param.interval;
  55. /* Calculate target */
  56. target = (s32)((integ * (s64)st->param.gr + deriv * (s64)st->param.gd +
  57. error * (s64)st->param.gp) >> 36);
  58. if (st->param.additive)
  59. target += st->target;
  60. target = max(target, st->param.min);
  61. target = min(target, st->param.max);
  62. st->target = target;
  63. return st->target;
  64. }
  65. EXPORT_SYMBOL_GPL(wf_pid_run);
  66. void wf_cpu_pid_init(struct wf_cpu_pid_state *st,
  67. struct wf_cpu_pid_param *param)
  68. {
  69. memset(st, 0, sizeof(struct wf_cpu_pid_state));
  70. st->param = *param;
  71. st->first = 1;
  72. }
  73. EXPORT_SYMBOL_GPL(wf_cpu_pid_init);
  74. s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
  75. {
  76. s64 integ, deriv, prop;
  77. s32 error, target, sval, adj;
  78. int i, hlen = st->param.history_len;
  79. /* Calculate error term */
  80. error = st->param.pmaxadj - new_power;
  81. /* Get samples into our history buffer */
  82. if (st->first) {
  83. for (i = 0; i < hlen; i++) {
  84. st->powers[i] = new_power;
  85. st->errors[i] = error;
  86. }
  87. st->temps[0] = st->temps[1] = new_temp;
  88. st->first = 0;
  89. st->index = st->tindex = 0;
  90. } else {
  91. st->index = (st->index + 1) % hlen;
  92. st->powers[st->index] = new_power;
  93. st->errors[st->index] = error;
  94. st->tindex = (st->tindex + 1) % 2;
  95. st->temps[st->tindex] = new_temp;
  96. }
  97. /* Calculate integral term */
  98. for (i = 0, integ = 0; i < hlen; i++)
  99. integ += st->errors[(st->index + hlen - i) % hlen];
  100. integ *= st->param.interval;
  101. integ *= st->param.gr;
  102. sval = st->param.tmax - (s32)(integ >> 20);
  103. adj = min(st->param.ttarget, sval);
  104. DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj);
  105. /* Calculate derivative term */
  106. deriv = st->temps[st->tindex] -
  107. st->temps[(st->tindex + 2 - 1) % 2];
  108. deriv /= st->param.interval;
  109. deriv *= st->param.gd;
  110. /* Calculate proportional term */
  111. prop = st->last_delta = (new_temp - adj);
  112. prop *= st->param.gp;
  113. DBG("deriv: %lx, prop: %lx\n", deriv, prop);
  114. /* Calculate target */
  115. target = st->target + (s32)((deriv + prop) >> 36);
  116. target = max(target, st->param.min);
  117. target = min(target, st->param.max);
  118. st->target = target;
  119. return st->target;
  120. }
  121. EXPORT_SYMBOL_GPL(wf_cpu_pid_run);
  122. MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
  123. MODULE_DESCRIPTION("PID algorithm for PowerMacs thermal control");
  124. MODULE_LICENSE("GPL");