p4tcc.c revision 139724
1124684Ssobomax/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ 2139724Simp/*- 3124684Ssobomax * Copyright (c) 2003 Ted Unangst 4124684Ssobomax * Copyright (c) 2004 Maxim Sobolev <sobomax@FreeBSD.org> 5124684Ssobomax * All rights reserved. 6124684Ssobomax * 7124684Ssobomax * Redistribution and use in source and binary forms, with or without 8124684Ssobomax * modification, are permitted provided that the following conditions 9124684Ssobomax * are met: 10124684Ssobomax * 1. Redistributions of source code must retain the above copyright 11124684Ssobomax * notice, this list of conditions and the following disclaimer. 12124684Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 13124684Ssobomax * notice, this list of conditions and the following disclaimer in the 14124684Ssobomax * documentation and/or other materials provided with the distribution. 15124684Ssobomax * 16124684Ssobomax * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17124684Ssobomax * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18124684Ssobomax * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19124684Ssobomax * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20124684Ssobomax * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21124684Ssobomax * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22124684Ssobomax * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23124684Ssobomax * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24124684Ssobomax * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25124684Ssobomax * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26124684Ssobomax * SUCH DAMAGE. 27124684Ssobomax */ 28124684Ssobomax/* 29124684Ssobomax * Restrict power consumption by using thermal control circuit. 30124684Ssobomax * This operates independently of speedstep. 31124684Ssobomax * Found on Pentium 4 and later models (feature TM). 32124684Ssobomax * 33124684Ssobomax * References: 34124684Ssobomax * Intel Developer's manual v.3 #245472-012 35124684Ssobomax * 36124684Ssobomax * On some models, the cpu can hang if it's running at a slow speed. 37124684Ssobomax * Workarounds included below. 38124684Ssobomax */ 39124684Ssobomax 40124684Ssobomax#include <sys/cdefs.h> 41124684Ssobomax__FBSDID("$FreeBSD: head/sys/i386/cpufreq/p4tcc.c 139724 2005-01-05 19:10:48Z imp $"); 42124684Ssobomax 43124684Ssobomax#include "opt_cpu.h" 44124684Ssobomax#include <sys/param.h> 45124684Ssobomax#include <sys/systm.h> 46124684Ssobomax#include <sys/kernel.h> 47124684Ssobomax#include <sys/conf.h> 48124684Ssobomax#include <sys/power.h> 49124684Ssobomax#include <sys/sysctl.h> 50124684Ssobomax#include <sys/types.h> 51124684Ssobomax 52124684Ssobomax#include <machine/md_var.h> 53124684Ssobomax#include <machine/specialreg.h> 54124684Ssobomax 55124684Ssobomaxstatic u_int p4tcc_percentage; 56124930Ssobomaxstatic u_int p4tcc_economy; 57124930Ssobomaxstatic u_int p4tcc_performance; 58124684Ssobomaxstatic struct sysctl_ctx_list p4tcc_sysctl_ctx; 59124930Ssobomaxstatic struct sysctl_oid *p4tcc_sysctl_tree; 60124684Ssobomax 61124684Ssobomaxstatic struct { 62124684Ssobomax u_short level; 63124684Ssobomax u_short rlevel; 64124684Ssobomax u_short reg; 65124684Ssobomax} tcc[] = { 66124684Ssobomax { 88, 100, 0 }, 67124684Ssobomax { 75, 88, 7 }, 68124684Ssobomax { 63, 75, 6 }, 69124684Ssobomax { 50, 63, 5 }, 70124684Ssobomax { 38, 50, 4 }, 71124684Ssobomax { 25, 38, 3 }, 72124684Ssobomax { 13, 25, 2 }, 73124684Ssobomax { 0, 13, 1 } 74124684Ssobomax}; 75124684Ssobomax 76124684Ssobomax#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) 77124684Ssobomax 78124684Ssobomaxstatic u_short 79124684Ssobomaxp4tcc_getperf(void) 80124684Ssobomax{ 81124684Ssobomax u_int64_t msreg; 82124684Ssobomax int i; 83124684Ssobomax 84124684Ssobomax msreg = rdmsr(MSR_THERM_CONTROL); 85124684Ssobomax msreg = (msreg >> 1) & 0x07; 86124684Ssobomax for (i = 0; i < TCC_LEVELS; i++) { 87124684Ssobomax if (msreg == tcc[i].reg) 88124684Ssobomax break; 89124684Ssobomax } 90124684Ssobomax 91124684Ssobomax return (tcc[i].rlevel); 92124684Ssobomax} 93124684Ssobomax 94124684Ssobomaxstatic void 95124684Ssobomaxp4tcc_setperf(u_int percentage) 96124684Ssobomax{ 97124684Ssobomax int i; 98124684Ssobomax u_int64_t msreg; 99124684Ssobomax 100124930Ssobomax if (percentage > tcc[0].rlevel) 101124930Ssobomax percentage = tcc[0].rlevel; 102124684Ssobomax for (i = 0; i < TCC_LEVELS - 1; i++) { 103124684Ssobomax if (percentage > tcc[i].level) 104124684Ssobomax break; 105124684Ssobomax } 106124684Ssobomax 107124684Ssobomax msreg = rdmsr(MSR_THERM_CONTROL); 108124684Ssobomax msreg &= ~0x1e; /* bit 0 reserved */ 109124684Ssobomax if (tcc[i].reg != 0) 110124684Ssobomax msreg |= tcc[i].reg << 1 | 1 << 4; 111124684Ssobomax wrmsr(MSR_THERM_CONTROL, msreg); 112124684Ssobomax} 113124684Ssobomax 114124684Ssobomaxstatic int 115124684Ssobomaxp4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS) 116124684Ssobomax{ 117124684Ssobomax u_int percentage; 118124684Ssobomax int error; 119124684Ssobomax 120124684Ssobomax p4tcc_percentage = p4tcc_getperf(); 121124684Ssobomax percentage = p4tcc_percentage; 122124684Ssobomax error = sysctl_handle_int(oidp, &percentage, 0, req); 123124684Ssobomax if (error || !req->newptr) { 124124684Ssobomax return (error); 125124684Ssobomax } 126124684Ssobomax if (p4tcc_percentage != percentage) { 127124684Ssobomax p4tcc_setperf(percentage); 128124684Ssobomax } 129124684Ssobomax 130124684Ssobomax return (error); 131124684Ssobomax} 132124684Ssobomax 133124684Ssobomaxstatic void 134124684Ssobomaxp4tcc_power_profile(void *arg) 135124684Ssobomax{ 136124684Ssobomax u_int new; 137124684Ssobomax 138134200Ssobomax switch (power_profile_get_state()) { 139124684Ssobomax case POWER_PROFILE_PERFORMANCE: 140124684Ssobomax new = p4tcc_performance; 141124684Ssobomax break; 142124684Ssobomax case POWER_PROFILE_ECONOMY: 143124684Ssobomax new = p4tcc_economy; 144124684Ssobomax break; 145124684Ssobomax default: 146134200Ssobomax return; 147124684Ssobomax } 148124684Ssobomax 149124684Ssobomax if (p4tcc_getperf() != new) { 150124684Ssobomax p4tcc_setperf(new); 151124684Ssobomax } 152124684Ssobomax} 153124684Ssobomax 154124684Ssobomaxstatic int 155124684Ssobomaxp4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) 156124684Ssobomax{ 157124684Ssobomax u_int32_t *argp; 158124684Ssobomax u_int32_t arg; 159124684Ssobomax int error; 160124684Ssobomax 161124684Ssobomax argp = (u_int32_t *)oidp->oid_arg1; 162124684Ssobomax arg = *argp; 163124684Ssobomax error = sysctl_handle_int(oidp, &arg, 0, req); 164124684Ssobomax 165124684Ssobomax /* error or no new value */ 166124684Ssobomax if ((error != 0) || (req->newptr == NULL)) 167124684Ssobomax return (error); 168124684Ssobomax 169124684Ssobomax /* range check */ 170124930Ssobomax if (arg > tcc[0].rlevel) 171124930Ssobomax arg = tcc[0].rlevel; 172124684Ssobomax 173124684Ssobomax /* set new value and possibly switch */ 174124684Ssobomax *argp = arg; 175124684Ssobomax 176124684Ssobomax p4tcc_power_profile(NULL); 177124684Ssobomax 178124684Ssobomax *argp = p4tcc_getperf(); 179124684Ssobomax 180124684Ssobomax return (0); 181124684Ssobomax} 182124684Ssobomax 183124684Ssobomaxstatic void 184124684Ssobomaxsetup_p4tcc(void *dummy __unused) 185124684Ssobomax{ 186134199Ssobomax int nsteps, i; 187134200Ssobomax static char p4tcc_levels[(3 * TCC_LEVELS) + 1]; 188134200Ssobomax char buf[4 + 1]; 189124684Ssobomax 190124684Ssobomax if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 191124684Ssobomax (CPUID_ACPI | CPUID_TM)) 192124684Ssobomax return; 193124684Ssobomax 194134199Ssobomax nsteps = TCC_LEVELS; 195124684Ssobomax switch (cpu_id & 0xf) { 196124684Ssobomax case 0x22: /* errata O50 P44 and Z21 */ 197124684Ssobomax case 0x24: 198124684Ssobomax case 0x25: 199124684Ssobomax case 0x27: 200124684Ssobomax case 0x29: 201124684Ssobomax /* hang with 12.5 */ 202124930Ssobomax tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2]; 203134199Ssobomax nsteps -= 1; 204124684Ssobomax break; 205124684Ssobomax case 0x07: /* errata N44 and P18 */ 206124684Ssobomax case 0x0a: 207124684Ssobomax case 0x12: 208124684Ssobomax case 0x13: 209124684Ssobomax /* hang at 12.5 and 25 */ 210124930Ssobomax tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3]; 211134199Ssobomax nsteps -= 2; 212124684Ssobomax break; 213124684Ssobomax default: 214124684Ssobomax break; 215124684Ssobomax } 216124684Ssobomax 217134199Ssobomax p4tcc_levels[0] = '\0'; 218134199Ssobomax for (i = nsteps; i > 0; i--) { 219134199Ssobomax sprintf(buf, "%d%s", tcc[i - 1].rlevel, (i != 1) ? " " : ""); 220134199Ssobomax strcat(p4tcc_levels, buf); 221134199Ssobomax } 222134199Ssobomax 223124930Ssobomax p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel; 224124930Ssobomax p4tcc_performance = tcc[0].rlevel; 225124930Ssobomax 226124684Ssobomax p4tcc_percentage = p4tcc_getperf(); 227134199Ssobomax printf("Pentium 4 TCC support enabled, %d steps from 100%% to %d%%, " 228134199Ssobomax "current performance %u%%\n", nsteps, p4tcc_economy, 229124684Ssobomax p4tcc_percentage); 230124684Ssobomax 231124684Ssobomax sysctl_ctx_init(&p4tcc_sysctl_ctx); 232124930Ssobomax p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx, 233124930Ssobomax SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0, 234126412Smaxim "Pentium 4 Thermal Control Circuitry support"); 235124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 236124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 237124684Ssobomax "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 238124684Ssobomax &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 239124684Ssobomax "CPU performance in % of maximum"); 240124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 241124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 242124684Ssobomax "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 243124684Ssobomax &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 244124684Ssobomax "CPU performance in % of maximum in Performance mode"); 245124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 246124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 247124684Ssobomax "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 248124684Ssobomax &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 249124684Ssobomax "CPU performance in % of maximum in Economy mode"); 250134199Ssobomax SYSCTL_ADD_STRING(&p4tcc_sysctl_ctx, 251134199Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 252134199Ssobomax "cpuperf_levels", CTLFLAG_RD, p4tcc_levels, 0, 253134199Ssobomax "Perormance levels supported by the Pentium 4 Thermal Control " 254134199Ssobomax "Circuitry"); 255124684Ssobomax 256124684Ssobomax /* register performance profile change handler */ 257124684Ssobomax EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 258124684Ssobomax} 259124684SsobomaxSYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 260