tau_6xx.c (5690B)
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/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#include <linux/delay.h> 23#include <linux/workqueue.h> 24 25#include <asm/interrupt.h> 26#include <asm/io.h> 27#include <asm/reg.h> 28#include <asm/nvram.h> 29#include <asm/cache.h> 30#include <asm/8xx_immap.h> 31#include <asm/machdep.h> 32 33#include "setup.h" 34 35static struct tau_temp 36{ 37 int interrupts; 38 unsigned char low; 39 unsigned char high; 40 unsigned char grew; 41} tau[NR_CPUS]; 42 43static bool tau_int_enable; 44 45/* TODO: put these in a /proc interface, with some sanity checks, and maybe 46 * dynamic adjustment to minimize # of interrupts */ 47/* configurable values for step size and how much to expand the window when 48 * we get an interrupt. These are based on the limit that was out of range */ 49#define step_size 2 /* step size when temp goes out of range */ 50#define window_expand 1 /* expand the window by this much */ 51/* configurable values for shrinking the window */ 52#define shrink_timer 2000 /* period between shrinking the window */ 53#define min_window 2 /* minimum window size, degrees C */ 54 55static void set_thresholds(unsigned long cpu) 56{ 57 u32 maybe_tie = tau_int_enable ? THRM1_TIE : 0; 58 59 /* setup THRM1, threshold, valid bit, interrupt when below threshold */ 60 mtspr(SPRN_THRM1, THRM1_THRES(tau[cpu].low) | THRM1_V | maybe_tie | THRM1_TID); 61 62 /* setup THRM2, threshold, valid bit, interrupt when above threshold */ 63 mtspr(SPRN_THRM2, THRM1_THRES(tau[cpu].high) | THRM1_V | maybe_tie); 64} 65 66static void TAUupdate(int cpu) 67{ 68 u32 thrm; 69 u32 bits = THRM1_TIV | THRM1_TIN | THRM1_V; 70 71 /* if both thresholds are crossed, the step_sizes cancel out 72 * and the window winds up getting expanded twice. */ 73 thrm = mfspr(SPRN_THRM1); 74 if ((thrm & bits) == bits) { 75 mtspr(SPRN_THRM1, 0); 76 77 if (tau[cpu].low >= step_size) { 78 tau[cpu].low -= step_size; 79 tau[cpu].high -= (step_size - window_expand); 80 } 81 tau[cpu].grew = 1; 82 pr_debug("%s: low threshold crossed\n", __func__); 83 } 84 thrm = mfspr(SPRN_THRM2); 85 if ((thrm & bits) == bits) { 86 mtspr(SPRN_THRM2, 0); 87 88 if (tau[cpu].high <= 127 - step_size) { 89 tau[cpu].low += (step_size - window_expand); 90 tau[cpu].high += step_size; 91 } 92 tau[cpu].grew = 1; 93 pr_debug("%s: high threshold crossed\n", __func__); 94 } 95} 96 97#ifdef CONFIG_TAU_INT 98/* 99 * TAU interrupts - called when we have a thermal assist unit interrupt 100 * with interrupts disabled 101 */ 102 103DEFINE_INTERRUPT_HANDLER_ASYNC(TAUException) 104{ 105 int cpu = smp_processor_id(); 106 107 tau[cpu].interrupts++; 108 109 TAUupdate(cpu); 110} 111#endif /* CONFIG_TAU_INT */ 112 113static void tau_timeout(void * info) 114{ 115 int cpu; 116 int size; 117 int shrink; 118 119 cpu = smp_processor_id(); 120 121 if (!tau_int_enable) 122 TAUupdate(cpu); 123 124 /* Stop thermal sensor comparisons and interrupts */ 125 mtspr(SPRN_THRM3, 0); 126 127 size = tau[cpu].high - tau[cpu].low; 128 if (size > min_window && ! tau[cpu].grew) { 129 /* do an exponential shrink of half the amount currently over size */ 130 shrink = (2 + size - min_window) / 4; 131 if (shrink) { 132 tau[cpu].low += shrink; 133 tau[cpu].high -= shrink; 134 } else { /* size must have been min_window + 1 */ 135 tau[cpu].low += 1; 136#if 1 /* debug */ 137 if ((tau[cpu].high - tau[cpu].low) != min_window){ 138 printk(KERN_ERR "temp.c: line %d, logic error\n", __LINE__); 139 } 140#endif 141 } 142 } 143 144 tau[cpu].grew = 0; 145 146 set_thresholds(cpu); 147 148 /* Restart thermal sensor comparisons and interrupts. 149 * The "PowerPC 740 and PowerPC 750 Microprocessor Datasheet" 150 * recommends that "the maximum value be set in THRM3 under all 151 * conditions." 152 */ 153 mtspr(SPRN_THRM3, THRM3_SITV(0x1fff) | THRM3_E); 154} 155 156static struct workqueue_struct *tau_workq; 157 158static void tau_work_func(struct work_struct *work) 159{ 160 msleep(shrink_timer); 161 on_each_cpu(tau_timeout, NULL, 0); 162 /* schedule ourselves to be run again */ 163 queue_work(tau_workq, work); 164} 165 166static DECLARE_WORK(tau_work, tau_work_func); 167 168/* 169 * setup the TAU 170 * 171 * Set things up to use THRM1 as a temperature lower bound, and THRM2 as an upper bound. 172 * Start off at zero 173 */ 174 175int tau_initialized = 0; 176 177static void __init TAU_init_smp(void *info) 178{ 179 unsigned long cpu = smp_processor_id(); 180 181 /* set these to a reasonable value and let the timer shrink the 182 * window */ 183 tau[cpu].low = 5; 184 tau[cpu].high = 120; 185 186 set_thresholds(cpu); 187} 188 189static int __init TAU_init(void) 190{ 191 /* We assume in SMP that if one CPU has TAU support, they 192 * all have it --BenH 193 */ 194 if (!cpu_has_feature(CPU_FTR_TAU)) { 195 printk("Thermal assist unit not available\n"); 196 tau_initialized = 0; 197 return 1; 198 } 199 200 tau_int_enable = IS_ENABLED(CONFIG_TAU_INT) && 201 !strcmp(cur_cpu_spec->platform, "ppc750"); 202 203 tau_workq = alloc_workqueue("tau", WQ_UNBOUND, 1); 204 if (!tau_workq) 205 return -ENOMEM; 206 207 on_each_cpu(TAU_init_smp, NULL, 0); 208 209 queue_work(tau_workq, &tau_work); 210 211 pr_info("Thermal assist unit using %s, shrink_timer: %d ms\n", 212 tau_int_enable ? "interrupts" : "workqueue", shrink_timer); 213 tau_initialized = 1; 214 215 return 0; 216} 217 218__initcall(TAU_init); 219 220/* 221 * return current temp 222 */ 223 224u32 cpu_temp_both(unsigned long cpu) 225{ 226 return ((tau[cpu].high << 16) | tau[cpu].low); 227} 228 229u32 cpu_temp(unsigned long cpu) 230{ 231 return ((tau[cpu].high + tau[cpu].low) / 2); 232} 233 234u32 tau_interrupts(unsigned long cpu) 235{ 236 return (tau[cpu].interrupts); 237}