162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Windfarm PowerMac thermal control. Generic PID helpers 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 662306a36Sopenharmony_ci * <benh@kernel.crashing.org> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/errno.h> 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "windfarm_pid.h" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#undef DEBUG 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#ifdef DEBUG 2062306a36Sopenharmony_ci#define DBG(args...) printk(args) 2162306a36Sopenharmony_ci#else 2262306a36Sopenharmony_ci#define DBG(args...) do { } while(0) 2362306a36Sopenharmony_ci#endif 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_civoid wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param) 2662306a36Sopenharmony_ci{ 2762306a36Sopenharmony_ci memset(st, 0, sizeof(struct wf_pid_state)); 2862306a36Sopenharmony_ci st->param = *param; 2962306a36Sopenharmony_ci st->first = 1; 3062306a36Sopenharmony_ci} 3162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_pid_init); 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cis32 wf_pid_run(struct wf_pid_state *st, s32 new_sample) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci s64 error, integ, deriv; 3662306a36Sopenharmony_ci s32 target; 3762306a36Sopenharmony_ci int i, hlen = st->param.history_len; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci /* Calculate error term */ 4062306a36Sopenharmony_ci error = new_sample - st->param.itarget; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* Get samples into our history buffer */ 4362306a36Sopenharmony_ci if (st->first) { 4462306a36Sopenharmony_ci for (i = 0; i < hlen; i++) { 4562306a36Sopenharmony_ci st->samples[i] = new_sample; 4662306a36Sopenharmony_ci st->errors[i] = error; 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ci st->first = 0; 4962306a36Sopenharmony_ci st->index = 0; 5062306a36Sopenharmony_ci } else { 5162306a36Sopenharmony_ci st->index = (st->index + 1) % hlen; 5262306a36Sopenharmony_ci st->samples[st->index] = new_sample; 5362306a36Sopenharmony_ci st->errors[st->index] = error; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci /* Calculate integral term */ 5762306a36Sopenharmony_ci for (i = 0, integ = 0; i < hlen; i++) 5862306a36Sopenharmony_ci integ += st->errors[(st->index + hlen - i) % hlen]; 5962306a36Sopenharmony_ci integ *= st->param.interval; 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Calculate derivative term */ 6262306a36Sopenharmony_ci deriv = st->errors[st->index] - 6362306a36Sopenharmony_ci st->errors[(st->index + hlen - 1) % hlen]; 6462306a36Sopenharmony_ci deriv /= st->param.interval; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci /* Calculate target */ 6762306a36Sopenharmony_ci target = (s32)((integ * (s64)st->param.gr + deriv * (s64)st->param.gd + 6862306a36Sopenharmony_ci error * (s64)st->param.gp) >> 36); 6962306a36Sopenharmony_ci if (st->param.additive) 7062306a36Sopenharmony_ci target += st->target; 7162306a36Sopenharmony_ci target = max(target, st->param.min); 7262306a36Sopenharmony_ci target = min(target, st->param.max); 7362306a36Sopenharmony_ci st->target = target; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci return st->target; 7662306a36Sopenharmony_ci} 7762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_pid_run); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_civoid wf_cpu_pid_init(struct wf_cpu_pid_state *st, 8062306a36Sopenharmony_ci struct wf_cpu_pid_param *param) 8162306a36Sopenharmony_ci{ 8262306a36Sopenharmony_ci memset(st, 0, sizeof(struct wf_cpu_pid_state)); 8362306a36Sopenharmony_ci st->param = *param; 8462306a36Sopenharmony_ci st->first = 1; 8562306a36Sopenharmony_ci} 8662306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_cpu_pid_init); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cis32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci s64 integ, deriv, prop; 9162306a36Sopenharmony_ci s32 error, target, sval, adj; 9262306a36Sopenharmony_ci int i, hlen = st->param.history_len; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci /* Calculate error term */ 9562306a36Sopenharmony_ci error = st->param.pmaxadj - new_power; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci /* Get samples into our history buffer */ 9862306a36Sopenharmony_ci if (st->first) { 9962306a36Sopenharmony_ci for (i = 0; i < hlen; i++) { 10062306a36Sopenharmony_ci st->powers[i] = new_power; 10162306a36Sopenharmony_ci st->errors[i] = error; 10262306a36Sopenharmony_ci } 10362306a36Sopenharmony_ci st->temps[0] = st->temps[1] = new_temp; 10462306a36Sopenharmony_ci st->first = 0; 10562306a36Sopenharmony_ci st->index = st->tindex = 0; 10662306a36Sopenharmony_ci } else { 10762306a36Sopenharmony_ci st->index = (st->index + 1) % hlen; 10862306a36Sopenharmony_ci st->powers[st->index] = new_power; 10962306a36Sopenharmony_ci st->errors[st->index] = error; 11062306a36Sopenharmony_ci st->tindex = (st->tindex + 1) % 2; 11162306a36Sopenharmony_ci st->temps[st->tindex] = new_temp; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* Calculate integral term */ 11562306a36Sopenharmony_ci for (i = 0, integ = 0; i < hlen; i++) 11662306a36Sopenharmony_ci integ += st->errors[(st->index + hlen - i) % hlen]; 11762306a36Sopenharmony_ci integ *= st->param.interval; 11862306a36Sopenharmony_ci integ *= st->param.gr; 11962306a36Sopenharmony_ci sval = st->param.tmax - (s32)(integ >> 20); 12062306a36Sopenharmony_ci adj = min(st->param.ttarget, sval); 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj); 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci /* Calculate derivative term */ 12562306a36Sopenharmony_ci deriv = st->temps[st->tindex] - 12662306a36Sopenharmony_ci st->temps[(st->tindex + 2 - 1) % 2]; 12762306a36Sopenharmony_ci deriv /= st->param.interval; 12862306a36Sopenharmony_ci deriv *= st->param.gd; 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci /* Calculate proportional term */ 13162306a36Sopenharmony_ci prop = st->last_delta = (new_temp - adj); 13262306a36Sopenharmony_ci prop *= st->param.gp; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci DBG("deriv: %lx, prop: %lx\n", deriv, prop); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* Calculate target */ 13762306a36Sopenharmony_ci target = st->target + (s32)((deriv + prop) >> 36); 13862306a36Sopenharmony_ci target = max(target, st->param.min); 13962306a36Sopenharmony_ci target = min(target, st->param.max); 14062306a36Sopenharmony_ci st->target = target; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci return st->target; 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_cpu_pid_run); 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 14762306a36Sopenharmony_ciMODULE_DESCRIPTION("PID algorithm for PowerMacs thermal control"); 14862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 149