12c162f9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
275722d39SBenjamin Herrenschmidt /*
375722d39SBenjamin Herrenschmidt  * Windfarm PowerMac thermal control. Generic PID helpers
475722d39SBenjamin Herrenschmidt  *
575722d39SBenjamin Herrenschmidt  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
675722d39SBenjamin Herrenschmidt  *                    <benh@kernel.crashing.org>
775722d39SBenjamin Herrenschmidt  */
875722d39SBenjamin Herrenschmidt 
975722d39SBenjamin Herrenschmidt #include <linux/types.h>
1075722d39SBenjamin Herrenschmidt #include <linux/errno.h>
1175722d39SBenjamin Herrenschmidt #include <linux/kernel.h>
1275722d39SBenjamin Herrenschmidt #include <linux/string.h>
1375722d39SBenjamin Herrenschmidt #include <linux/module.h>
1475722d39SBenjamin Herrenschmidt 
1575722d39SBenjamin Herrenschmidt #include "windfarm_pid.h"
1675722d39SBenjamin Herrenschmidt 
1775722d39SBenjamin Herrenschmidt #undef DEBUG
1875722d39SBenjamin Herrenschmidt 
1975722d39SBenjamin Herrenschmidt #ifdef DEBUG
2075722d39SBenjamin Herrenschmidt #define DBG(args...)	printk(args)
2175722d39SBenjamin Herrenschmidt #else
2275722d39SBenjamin Herrenschmidt #define DBG(args...)	do { } while(0)
2375722d39SBenjamin Herrenschmidt #endif
2475722d39SBenjamin Herrenschmidt 
wf_pid_init(struct wf_pid_state * st,struct wf_pid_param * param)2575722d39SBenjamin Herrenschmidt void wf_pid_init(struct wf_pid_state *st, struct wf_pid_param *param)
2675722d39SBenjamin Herrenschmidt {
2775722d39SBenjamin Herrenschmidt 	memset(st, 0, sizeof(struct wf_pid_state));
2875722d39SBenjamin Herrenschmidt 	st->param = *param;
2975722d39SBenjamin Herrenschmidt 	st->first = 1;
3075722d39SBenjamin Herrenschmidt }
3175722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_pid_init);
3275722d39SBenjamin Herrenschmidt 
wf_pid_run(struct wf_pid_state * st,s32 new_sample)3375722d39SBenjamin Herrenschmidt s32 wf_pid_run(struct wf_pid_state *st, s32 new_sample)
3475722d39SBenjamin Herrenschmidt {
3575722d39SBenjamin Herrenschmidt 	s64	error, integ, deriv;
3675722d39SBenjamin Herrenschmidt 	s32	target;
3775722d39SBenjamin Herrenschmidt 	int	i, hlen = st->param.history_len;
3875722d39SBenjamin Herrenschmidt 
3975722d39SBenjamin Herrenschmidt 	/* Calculate error term */
4075722d39SBenjamin Herrenschmidt 	error = new_sample - st->param.itarget;
4175722d39SBenjamin Herrenschmidt 
4275722d39SBenjamin Herrenschmidt 	/* Get samples into our history buffer */
4375722d39SBenjamin Herrenschmidt 	if (st->first) {
4475722d39SBenjamin Herrenschmidt 		for (i = 0; i < hlen; i++) {
4575722d39SBenjamin Herrenschmidt 			st->samples[i] = new_sample;
4675722d39SBenjamin Herrenschmidt 			st->errors[i] = error;
4775722d39SBenjamin Herrenschmidt 		}
4875722d39SBenjamin Herrenschmidt 		st->first = 0;
4975722d39SBenjamin Herrenschmidt 		st->index = 0;
5075722d39SBenjamin Herrenschmidt 	} else {
5175722d39SBenjamin Herrenschmidt 		st->index = (st->index + 1) % hlen;
5275722d39SBenjamin Herrenschmidt 		st->samples[st->index] = new_sample;
5375722d39SBenjamin Herrenschmidt 		st->errors[st->index] = error;
5475722d39SBenjamin Herrenschmidt 	}
5575722d39SBenjamin Herrenschmidt 
5675722d39SBenjamin Herrenschmidt 	/* Calculate integral term */
5775722d39SBenjamin Herrenschmidt 	for (i = 0, integ = 0; i < hlen; i++)
5875722d39SBenjamin Herrenschmidt 		integ += st->errors[(st->index + hlen - i) % hlen];
5975722d39SBenjamin Herrenschmidt 	integ *= st->param.interval;
6075722d39SBenjamin Herrenschmidt 
6175722d39SBenjamin Herrenschmidt 	/* Calculate derivative term */
6275722d39SBenjamin Herrenschmidt 	deriv = st->errors[st->index] -
6375722d39SBenjamin Herrenschmidt 		st->errors[(st->index + hlen - 1) % hlen];
6475722d39SBenjamin Herrenschmidt 	deriv /= st->param.interval;
6575722d39SBenjamin Herrenschmidt 
6675722d39SBenjamin Herrenschmidt 	/* Calculate target */
6775722d39SBenjamin Herrenschmidt 	target = (s32)((integ * (s64)st->param.gr + deriv * (s64)st->param.gd +
6875722d39SBenjamin Herrenschmidt 		  error * (s64)st->param.gp) >> 36);
6975722d39SBenjamin Herrenschmidt 	if (st->param.additive)
7075722d39SBenjamin Herrenschmidt 		target += st->target;
7175722d39SBenjamin Herrenschmidt 	target = max(target, st->param.min);
7275722d39SBenjamin Herrenschmidt 	target = min(target, st->param.max);
7375722d39SBenjamin Herrenschmidt 	st->target = target;
7475722d39SBenjamin Herrenschmidt 
7575722d39SBenjamin Herrenschmidt 	return st->target;
7675722d39SBenjamin Herrenschmidt }
7775722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_pid_run);
7875722d39SBenjamin Herrenschmidt 
wf_cpu_pid_init(struct wf_cpu_pid_state * st,struct wf_cpu_pid_param * param)7975722d39SBenjamin Herrenschmidt void wf_cpu_pid_init(struct wf_cpu_pid_state *st,
8075722d39SBenjamin Herrenschmidt 		     struct wf_cpu_pid_param *param)
8175722d39SBenjamin Herrenschmidt {
8275722d39SBenjamin Herrenschmidt 	memset(st, 0, sizeof(struct wf_cpu_pid_state));
8375722d39SBenjamin Herrenschmidt 	st->param = *param;
8475722d39SBenjamin Herrenschmidt 	st->first = 1;
8575722d39SBenjamin Herrenschmidt }
8675722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_cpu_pid_init);
8775722d39SBenjamin Herrenschmidt 
wf_cpu_pid_run(struct wf_cpu_pid_state * st,s32 new_power,s32 new_temp)8875722d39SBenjamin Herrenschmidt s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
8975722d39SBenjamin Herrenschmidt {
90ac171c46SBenjamin Herrenschmidt 	s64	integ, deriv, prop;
91ac171c46SBenjamin Herrenschmidt 	s32	error, target, sval, adj;
9275722d39SBenjamin Herrenschmidt 	int	i, hlen = st->param.history_len;
9375722d39SBenjamin Herrenschmidt 
9475722d39SBenjamin Herrenschmidt 	/* Calculate error term */
9575722d39SBenjamin Herrenschmidt 	error = st->param.pmaxadj - new_power;
9675722d39SBenjamin Herrenschmidt 
9775722d39SBenjamin Herrenschmidt 	/* Get samples into our history buffer */
9875722d39SBenjamin Herrenschmidt 	if (st->first) {
9975722d39SBenjamin Herrenschmidt 		for (i = 0; i < hlen; i++) {
10075722d39SBenjamin Herrenschmidt 			st->powers[i] = new_power;
10175722d39SBenjamin Herrenschmidt 			st->errors[i] = error;
10275722d39SBenjamin Herrenschmidt 		}
10375722d39SBenjamin Herrenschmidt 		st->temps[0] = st->temps[1] = new_temp;
10475722d39SBenjamin Herrenschmidt 		st->first = 0;
10575722d39SBenjamin Herrenschmidt 		st->index = st->tindex = 0;
10675722d39SBenjamin Herrenschmidt 	} else {
10775722d39SBenjamin Herrenschmidt 		st->index = (st->index + 1) % hlen;
10875722d39SBenjamin Herrenschmidt 		st->powers[st->index] = new_power;
10975722d39SBenjamin Herrenschmidt 		st->errors[st->index] = error;
11075722d39SBenjamin Herrenschmidt 		st->tindex = (st->tindex + 1) % 2;
11175722d39SBenjamin Herrenschmidt 		st->temps[st->tindex] = new_temp;
11275722d39SBenjamin Herrenschmidt 	}
11375722d39SBenjamin Herrenschmidt 
11475722d39SBenjamin Herrenschmidt 	/* Calculate integral term */
11575722d39SBenjamin Herrenschmidt 	for (i = 0, integ = 0; i < hlen; i++)
11675722d39SBenjamin Herrenschmidt 		integ += st->errors[(st->index + hlen - i) % hlen];
11775722d39SBenjamin Herrenschmidt 	integ *= st->param.interval;
11875722d39SBenjamin Herrenschmidt 	integ *= st->param.gr;
119ac171c46SBenjamin Herrenschmidt 	sval = st->param.tmax - (s32)(integ >> 20);
12075722d39SBenjamin Herrenschmidt 	adj = min(st->param.ttarget, sval);
12175722d39SBenjamin Herrenschmidt 
12275722d39SBenjamin Herrenschmidt 	DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj);
12375722d39SBenjamin Herrenschmidt 
12475722d39SBenjamin Herrenschmidt 	/* Calculate derivative term */
12575722d39SBenjamin Herrenschmidt 	deriv = st->temps[st->tindex] -
12675722d39SBenjamin Herrenschmidt 		st->temps[(st->tindex + 2 - 1) % 2];
12775722d39SBenjamin Herrenschmidt 	deriv /= st->param.interval;
12875722d39SBenjamin Herrenschmidt 	deriv *= st->param.gd;
12975722d39SBenjamin Herrenschmidt 
13075722d39SBenjamin Herrenschmidt 	/* Calculate proportional term */
131ac171c46SBenjamin Herrenschmidt 	prop = st->last_delta = (new_temp - adj);
13275722d39SBenjamin Herrenschmidt 	prop *= st->param.gp;
13375722d39SBenjamin Herrenschmidt 
13475722d39SBenjamin Herrenschmidt 	DBG("deriv: %lx, prop: %lx\n", deriv, prop);
13575722d39SBenjamin Herrenschmidt 
13675722d39SBenjamin Herrenschmidt 	/* Calculate target */
13775722d39SBenjamin Herrenschmidt 	target = st->target + (s32)((deriv + prop) >> 36);
13875722d39SBenjamin Herrenschmidt 	target = max(target, st->param.min);
13975722d39SBenjamin Herrenschmidt 	target = min(target, st->param.max);
14075722d39SBenjamin Herrenschmidt 	st->target = target;
14175722d39SBenjamin Herrenschmidt 
14275722d39SBenjamin Herrenschmidt 	return st->target;
14375722d39SBenjamin Herrenschmidt }
14475722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_cpu_pid_run);
145cdd440feSBenjamin Herrenschmidt 
146cdd440feSBenjamin Herrenschmidt MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
147cdd440feSBenjamin Herrenschmidt MODULE_DESCRIPTION("PID algorithm for PowerMacs thermal control");
148cdd440feSBenjamin Herrenschmidt MODULE_LICENSE("GPL");
149