p4tcc.c revision 124684
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 124684 2004-01-18 21:06: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; 56124684Ssobomaxstatic u_int p4tcc_economy = 13; 57124684Ssobomaxstatic u_int p4tcc_performance = 100; 58124684Ssobomaxstatic struct sysctl_ctx_list p4tcc_sysctl_ctx; 59124684Ssobomax 60124684Ssobomaxstatic struct { 61124684Ssobomax u_short level; 62124684Ssobomax u_short rlevel; 63124684Ssobomax u_short reg; 64124684Ssobomax} tcc[] = { 65124684Ssobomax { 88, 100, 0 }, 66124684Ssobomax { 75, 88, 7 }, 67124684Ssobomax { 63, 75, 6 }, 68124684Ssobomax { 50, 63, 5 }, 69124684Ssobomax { 38, 50, 4 }, 70124684Ssobomax { 25, 38, 3 }, 71124684Ssobomax { 13, 25, 2 }, 72124684Ssobomax { 0, 13, 1 } 73124684Ssobomax}; 74124684Ssobomax 75124684Ssobomax#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) 76124684Ssobomax#define TCC_MAXPERF 100 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 100124684Ssobomax if (percentage > TCC_MAXPERF) 101124684Ssobomax percentage = TCC_MAXPERF; 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 */ 178124684Ssobomax if (arg > TCC_MAXPERF) 179124684Ssobomax arg = TCC_MAXPERF; 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{ 194124684Ssobomax 195124684Ssobomax if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 196124684Ssobomax (CPUID_ACPI | CPUID_TM)) 197124684Ssobomax return; 198124684Ssobomax 199124684Ssobomax switch (cpu_id & 0xf) { 200124684Ssobomax case 0x22: /* errata O50 P44 and Z21 */ 201124684Ssobomax case 0x24: 202124684Ssobomax case 0x25: 203124684Ssobomax case 0x27: 204124684Ssobomax case 0x29: 205124684Ssobomax /* hang with 12.5 */ 206124684Ssobomax tcc[TCC_LEVELS - 1].reg = 2; 207124684Ssobomax break; 208124684Ssobomax case 0x07: /* errata N44 and P18 */ 209124684Ssobomax case 0x0a: 210124684Ssobomax case 0x12: 211124684Ssobomax case 0x13: 212124684Ssobomax /* hang at 12.5 and 25 */ 213124684Ssobomax tcc[TCC_LEVELS - 1].reg = 3; 214124684Ssobomax tcc[TCC_LEVELS - 2].reg = 3; 215124684Ssobomax break; 216124684Ssobomax default: 217124684Ssobomax break; 218124684Ssobomax } 219124684Ssobomax 220124684Ssobomax p4tcc_percentage = p4tcc_getperf(); 221124684Ssobomax printf("Pentium 4 TCC support enabled, current performance %u%%\n", 222124684Ssobomax p4tcc_percentage); 223124684Ssobomax 224124684Ssobomax sysctl_ctx_init(&p4tcc_sysctl_ctx); 225124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 226124684Ssobomax SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 227124684Ssobomax "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 228124684Ssobomax &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 229124684Ssobomax "CPU performance in % of maximum"); 230124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 231124684Ssobomax SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 232124684Ssobomax "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 233124684Ssobomax &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 234124684Ssobomax "CPU performance in % of maximum in Performance mode"); 235124684Ssobomax SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 236124684Ssobomax SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, 237124684Ssobomax "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 238124684Ssobomax &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 239124684Ssobomax "CPU performance in % of maximum in Economy mode"); 240124684Ssobomax 241124684Ssobomax /* register performance profile change handler */ 242124684Ssobomax EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 243124684Ssobomax} 244124684SsobomaxSYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 245