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