p4tcc.c revision 134199
1/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ 2/* 3 * Copyright (c) 2003 Ted Unangst 4 * Copyright (c) 2004 Maxim Sobolev <sobomax@FreeBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28/* 29 * Restrict power consumption by using thermal control circuit. 30 * This operates independently of speedstep. 31 * Found on Pentium 4 and later models (feature TM). 32 * 33 * References: 34 * Intel Developer's manual v.3 #245472-012 35 * 36 * On some models, the cpu can hang if it's running at a slow speed. 37 * Workarounds included below. 38 */ 39 40#include <sys/cdefs.h> 41__FBSDID("$FreeBSD: head/sys/i386/cpufreq/p4tcc.c 134199 2004-08-23 09:47:56Z sobomax $"); 42 43#include "opt_cpu.h" 44#include <sys/param.h> 45#include <sys/systm.h> 46#include <sys/kernel.h> 47#include <sys/conf.h> 48#include <sys/power.h> 49#include <sys/sysctl.h> 50#include <sys/types.h> 51 52#include <machine/md_var.h> 53#include <machine/specialreg.h> 54 55static u_int p4tcc_percentage; 56static u_int p4tcc_economy; 57static u_int p4tcc_performance; 58static struct sysctl_ctx_list p4tcc_sysctl_ctx; 59static struct sysctl_oid *p4tcc_sysctl_tree; 60 61static struct { 62 u_short level; 63 u_short rlevel; 64 u_short reg; 65} tcc[] = { 66 { 88, 100, 0 }, 67 { 75, 88, 7 }, 68 { 63, 75, 6 }, 69 { 50, 63, 5 }, 70 { 38, 50, 4 }, 71 { 25, 38, 3 }, 72 { 13, 25, 2 }, 73 { 0, 13, 1 } 74}; 75 76#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) 77 78static u_short 79p4tcc_getperf(void) 80{ 81 u_int64_t msreg; 82 int i; 83 84 msreg = rdmsr(MSR_THERM_CONTROL); 85 msreg = (msreg >> 1) & 0x07; 86 for (i = 0; i < TCC_LEVELS; i++) { 87 if (msreg == tcc[i].reg) 88 break; 89 } 90 91 return (tcc[i].rlevel); 92} 93 94static void 95p4tcc_setperf(u_int percentage) 96{ 97 int i; 98 u_int64_t msreg; 99 100 if (percentage > tcc[0].rlevel) 101 percentage = tcc[0].rlevel; 102 for (i = 0; i < TCC_LEVELS - 1; i++) { 103 if (percentage > tcc[i].level) 104 break; 105 } 106 107 msreg = rdmsr(MSR_THERM_CONTROL); 108 msreg &= ~0x1e; /* bit 0 reserved */ 109 if (tcc[i].reg != 0) 110 msreg |= tcc[i].reg << 1 | 1 << 4; 111 wrmsr(MSR_THERM_CONTROL, msreg); 112} 113 114static int 115p4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS) 116{ 117 u_int percentage; 118 int error; 119 120 p4tcc_percentage = p4tcc_getperf(); 121 percentage = p4tcc_percentage; 122 error = sysctl_handle_int(oidp, &percentage, 0, req); 123 if (error || !req->newptr) { 124 return (error); 125 } 126 if (p4tcc_percentage != percentage) { 127 p4tcc_setperf(percentage); 128 } 129 130 return (error); 131} 132 133static void 134p4tcc_power_profile(void *arg) 135{ 136 int state; 137 u_int new; 138 139 state = power_profile_get_state(); 140 if (state != POWER_PROFILE_PERFORMANCE && 141 state != POWER_PROFILE_ECONOMY) { 142 return; 143 } 144 145 switch (state) { 146 case POWER_PROFILE_PERFORMANCE: 147 new = p4tcc_performance; 148 break; 149 case POWER_PROFILE_ECONOMY: 150 new = p4tcc_economy; 151 break; 152 default: 153 new = p4tcc_getperf(); 154 break; 155 } 156 157 if (p4tcc_getperf() != new) { 158 p4tcc_setperf(new); 159 } 160} 161 162static int 163p4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) 164{ 165 u_int32_t *argp; 166 u_int32_t arg; 167 int error; 168 169 argp = (u_int32_t *)oidp->oid_arg1; 170 arg = *argp; 171 error = sysctl_handle_int(oidp, &arg, 0, req); 172 173 /* error or no new value */ 174 if ((error != 0) || (req->newptr == NULL)) 175 return (error); 176 177 /* range check */ 178 if (arg > tcc[0].rlevel) 179 arg = tcc[0].rlevel; 180 181 /* set new value and possibly switch */ 182 *argp = arg; 183 184 p4tcc_power_profile(NULL); 185 186 *argp = p4tcc_getperf(); 187 188 return (0); 189} 190 191static void 192setup_p4tcc(void *dummy __unused) 193{ 194 int nsteps, i; 195 static char p4tcc_levels[(3 * TCC_LEVELS) + 1]; 196 char buf[4 + 1]; 197 198 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 199 (CPUID_ACPI | CPUID_TM)) 200 return; 201 202 nsteps = TCC_LEVELS; 203 switch (cpu_id & 0xf) { 204 case 0x22: /* errata O50 P44 and Z21 */ 205 case 0x24: 206 case 0x25: 207 case 0x27: 208 case 0x29: 209 /* hang with 12.5 */ 210 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2]; 211 nsteps -= 1; 212 break; 213 case 0x07: /* errata N44 and P18 */ 214 case 0x0a: 215 case 0x12: 216 case 0x13: 217 /* hang at 12.5 and 25 */ 218 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3]; 219 nsteps -= 2; 220 break; 221 default: 222 break; 223 } 224 225 p4tcc_levels[0] = '\0'; 226 for (i = nsteps; i > 0; i--) { 227 sprintf(buf, "%d%s", tcc[i - 1].rlevel, (i != 1) ? " " : ""); 228 strcat(p4tcc_levels, buf); 229 } 230 231 p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel; 232 p4tcc_performance = tcc[0].rlevel; 233 234 p4tcc_percentage = p4tcc_getperf(); 235 printf("Pentium 4 TCC support enabled, %d steps from 100%% to %d%%, " 236 "current performance %u%%\n", nsteps, p4tcc_economy, 237 p4tcc_percentage); 238 239 sysctl_ctx_init(&p4tcc_sysctl_ctx); 240 p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx, 241 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0, 242 "Pentium 4 Thermal Control Circuitry support"); 243 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 244 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 245 "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 246 &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 247 "CPU performance in % of maximum"); 248 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 249 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 250 "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 251 &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 252 "CPU performance in % of maximum in Performance mode"); 253 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 254 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 255 "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 256 &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 257 "CPU performance in % of maximum in Economy mode"); 258 SYSCTL_ADD_STRING(&p4tcc_sysctl_ctx, 259 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 260 "cpuperf_levels", CTLFLAG_RD, p4tcc_levels, 0, 261 "Perormance levels supported by the Pentium 4 Thermal Control " 262 "Circuitry"); 263 264 /* register performance profile change handler */ 265 EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 266} 267SYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 268