linux/drivers/macintosh/windfarm_pid.c
<<
>>
Prefs
   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
  26void 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}
  32EXPORT_SYMBOL_GPL(wf_pid_init);
  33
  34s32 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}
  78EXPORT_SYMBOL_GPL(wf_pid_run);
  79
  80void 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}
  87EXPORT_SYMBOL_GPL(wf_cpu_pid_init);
  88
  89s32 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}
 145EXPORT_SYMBOL_GPL(wf_cpu_pid_run);
 146
 147MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 148MODULE_DESCRIPTION("PID algorithm for PowerMacs thermal control");
 149MODULE_LICENSE("GPL");
 150