p4tcc.c revision 124930
179543Sru/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ 279543Sru/* 369626Sru * Copyright (c) 2003 Ted Unangst 469626Sru * Copyright (c) 2004 Maxim Sobolev <sobomax@FreeBSD.org> 569626Sru * All rights reserved. 669626Sru * 769626Sru * Redistribution and use in source and binary forms, with or without 869626Sru * modification, are permitted provided that the following conditions 969626Sru * are met: 1069626Sru * 1. Redistributions of source code must retain the above copyright 1169626Sru * notice, this list of conditions and the following disclaimer. 1269626Sru * 2. Redistributions in binary form must reproduce the above copyright 1369626Sru * notice, this list of conditions and the following disclaimer in the 1469626Sru * documentation and/or other materials provided with the distribution. 1569626Sru * 1669626Sru * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1769626Sru * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1869626Sru * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1969626Sru * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 2069626Sru * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 2169626Sru * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2269626Sru * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2369626Sru * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2455839Sasmodai * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2555839Sasmodai * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2655839Sasmodai * SUCH DAMAGE. 2755839Sasmodai */ 2869626Sru/* 2969626Sru * Restrict power consumption by using thermal control circuit. 3055839Sasmodai * This operates independently of speedstep. 3169626Sru * Found on Pentium 4 and later models (feature TM). 3255839Sasmodai * 3375584Sru * References: 3455839Sasmodai * Intel Developer's manual v.3 #245472-012 3555839Sasmodai * 3655839Sasmodai * On some models, the cpu can hang if it's running at a slow speed. 3755839Sasmodai * Workarounds included below. 3855839Sasmodai */ 3955839Sasmodai 4075584Sru#include <sys/cdefs.h> 4175584Sru__FBSDID("$FreeBSD: head/sys/i386/cpufreq/p4tcc.c 124930 2004-01-24 21:13:13Z sobomax $"); 4275584Sru 4375584Sru#include "opt_cpu.h" 4475584Sru#include <sys/param.h> 4575584Sru#include <sys/systm.h> 4675584Sru#include <sys/kernel.h> 4775584Sru#include <sys/conf.h> 4875584Sru#include <sys/power.h> 4955839Sasmodai#include <sys/sysctl.h> 5055839Sasmodai#include <sys/types.h> 5155839Sasmodai 5275584Sru#include <machine/md_var.h> 5375584Sru#include <machine/specialreg.h> 5475584Sru 5555839Sasmodaistatic u_int p4tcc_percentage; 5675584Srustatic u_int p4tcc_economy; 5755839Sasmodaistatic u_int p4tcc_performance; 5855839Sasmodaistatic struct sysctl_ctx_list p4tcc_sysctl_ctx; 5969626Srustatic struct sysctl_oid *p4tcc_sysctl_tree; 6069626Sru 6169626Srustatic struct { 6269626Sru u_short level; 6369626Sru u_short rlevel; 6469626Sru u_short reg; 6569626Sru} tcc[] = { 6655839Sasmodai { 88, 100, 0 }, 6775584Sru { 75, 88, 7 }, 6875584Sru { 63, 75, 6 }, 6975584Sru { 50, 63, 5 }, 7075584Sru { 38, 50, 4 }, 7175584Sru { 25, 38, 3 }, 7275584Sru { 13, 25, 2 }, 7375584Sru { 0, 13, 1 } 7469626Sru}; 7569626Sru 7669626Sru#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) 7769626Sru 7869626Srustatic u_short 7969626Srup4tcc_getperf(void) 8069626Sru{ 8169626Sru u_int64_t msreg; 8269626Sru int i; 8369626Sru 8469626Sru msreg = rdmsr(MSR_THERM_CONTROL); 8569626Sru msreg = (msreg >> 1) & 0x07; 8669626Sru for (i = 0; i < TCC_LEVELS; i++) { 8769626Sru if (msreg == tcc[i].reg) 8869626Sru break; 8969626Sru } 9069626Sru 9169626Sru return (tcc[i].rlevel); 9269626Sru} 9369626Sru 9469626Srustatic void 9569626Srup4tcc_setperf(u_int percentage) 9669626Sru{ 9769626Sru int i; 9869626Sru u_int64_t msreg; 9969626Sru 10069626Sru if (percentage > tcc[0].rlevel) 10169626Sru percentage = tcc[0].rlevel; 10269626Sru for (i = 0; i < TCC_LEVELS - 1; i++) { 10369626Sru if (percentage > tcc[i].level) 10469626Sru break; 10569626Sru } 10669626Sru 10769626Sru msreg = rdmsr(MSR_THERM_CONTROL); 10869626Sru msreg &= ~0x1e; /* bit 0 reserved */ 10969626Sru if (tcc[i].reg != 0) 11075584Sru msreg |= tcc[i].reg << 1 | 1 << 4; 11175584Sru wrmsr(MSR_THERM_CONTROL, msreg); 11275584Sru} 11369626Sru 11469626Srustatic int 11575584Srup4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS) 11655839Sasmodai{ 11755839Sasmodai u_int percentage; 11855839Sasmodai int error; 11955839Sasmodai 12055839Sasmodai p4tcc_percentage = p4tcc_getperf(); 12155839Sasmodai percentage = p4tcc_percentage; 12255839Sasmodai error = sysctl_handle_int(oidp, &percentage, 0, req); 12355839Sasmodai if (error || !req->newptr) { 12469626Sru return (error); 12569626Sru } 12669626Sru if (p4tcc_percentage != percentage) { 12769626Sru p4tcc_setperf(percentage); 12869626Sru } 12969626Sru 13069626Sru return (error); 13169626Sru} 13269626Sru 13369626Srustatic void 13469626Srup4tcc_power_profile(void *arg) 13569626Sru{ 13669626Sru int state; 13769626Sru u_int new; 13869626Sru 13969626Sru state = power_profile_get_state(); 14069626Sru if (state != POWER_PROFILE_PERFORMANCE && 14169626Sru state != POWER_PROFILE_ECONOMY) { 14269626Sru return; 14369626Sru } 14469626Sru 14569626Sru switch (state) { 14669626Sru case POWER_PROFILE_PERFORMANCE: 14769626Sru new = p4tcc_performance; 14869626Sru break; 14955839Sasmodai case POWER_PROFILE_ECONOMY: 15069626Sru new = p4tcc_economy; 15169626Sru break; 15269626Sru default: 15369626Sru new = p4tcc_getperf(); 15469626Sru break; 15569626Sru } 15669626Sru 15769626Sru if (p4tcc_getperf() != new) { 15869626Sru p4tcc_setperf(new); 15969626Sru } 16055839Sasmodai} 16169626Sru 16269626Srustatic int 16369626Srup4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) 16469626Sru{ 16569626Sru u_int32_t *argp; 16669626Sru u_int32_t arg; 16769626Sru int error; 16869626Sru 16969626Sru argp = (u_int32_t *)oidp->oid_arg1; 17069626Sru arg = *argp; 17155839Sasmodai error = sysctl_handle_int(oidp, &arg, 0, req); 17255839Sasmodai 17369626Sru /* error or no new value */ 17475584Sru if ((error != 0) || (req->newptr == NULL)) 17555839Sasmodai return (error); 17669626Sru 17769626Sru /* range check */ 17855839Sasmodai if (arg > tcc[0].rlevel) 17955839Sasmodai arg = tcc[0].rlevel; 18055839Sasmodai 18155839Sasmodai /* set new value and possibly switch */ 18255839Sasmodai *argp = arg; 18355839Sasmodai 18455839Sasmodai p4tcc_power_profile(NULL); 18555839Sasmodai 18655839Sasmodai *argp = p4tcc_getperf(); 18769626Sru 18869626Sru return (0); 18969626Sru} 19069626Sru 19169626Srustatic void 19269626Srusetup_p4tcc(void *dummy __unused) 19355839Sasmodai{ 19469626Sru 19569626Sru if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != 19669626Sru (CPUID_ACPI | CPUID_TM)) 19769626Sru return; 19869626Sru 19955839Sasmodai switch (cpu_id & 0xf) { 20055839Sasmodai case 0x22: /* errata O50 P44 and Z21 */ 20169626Sru case 0x24: 20269626Sru case 0x25: 20369626Sru case 0x27: 20469626Sru case 0x29: 20569626Sru /* hang with 12.5 */ 20655839Sasmodai tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2]; 20755839Sasmodai break; 20875584Sru case 0x07: /* errata N44 and P18 */ 20975584Sru case 0x0a: 21055839Sasmodai case 0x12: 21169626Sru case 0x13: 21255839Sasmodai /* hang at 12.5 and 25 */ 21369626Sru tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3]; 21469626Sru break; 21569626Sru default: 21669626Sru break; 21769626Sru } 21869626Sru 21955839Sasmodai p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel; 22055839Sasmodai p4tcc_performance = tcc[0].rlevel; 22169626Sru 22269626Sru p4tcc_percentage = p4tcc_getperf(); 22355839Sasmodai printf("Pentium 4 TCC support enabled, current performance %u%%\n", 22469626Sru p4tcc_percentage); 22575584Sru 22675584Sru sysctl_ctx_init(&p4tcc_sysctl_ctx); 22775584Sru p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx, 22855839Sasmodai SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0, 22969626Sru "Pentium 4 Ternal Control Circuitry support"); 23069626Sru SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 23155839Sasmodai SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 23255839Sasmodai "cpuperf", CTLTYPE_INT | CTLFLAG_RW, 23369626Sru &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", 23469626Sru "CPU performance in % of maximum"); 23569626Sru SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 23669626Sru SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 23755839Sasmodai "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 23855839Sasmodai &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", 23969626Sru "CPU performance in % of maximum in Performance mode"); 24055839Sasmodai SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, 24155839Sasmodai SYSCTL_CHILDREN(p4tcc_sysctl_tree), OID_AUTO, 24269626Sru "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, 24369626Sru &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", 24469626Sru "CPU performance in % of maximum in Economy mode"); 24569626Sru 24669626Sru /* register performance profile change handler */ 24769626Sru EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); 24869626Sru} 24969626SruSYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); 25069626Sru