p4tcc.c revision 134200
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 134200 2004-08-23 10:09:29Z 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 u_int new; 137 138 switch (power_profile_get_state()) { 139 case POWER_PROFILE_PERFORMANCE: 140 new = p4tcc_performance; 141 break; 142 case POWER_PROFILE_ECONOMY: 143 new = p4tcc_economy; 144 break; 145 default: 146 return; 147 } 148 149 if (p4tcc_getperf() != new) { 150 p4tcc_setperf(new); 151 } 152} 153 154static int 155p4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) 156{ 157 u_int32_t *argp; 158 u_int32_t arg; 159 int error; 160 161 argp = (u_int32_t *)oidp->oid_arg1; 162 arg = *argp; 163 error = sysctl_handle_int(oidp, &arg, 0, req); 164 165 /* error or no new value */ 166 if ((error != 0) || (req->newptr == NULL)) 167 return (error); 168 169 /* range check */ 170 if (arg > tcc[0].rlevel) 171 arg = tcc[0].rlevel; 172 173 /* set new value and possibly switch */ 174 *argp = arg; 175 176 p4tcc_power_profile(NULL); 177 178 *argp = p4tcc_getperf(); 179 180 return (0); 181} 182 183static void 184setup_p4tcc(void *dummy __unused) 185{ 186 int nsteps, i; 187 static char p4tcc_levels[(3 * TCC_LEVELS) + 1]; 188 char buf[4 + 1]; 189 190 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 191 (CPUID_ACPI | CPUID_TM)) 192 return; 193 194 nsteps = TCC_LEVELS; 195 switch (cpu_id & 0xf) { 196 case 0x22: /* errata O50 P44 and Z21 */ 197 case 0x24: 198 case 0x25: 199 case 0x27: 200 case 0x29: 201 /* hang with 12.5 */ 202 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2]; 203 nsteps -= 1; 204 break; 205 case 0x07: /* errata N44 and P18 */ 206 case 0x0a: 207 case 0x12: 208 case 0x13: 209 /* hang at 12.5 and 25 */ 210 tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3]; 211 nsteps -= 2; 212 break; 213 default: 214 break; 215 } 216 217 p4tcc_levels[0] = '\0'; 218 for (i = nsteps; i > 0; i--) { 219 sprintf(buf, "%d%s", tcc[i - 1].rlevel, (i != 1) ? " " : ""); 220 strcat(p4tcc_levels, buf); 221 } 222 223 p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel; 224 p4tcc_performance = tcc[0].rlevel; 225 226 p4tcc_percentage = p4tcc_getperf(); 227 printf("Pentium 4 TCC support enabled, %d steps from 100%% to %d%%, " 228 "current performance %u%%\n", nsteps, p4tcc_economy, 229 p4tcc_percentage); 230 231 sysctl_ctx_init(&p4tcc_sysctl_ctx); 232 p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx, 233 SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0, 234 "Pentium 4 Thermal Control Circuitry support"); 235 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 236 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 237 "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 238 &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 239 "CPU performance in % of maximum"); 240 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 241 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 242 "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 243 &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 244 "CPU performance in % of maximum in Performance mode"); 245 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 246 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 247 "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 248 &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 249 "CPU performance in % of maximum in Economy mode"); 250 SYSCTL_ADD_STRING(&p4tcc_sysctl_ctx, 251 SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 252 "cpuperf_levels", CTLFLAG_RD, p4tcc_levels, 0, 253 "Perormance levels supported by the Pentium 4 Thermal Control " 254 "Circuitry"); 255 256 /* register performance profile change handler */ 257 EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 258} 259SYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 260