p4tcc.c revision 124684
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 124684 2004-01-18 21:06: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 = 13; 57static u_int p4tcc_performance = 100; 58static struct sysctl_ctx_list p4tcc_sysctl_ctx; 59 60static struct { 61 u_short level; 62 u_short rlevel; 63 u_short reg; 64} tcc[] = { 65 { 88, 100, 0 }, 66 { 75, 88, 7 }, 67 { 63, 75, 6 }, 68 { 50, 63, 5 }, 69 { 38, 50, 4 }, 70 { 25, 38, 3 }, 71 { 13, 25, 2 }, 72 { 0, 13, 1 } 73}; 74 75#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) 76#define TCC_MAXPERF 100 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_MAXPERF) 101 percentage = TCC_MAXPERF; 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_MAXPERF) 179 arg = TCC_MAXPERF; 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 195 if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 196 (CPUID_ACPI | CPUID_TM)) 197 return; 198 199 switch (cpu_id & 0xf) { 200 case 0x22: /* errata O50 P44 and Z21 */ 201 case 0x24: 202 case 0x25: 203 case 0x27: 204 case 0x29: 205 /* hang with 12.5 */ 206 tcc[TCC_LEVELS - 1].reg = 2; 207 break; 208 case 0x07: /* errata N44 and P18 */ 209 case 0x0a: 210 case 0x12: 211 case 0x13: 212 /* hang at 12.5 and 25 */ 213 tcc[TCC_LEVELS - 1].reg = 3; 214 tcc[TCC_LEVELS - 2].reg = 3; 215 break; 216 default: 217 break; 218 } 219 220 p4tcc_percentage = p4tcc_getperf(); 221 printf("Pentium 4 TCC support enabled, current performance %u%%\n", 222 p4tcc_percentage); 223 224 sysctl_ctx_init(&p4tcc_sysctl_ctx); 225 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 226 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 227 "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 228 &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 229 "CPU performance in % of maximum"); 230 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 231 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 232 "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 233 &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 234 "CPU performance in % of maximum in Performance mode"); 235 SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 236 SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 237 "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 238 &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 239 "CPU performance in % of maximum in Economy mode"); 240 241 /* register performance profile change handler */ 242 EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 243} 244SYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 245