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