18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Windfarm PowerMac thermal control. Generic PID helpers 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 68c2ecf20Sopenharmony_ci * <benh@kernel.crashing.org> 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/errno.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci 158c2ecf20Sopenharmony_ci#include "windfarm_pid.h" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#undef DEBUG 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#ifdef DEBUG 208c2ecf20Sopenharmony_ci#define DBG(args...) printk(args) 218c2ecf20Sopenharmony_ci#else 228c2ecf20Sopenharmony_ci#define DBG(args...) do { } while(0) 238c2ecf20Sopenharmony_ci#endif 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_civoid wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param) 268c2ecf20Sopenharmony_ci{ 278c2ecf20Sopenharmony_ci memset(st, 0, sizeof(struct wf_pid_state)); 288c2ecf20Sopenharmony_ci st->param = *param; 298c2ecf20Sopenharmony_ci st->first = 1; 308c2ecf20Sopenharmony_ci} 318c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_pid_init); 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cis32 wf_pid_run(struct wf_pid_state *st, s32 new_sample) 348c2ecf20Sopenharmony_ci{ 358c2ecf20Sopenharmony_ci s64 error, integ, deriv; 368c2ecf20Sopenharmony_ci s32 target; 378c2ecf20Sopenharmony_ci int i, hlen = st->param.history_len; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci /* Calculate error term */ 408c2ecf20Sopenharmony_ci error = new_sample - st->param.itarget; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* Get samples into our history buffer */ 438c2ecf20Sopenharmony_ci if (st->first) { 448c2ecf20Sopenharmony_ci for (i = 0; i < hlen; i++) { 458c2ecf20Sopenharmony_ci st->samples[i] = new_sample; 468c2ecf20Sopenharmony_ci st->errors[i] = error; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci st->first = 0; 498c2ecf20Sopenharmony_ci st->index = 0; 508c2ecf20Sopenharmony_ci } else { 518c2ecf20Sopenharmony_ci st->index = (st->index + 1) % hlen; 528c2ecf20Sopenharmony_ci st->samples[st->index] = new_sample; 538c2ecf20Sopenharmony_ci st->errors[st->index] = error; 548c2ecf20Sopenharmony_ci } 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci /* Calculate integral term */ 578c2ecf20Sopenharmony_ci for (i = 0, integ = 0; i < hlen; i++) 588c2ecf20Sopenharmony_ci integ += st->errors[(st->index + hlen - i) % hlen]; 598c2ecf20Sopenharmony_ci integ *= st->param.interval; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci /* Calculate derivative term */ 628c2ecf20Sopenharmony_ci deriv = st->errors[st->index] - 638c2ecf20Sopenharmony_ci st->errors[(st->index + hlen - 1) % hlen]; 648c2ecf20Sopenharmony_ci deriv /= st->param.interval; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* Calculate target */ 678c2ecf20Sopenharmony_ci target = (s32)((integ * (s64)st->param.gr + deriv * (s64)st->param.gd + 688c2ecf20Sopenharmony_ci error * (s64)st->param.gp) >> 36); 698c2ecf20Sopenharmony_ci if (st->param.additive) 708c2ecf20Sopenharmony_ci target += st->target; 718c2ecf20Sopenharmony_ci target = max(target, st->param.min); 728c2ecf20Sopenharmony_ci target = min(target, st->param.max); 738c2ecf20Sopenharmony_ci st->target = target; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci return st->target; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_pid_run); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_civoid wf_cpu_pid_init(struct wf_cpu_pid_state *st, 808c2ecf20Sopenharmony_ci struct wf_cpu_pid_param *param) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci memset(st, 0, sizeof(struct wf_cpu_pid_state)); 838c2ecf20Sopenharmony_ci st->param = *param; 848c2ecf20Sopenharmony_ci st->first = 1; 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_cpu_pid_init); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cis32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci s64 integ, deriv, prop; 918c2ecf20Sopenharmony_ci s32 error, target, sval, adj; 928c2ecf20Sopenharmony_ci int i, hlen = st->param.history_len; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci /* Calculate error term */ 958c2ecf20Sopenharmony_ci error = st->param.pmaxadj - new_power; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* Get samples into our history buffer */ 988c2ecf20Sopenharmony_ci if (st->first) { 998c2ecf20Sopenharmony_ci for (i = 0; i < hlen; i++) { 1008c2ecf20Sopenharmony_ci st->powers[i] = new_power; 1018c2ecf20Sopenharmony_ci st->errors[i] = error; 1028c2ecf20Sopenharmony_ci } 1038c2ecf20Sopenharmony_ci st->temps[0] = st->temps[1] = new_temp; 1048c2ecf20Sopenharmony_ci st->first = 0; 1058c2ecf20Sopenharmony_ci st->index = st->tindex = 0; 1068c2ecf20Sopenharmony_ci } else { 1078c2ecf20Sopenharmony_ci st->index = (st->index + 1) % hlen; 1088c2ecf20Sopenharmony_ci st->powers[st->index] = new_power; 1098c2ecf20Sopenharmony_ci st->errors[st->index] = error; 1108c2ecf20Sopenharmony_ci st->tindex = (st->tindex + 1) % 2; 1118c2ecf20Sopenharmony_ci st->temps[st->tindex] = new_temp; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci /* Calculate integral term */ 1158c2ecf20Sopenharmony_ci for (i = 0, integ = 0; i < hlen; i++) 1168c2ecf20Sopenharmony_ci integ += st->errors[(st->index + hlen - i) % hlen]; 1178c2ecf20Sopenharmony_ci integ *= st->param.interval; 1188c2ecf20Sopenharmony_ci integ *= st->param.gr; 1198c2ecf20Sopenharmony_ci sval = st->param.tmax - (s32)(integ >> 20); 1208c2ecf20Sopenharmony_ci adj = min(st->param.ttarget, sval); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Calculate derivative term */ 1258c2ecf20Sopenharmony_ci deriv = st->temps[st->tindex] - 1268c2ecf20Sopenharmony_ci st->temps[(st->tindex + 2 - 1) % 2]; 1278c2ecf20Sopenharmony_ci deriv /= st->param.interval; 1288c2ecf20Sopenharmony_ci deriv *= st->param.gd; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Calculate proportional term */ 1318c2ecf20Sopenharmony_ci prop = st->last_delta = (new_temp - adj); 1328c2ecf20Sopenharmony_ci prop *= st->param.gp; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci DBG("deriv: %lx, prop: %lx\n", deriv, prop); 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci /* Calculate target */ 1378c2ecf20Sopenharmony_ci target = st->target + (s32)((deriv + prop) >> 36); 1388c2ecf20Sopenharmony_ci target = max(target, st->param.min); 1398c2ecf20Sopenharmony_ci target = min(target, st->param.max); 1408c2ecf20Sopenharmony_ci st->target = target; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci return st->target; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_cpu_pid_run); 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 1478c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("PID algorithm for PowerMacs thermal control"); 1488c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 149