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