linux/arch/powerpc/kernel/tau_6xx.c
<<
>>
Prefs
   1/*
   2 * temp.c       Thermal management for cpu's with Thermal Assist Units
   3 *
   4 * Written by Troy Benjegerdes <hozer@drgw.net>
   5 *
   6 * TODO:
   7 * dynamic power management to limit peak CPU temp (using ICTC)
   8 * calibration???
   9 *
  10 * Silly, crazy ideas: use cpu load (from scheduler) and ICTC to extend battery
  11 * life in portables, and add a 'performance/watt' metric somewhere in /proc
  12 */
  13
  14#include <linux/errno.h>
  15#include <linux/jiffies.h>
  16#include <linux/kernel.h>
  17#include <linux/param.h>
  18#include <linux/string.h>
  19#include <linux/mm.h>
  20#include <linux/interrupt.h>
  21#include <linux/init.h>
  22
  23#include <asm/io.h>
  24#include <asm/reg.h>
  25#include <asm/nvram.h>
  26#include <asm/cache.h>
  27#include <asm/8xx_immap.h>
  28#include <asm/machdep.h>
  29
  30static struct tau_temp
  31{
  32        int interrupts;
  33        unsigned char low;
  34        unsigned char high;
  35        unsigned char grew;
  36} tau[NR_CPUS];
  37
  38struct timer_list tau_timer;
  39
  40#undef DEBUG
  41
  42/* TODO: put these in a /proc interface, with some sanity checks, and maybe
  43 * dynamic adjustment to minimize # of interrupts */
  44/* configurable values for step size and how much to expand the window when
  45 * we get an interrupt. These are based on the limit that was out of range */
  46#define step_size               2       /* step size when temp goes out of range */
  47#define window_expand           1       /* expand the window by this much */
  48/* configurable values for shrinking the window */
  49#define shrink_timer    2*HZ    /* period between shrinking the window */
  50#define min_window      2       /* minimum window size, degrees C */
  51
  52void set_thresholds(unsigned long cpu)
  53{
  54#ifdef CONFIG_TAU_INT
  55        /*
  56         * setup THRM1,
  57         * threshold, valid bit, enable interrupts, interrupt when below threshold
  58         */
  59        mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TIE | THRM1_TID);
  60
  61        /* setup THRM2,
  62         * threshold, valid bit, enable interrupts, interrupt when above threshold
  63         */
  64        mtspr (SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V | THRM1_TIE);
  65#else
  66        /* same thing but don't enable interrupts */
  67        mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | THRM1_TID);
  68        mtspr(SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V);
  69#endif
  70}
  71
  72void TAUupdate(int cpu)
  73{
  74        unsigned thrm;
  75
  76#ifdef DEBUG
  77        printk("TAUupdate ");
  78#endif
  79
  80        /* if both thresholds are crossed, the step_sizes cancel out
  81         * and the window winds up getting expanded twice. */
  82        if((thrm = mfspr(SPRN_THRM1)) & THRM1_TIV){ /* is valid? */
  83                if(thrm & THRM1_TIN){ /* crossed low threshold */
  84                        if (tau[cpu].low >= step_size){
  85                                tau[cpu].low -= step_size;
  86                                tau[cpu].high -= (step_size - window_expand);
  87                        }
  88                        tau[cpu].grew = 1;
  89#ifdef DEBUG
  90                        printk("low threshold crossed ");
  91#endif
  92                }
  93        }
  94        if((thrm = mfspr(SPRN_THRM2)) & THRM1_TIV){ /* is valid? */
  95                if(thrm & THRM1_TIN){ /* crossed high threshold */
  96                        if (tau[cpu].high <= 127-step_size){
  97                                tau[cpu].low += (step_size - window_expand);
  98                                tau[cpu].high += step_size;
  99                        }
 100                        tau[cpu].grew = 1;
 101#ifdef DEBUG
 102                        printk("high threshold crossed ");
 103#endif
 104                }
 105        }
 106
 107#ifdef DEBUG
 108        printk("grew = %d\n", tau[cpu].grew);
 109#endif
 110
 111#ifndef CONFIG_TAU_INT /* tau_timeout will do this if not using interrupts */
 112        set_thresholds(cpu);
 113#endif
 114
 115}
 116
 117#ifdef CONFIG_TAU_INT
 118/*
 119 * TAU interrupts - called when we have a thermal assist unit interrupt
 120 * with interrupts disabled
 121 */
 122
 123void TAUException(struct pt_regs * regs)
 124{
 125        int cpu = smp_processor_id();
 126
 127        irq_enter();
 128        tau[cpu].interrupts++;
 129
 130        TAUupdate(cpu);
 131
 132        irq_exit();
 133}
 134#endif /* CONFIG_TAU_INT */
 135
 136static void tau_timeout(void * info)
 137{
 138        int cpu;
 139        unsigned long flags;
 140        int size;
 141        int shrink;
 142
 143        /* disabling interrupts *should* be okay */
 144        local_irq_save(flags);
 145        cpu = smp_processor_id();
 146
 147#ifndef CONFIG_TAU_INT
 148        TAUupdate(cpu);
 149#endif
 150
 151        size = tau[cpu].high - tau[cpu].low;
 152        if (size > min_window && ! tau[cpu].grew) {
 153                /* do an exponential shrink of half the amount currently over size */
 154                shrink = (2 + size - min_window) / 4;
 155                if (shrink) {
 156                        tau[cpu].low += shrink;
 157                        tau[cpu].high -= shrink;
 158                } else { /* size must have been min_window + 1 */
 159                        tau[cpu].low += 1;
 160#if 1 /* debug */
 161                        if ((tau[cpu].high - tau[cpu].low) != min_window){
 162                                printk(KERN_ERR "temp.c: line %d, logic error\n", __LINE__);
 163                        }
 164#endif
 165                }
 166        }
 167
 168        tau[cpu].grew = 0;
 169
 170        set_thresholds(cpu);
 171
 172        /*
 173         * Do the enable every time, since otherwise a bunch of (relatively)
 174         * complex sleep code needs to be added. One mtspr every time
 175         * tau_timeout is called is probably not a big deal.
 176         *
 177         * Enable thermal sensor and set up sample interval timer
 178         * need 20 us to do the compare.. until a nice 'cpu_speed' function
 179         * call is implemented, just assume a 500 mhz clock. It doesn't really
 180         * matter if we take too long for a compare since it's all interrupt
 181         * driven anyway.
 182         *
 183         * use a extra long time.. (60 us @ 500 mhz)
 184         */
 185        mtspr(SPRN_THRM3, THRM3_SITV(500*60) | THRM3_E);
 186
 187        local_irq_restore(flags);
 188}
 189
 190static void tau_timeout_smp(unsigned long unused)
 191{
 192
 193        /* schedule ourselves to be run again */
 194        mod_timer(&tau_timer, jiffies + shrink_timer) ;
 195        on_each_cpu(tau_timeout, NULL, 0);
 196}
 197
 198/*
 199 * setup the TAU
 200 *
 201 * Set things up to use THRM1 as a temperature lower bound, and THRM2 as an upper bound.
 202 * Start off at zero
 203 */
 204
 205int tau_initialized = 0;
 206
 207void __init TAU_init_smp(void * info)
 208{
 209        unsigned long cpu = smp_processor_id();
 210
 211        /* set these to a reasonable value and let the timer shrink the
 212         * window */
 213        tau[cpu].low = 5;
 214        tau[cpu].high = 120;
 215
 216        set_thresholds(cpu);
 217}
 218
 219int __init TAU_init(void)
 220{
 221        /* We assume in SMP that if one CPU has TAU support, they
 222         * all have it --BenH
 223         */
 224        if (!cpu_has_feature(CPU_FTR_TAU)) {
 225                printk("Thermal assist unit not available\n");
 226                tau_initialized = 0;
 227                return 1;
 228        }
 229
 230
 231        /* first, set up the window shrinking timer */
 232        init_timer(&tau_timer);
 233        tau_timer.function = tau_timeout_smp;
 234        tau_timer.expires = jiffies + shrink_timer;
 235        add_timer(&tau_timer);
 236
 237        on_each_cpu(TAU_init_smp, NULL, 0);
 238
 239        printk("Thermal assist unit ");
 240#ifdef CONFIG_TAU_INT
 241        printk("using interrupts, ");
 242#else
 243        printk("using timers, ");
 244#endif
 245        printk("shrink_timer: %d jiffies\n", shrink_timer);
 246        tau_initialized = 1;
 247
 248        return 0;
 249}
 250
 251__initcall(TAU_init);
 252
 253/*
 254 * return current temp
 255 */
 256
 257u32 cpu_temp_both(unsigned long cpu)
 258{
 259        return ((tau[cpu].high << 16) | tau[cpu].low);
 260}
 261
 262int cpu_temp(unsigned long cpu)
 263{
 264        return ((tau[cpu].high + tau[cpu].low) / 2);
 265}
 266
 267int tau_interrupts(unsigned long cpu)
 268{
 269        return (tau[cpu].interrupts);
 270}
 271