p4tcc.c revision 134199
1124684Ssobomax/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ 2124684Ssobomax/* 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 134199 2004-08-23 09:47:56Z sobomax $"); 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 int state; 137124684Ssobomax u_int new; 138124684Ssobomax 139124684Ssobomax state = power_profile_get_state(); 140124684Ssobomax if (state != POWER_PROFILE_PERFORMANCE && 141124684Ssobomax state != POWER_PROFILE_ECONOMY) { 142124684Ssobomax return; 143124684Ssobomax } 144124684Ssobomax 145124684Ssobomax switch (state) { 146124684Ssobomax case POWER_PROFILE_PERFORMANCE: 147124684Ssobomax new = p4tcc_performance; 148124684Ssobomax break; 149124684Ssobomax case POWER_PROFILE_ECONOMY: 150124684Ssobomax new = p4tcc_economy; 151124684Ssobomax break; 152124684Ssobomax default: 153124684Ssobomax new = p4tcc_getperf(); 154124684Ssobomax break; 155124684Ssobomax } 156124684Ssobomax 157124684Ssobomax if (p4tcc_getperf() != new) { 158124684Ssobomax p4tcc_setperf(new); 159124684Ssobomax } 160124684Ssobomax} 161124684Ssobomax 162124684Ssobomaxstatic int 163124684Ssobomaxp4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) 164124684Ssobomax{ 165124684Ssobomax u_int32_t *argp; 166124684Ssobomax u_int32_t arg; 167124684Ssobomax int error; 168124684Ssobomax 169124684Ssobomax argp = (u_int32_t *)oidp->oid_arg1; 170124684Ssobomax arg = *argp; 171124684Ssobomax error = sysctl_handle_int(oidp, &arg, 0, req); 172124684Ssobomax 173124684Ssobomax /* error or no new value */ 174124684Ssobomax if ((error != 0) || (req->newptr == NULL)) 175124684Ssobomax return (error); 176124684Ssobomax 177124684Ssobomax /* range check */ 178124930Ssobomax if (arg > tcc[0].rlevel) 179124930Ssobomax arg = tcc[0].rlevel; 180124684Ssobomax 181124684Ssobomax /* set new value and possibly switch */ 182124684Ssobomax *argp = arg; 183124684Ssobomax 184124684Ssobomax p4tcc_power_profile(NULL); 185124684Ssobomax 186124684Ssobomax *argp = p4tcc_getperf(); 187124684Ssobomax 188124684Ssobomax return (0); 189124684Ssobomax} 190124684Ssobomax 191124684Ssobomaxstatic void 192124684Ssobomaxsetup_p4tcc(void *dummy __unused) 193124684Ssobomax{ 194134199Ssobomax int nsteps, i; 195134199Ssobomax static char p4tcc_levels[(3 * TCC_LEVELS) + 1]; 196134199Ssobomax char buf[4 + 1]; 197124684Ssobomax 198124684Ssobomax if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 199124684Ssobomax (CPUID_ACPI | CPUID_TM)) 200124684Ssobomax return; 201124684Ssobomax 202134199Ssobomax nsteps = TCC_LEVELS; 203124684Ssobomax switch (cpu_id & 0xf) { 204124684Ssobomax case 0x22: /* errata O50 P44 and Z21 */ 205124684Ssobomax case 0x24: 206124684Ssobomax case 0x25: 207124684Ssobomax case 0x27: 208124684Ssobomax case 0x29: 209124684Ssobomax /* hang with 12.5 */ 210124930Ssobomax tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2]; 211134199Ssobomax nsteps -= 1; 212124684Ssobomax break; 213124684Ssobomax case 0x07: /* errata N44 and P18 */ 214124684Ssobomax case 0x0a: 215124684Ssobomax case 0x12: 216124684Ssobomax case 0x13: 217124684Ssobomax /* hang at 12.5 and 25 */ 218124930Ssobomax tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3]; 219134199Ssobomax nsteps -= 2; 220124684Ssobomax break; 221124684Ssobomax default: 222124684Ssobomax break; 223124684Ssobomax } 224124684Ssobomax 225134199Ssobomax p4tcc_levels[0] = '\0'; 226134199Ssobomax for (i = nsteps; i > 0; i--) { 227134199Ssobomax sprintf(buf, "%d%s", tcc[i - 1].rlevel, (i != 1) ? " " : ""); 228134199Ssobomax strcat(p4tcc_levels, buf); 229134199Ssobomax } 230134199Ssobomax 231124930Ssobomax p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel; 232124930Ssobomax p4tcc_performance = tcc[0].rlevel; 233124930Ssobomax 234124684Ssobomax p4tcc_percentage = p4tcc_getperf(); 235134199Ssobomax printf("Pentium 4 TCC support enabled, %d steps from 100%% to %d%%, " 236134199Ssobomax "current performance %u%%\n", nsteps, p4tcc_economy, 237124684Ssobomax p4tcc_percentage); 238124684Ssobomax 239124684Ssobomax sysctl_ctx_init(&p4tcc_sysctl_ctx); 240124930Ssobomax p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx, 241124930Ssobomax SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0, 242126412Smaxim "Pentium 4 Thermal Control Circuitry support"); 243124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 244124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 245124684Ssobomax "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 246124684Ssobomax &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 247124684Ssobomax "CPU performance in % of maximum"); 248124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 249124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 250124684Ssobomax "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 251124684Ssobomax &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 252124684Ssobomax "CPU performance in % of maximum in Performance mode"); 253124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 254124930Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 255124684Ssobomax "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 256124684Ssobomax &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 257124684Ssobomax "CPU performance in % of maximum in Economy mode"); 258134199Ssobomax SYSCTL_ADD_STRING(&p4tcc_sysctl_ctx, 259134199Ssobomax SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 260134199Ssobomax "cpuperf_levels", CTLFLAG_RD, p4tcc_levels, 0, 261134199Ssobomax "Perormance levels supported by the Pentium 4 Thermal Control " 262134199Ssobomax "Circuitry"); 263124684Ssobomax 264124684Ssobomax /* register performance profile change handler */ 265124684Ssobomax EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 266124684Ssobomax} 267124684SsobomaxSYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 268