diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 3c30a554cc7e..59a786af1d27 100644 --- a/sys/conf/files.i386 +++ b/sys/conf/files.i386 @@ -221,6 +221,7 @@ i386/i386/mpboot.s optional smp i386/i386/mptable.c optional apic i386/i386/mptable_pci.c optional apic pci i386/i386/nexus.c standard +i386/i386/p4tcc.c optional cpu_enable_tcc i386/i386/perfmon.c optional perfmon i386/i386/perfmon.c optional perfmon profiling-routine i386/i386/pmap.c standard diff --git a/sys/conf/options.i386 b/sys/conf/options.i386 index 8c828bb57e7a..0ae36396dc75 100644 --- a/sys/conf/options.i386 +++ b/sys/conf/options.i386 @@ -52,6 +52,7 @@ CPU_ELAN opt_cpu.h CPU_ELAN_XTAL opt_cpu.h CPU_ELAN_PPS opt_cpu.h CPU_ENABLE_SSE opt_cpu.h +CPU_ENABLE_TCC opt_cpu.h CPU_FASTER_5X86_FPU opt_cpu.h CPU_GEODE opt_cpu.h CPU_I486_ON_386 opt_cpu.h diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES index 0ccd0d3b380d..8022f945b8b2 100644 --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -164,6 +164,12 @@ cpu I686_CPU # aka Pentium Pro(tm) # the guest OS to run very slowly. Enabling this with a SMP kernel # will cause the kernel to be unusable. # +# CPU_ENABLE_TCC enables Thermal Control Circuitry (TCC) found in some +# Pentium(tm) 4 and (possibly) later CPUs. When enabled and detected, +# TCC allows to restrict power consumption by using machdep.cpuperf* +# sysctls. This operates independently of SpeedStep and is useful on +# systems where other mechanisms such as apm(4) or acpi(4) don't work. +# # NOTE 1: The options, CPU_BTB_EN, CPU_LOOP_EN, CPU_IORT, # CPU_LOOP_EN and CPU_RSTK_EN should not be used because of CPU bugs. # These options may crash your system. @@ -186,6 +192,7 @@ options CPU_SOEKRIS options CPU_ELAN_XTAL=32768000 options CPU_ELAN_PPS options CPU_ENABLE_SSE +options CPU_ENABLE_TCC #options CPU_DISABLE_SSE options CPU_FASTER_5X86_FPU options CPU_GEODE diff --git a/sys/i386/cpufreq/p4tcc.c b/sys/i386/cpufreq/p4tcc.c new file mode 100644 index 000000000000..20cef120b014 --- /dev/null +++ b/sys/i386/cpufreq/p4tcc.c @@ -0,0 +1,244 @@ +/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ +/* + * Copyright (c) 2003 Ted Unangst + * Copyright (c) 2004 Maxim Sobolev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Restrict power consumption by using thermal control circuit. + * This operates independently of speedstep. + * Found on Pentium 4 and later models (feature TM). + * + * References: + * Intel Developer's manual v.3 #245472-012 + * + * On some models, the cpu can hang if it's running at a slow speed. + * Workarounds included below. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_cpu.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u_int p4tcc_percentage; +static u_int p4tcc_economy = 13; +static u_int p4tcc_performance = 100; +static struct sysctl_ctx_list p4tcc_sysctl_ctx; + +static struct { + u_short level; + u_short rlevel; + u_short reg; +} tcc[] = { + { 88, 100, 0 }, + { 75, 88, 7 }, + { 63, 75, 6 }, + { 50, 63, 5 }, + { 38, 50, 4 }, + { 25, 38, 3 }, + { 13, 25, 2 }, + { 0, 13, 1 } +}; + +#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) +#define TCC_MAXPERF 100 + +static u_short +p4tcc_getperf(void) +{ + u_int64_t msreg; + int i; + + msreg = rdmsr(MSR_THERM_CONTROL); + msreg = (msreg >> 1) & 0x07; + for (i = 0; i < TCC_LEVELS; i++) { + if (msreg == tcc[i].reg) + break; + } + + return (tcc[i].rlevel); +} + +static void +p4tcc_setperf(u_int percentage) +{ + int i; + u_int64_t msreg; + + if (percentage > TCC_MAXPERF) + percentage = TCC_MAXPERF; + for (i = 0; i < TCC_LEVELS - 1; i++) { + if (percentage > tcc[i].level) + break; + } + + msreg = rdmsr(MSR_THERM_CONTROL); + msreg &= ~0x1e; /* bit 0 reserved */ + if (tcc[i].reg != 0) + msreg |= tcc[i].reg << 1 | 1 << 4; + wrmsr(MSR_THERM_CONTROL, msreg); +} + +static int +p4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int percentage; + int error; + + p4tcc_percentage = p4tcc_getperf(); + percentage = p4tcc_percentage; + error = sysctl_handle_int(oidp, &percentage, 0, req); + if (error || !req->newptr) { + return (error); + } + if (p4tcc_percentage != percentage) { + p4tcc_setperf(percentage); + } + + return (error); +} + +static void +p4tcc_power_profile(void *arg) +{ + int state; + u_int new; + + state = power_profile_get_state(); + if (state != POWER_PROFILE_PERFORMANCE && + state != POWER_PROFILE_ECONOMY) { + return; + } + + switch (state) { + case POWER_PROFILE_PERFORMANCE: + new = p4tcc_performance; + break; + case POWER_PROFILE_ECONOMY: + new = p4tcc_economy; + break; + default: + new = p4tcc_getperf(); + break; + } + + if (p4tcc_getperf() != new) { + p4tcc_setperf(new); + } +} + +static int +p4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int32_t *argp; + u_int32_t arg; + int error; + + argp = (u_int32_t *)oidp->oid_arg1; + arg = *argp; + error = sysctl_handle_int(oidp, &arg, 0, req); + + /* error or no new value */ + if ((error != 0) || (req->newptr == NULL)) + return (error); + + /* range check */ + if (arg > TCC_MAXPERF) + arg = TCC_MAXPERF; + + /* set new value and possibly switch */ + *argp = arg; + + p4tcc_power_profile(NULL); + + *argp = p4tcc_getperf(); + + return (0); +} + +static void +setup_p4tcc(void *dummy __unused) +{ + + if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != + (CPUID_ACPI | CPUID_TM)) + return; + + switch (cpu_id & 0xf) { + case 0x22: /* errata O50 P44 and Z21 */ + case 0x24: + case 0x25: + case 0x27: + case 0x29: + /* hang with 12.5 */ + tcc[TCC_LEVELS - 1].reg = 2; + break; + case 0x07: /* errata N44 and P18 */ + case 0x0a: + case 0x12: + case 0x13: + /* hang at 12.5 and 25 */ + tcc[TCC_LEVELS - 1].reg = 3; + tcc[TCC_LEVELS - 2].reg = 3; + break; + default: + break; + } + + p4tcc_percentage = p4tcc_getperf(); + printf("Pentium 4 TCC support enabled, current performance %u%%\n", + p4tcc_percentage); + + sysctl_ctx_init(&p4tcc_sysctl_ctx); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf", CTLTYPE_INT | CTLFLAG_RW, + &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", + "CPU performance in % of maximum"); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, + &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", + "CPU performance in % of maximum in Performance mode"); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, + &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", + "CPU performance in % of maximum in Economy mode"); + + /* register performance profile change handler */ + EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); +} +SYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL); diff --git a/sys/i386/i386/p4tcc.c b/sys/i386/i386/p4tcc.c new file mode 100644 index 000000000000..20cef120b014 --- /dev/null +++ b/sys/i386/i386/p4tcc.c @@ -0,0 +1,244 @@ +/* $OpenBSD: p4tcc.c,v 1.1 2003/12/20 18:23:18 tedu Exp $ */ +/* + * Copyright (c) 2003 Ted Unangst + * Copyright (c) 2004 Maxim Sobolev + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Restrict power consumption by using thermal control circuit. + * This operates independently of speedstep. + * Found on Pentium 4 and later models (feature TM). + * + * References: + * Intel Developer's manual v.3 #245472-012 + * + * On some models, the cpu can hang if it's running at a slow speed. + * Workarounds included below. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_cpu.h" +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +static u_int p4tcc_percentage; +static u_int p4tcc_economy = 13; +static u_int p4tcc_performance = 100; +static struct sysctl_ctx_list p4tcc_sysctl_ctx; + +static struct { + u_short level; + u_short rlevel; + u_short reg; +} tcc[] = { + { 88, 100, 0 }, + { 75, 88, 7 }, + { 63, 75, 6 }, + { 50, 63, 5 }, + { 38, 50, 4 }, + { 25, 38, 3 }, + { 13, 25, 2 }, + { 0, 13, 1 } +}; + +#define TCC_LEVELS sizeof(tcc) / sizeof(tcc[0]) +#define TCC_MAXPERF 100 + +static u_short +p4tcc_getperf(void) +{ + u_int64_t msreg; + int i; + + msreg = rdmsr(MSR_THERM_CONTROL); + msreg = (msreg >> 1) & 0x07; + for (i = 0; i < TCC_LEVELS; i++) { + if (msreg == tcc[i].reg) + break; + } + + return (tcc[i].rlevel); +} + +static void +p4tcc_setperf(u_int percentage) +{ + int i; + u_int64_t msreg; + + if (percentage > TCC_MAXPERF) + percentage = TCC_MAXPERF; + for (i = 0; i < TCC_LEVELS - 1; i++) { + if (percentage > tcc[i].level) + break; + } + + msreg = rdmsr(MSR_THERM_CONTROL); + msreg &= ~0x1e; /* bit 0 reserved */ + if (tcc[i].reg != 0) + msreg |= tcc[i].reg << 1 | 1 << 4; + wrmsr(MSR_THERM_CONTROL, msreg); +} + +static int +p4tcc_perf_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int percentage; + int error; + + p4tcc_percentage = p4tcc_getperf(); + percentage = p4tcc_percentage; + error = sysctl_handle_int(oidp, &percentage, 0, req); + if (error || !req->newptr) { + return (error); + } + if (p4tcc_percentage != percentage) { + p4tcc_setperf(percentage); + } + + return (error); +} + +static void +p4tcc_power_profile(void *arg) +{ + int state; + u_int new; + + state = power_profile_get_state(); + if (state != POWER_PROFILE_PERFORMANCE && + state != POWER_PROFILE_ECONOMY) { + return; + } + + switch (state) { + case POWER_PROFILE_PERFORMANCE: + new = p4tcc_performance; + break; + case POWER_PROFILE_ECONOMY: + new = p4tcc_economy; + break; + default: + new = p4tcc_getperf(); + break; + } + + if (p4tcc_getperf() != new) { + p4tcc_setperf(new); + } +} + +static int +p4tcc_profile_sysctl(SYSCTL_HANDLER_ARGS) +{ + u_int32_t *argp; + u_int32_t arg; + int error; + + argp = (u_int32_t *)oidp->oid_arg1; + arg = *argp; + error = sysctl_handle_int(oidp, &arg, 0, req); + + /* error or no new value */ + if ((error != 0) || (req->newptr == NULL)) + return (error); + + /* range check */ + if (arg > TCC_MAXPERF) + arg = TCC_MAXPERF; + + /* set new value and possibly switch */ + *argp = arg; + + p4tcc_power_profile(NULL); + + *argp = p4tcc_getperf(); + + return (0); +} + +static void +setup_p4tcc(void *dummy __unused) +{ + + if ((cpu_feature & (CPUID_ACPI | CPUID_TM)) != + (CPUID_ACPI | CPUID_TM)) + return; + + switch (cpu_id & 0xf) { + case 0x22: /* errata O50 P44 and Z21 */ + case 0x24: + case 0x25: + case 0x27: + case 0x29: + /* hang with 12.5 */ + tcc[TCC_LEVELS - 1].reg = 2; + break; + case 0x07: /* errata N44 and P18 */ + case 0x0a: + case 0x12: + case 0x13: + /* hang at 12.5 and 25 */ + tcc[TCC_LEVELS - 1].reg = 3; + tcc[TCC_LEVELS - 2].reg = 3; + break; + default: + break; + } + + p4tcc_percentage = p4tcc_getperf(); + printf("Pentium 4 TCC support enabled, current performance %u%%\n", + p4tcc_percentage); + + sysctl_ctx_init(&p4tcc_sysctl_ctx); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf", CTLTYPE_INT | CTLFLAG_RW, + &p4tcc_percentage, 0, p4tcc_perf_sysctl, "I", + "CPU performance in % of maximum"); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf_performance", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, + &p4tcc_performance, 0, p4tcc_profile_sysctl, "I", + "CPU performance in % of maximum in Performance mode"); + SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx, + SYSCTL_STATIC_CHILDREN(_machdep), OID_AUTO, + "cpuperf_economy", CTLTYPE_INT | CTLFLAG_RD | CTLFLAG_RW, + &p4tcc_economy, 0, p4tcc_profile_sysctl, "I", + "CPU performance in % of maximum in Economy mode"); + + /* register performance profile change handler */ + EVENTHANDLER_REGISTER(power_profile_change, p4tcc_power_profile, NULL, 0); +} +SYSINIT(setup_p4tcc, SI_SUB_CPU, SI_ORDER_ANY, setup_p4tcc, NULL);