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