From 9b2b699a8fb552eba62fd0727edb13222390107d Mon Sep 17 00:00:00 2001 From: sobomax Date: Sun, 18 Jan 2004 21:06:56 +0000 Subject: Add new CPU_ENABLE_TCC option, from NOTES: 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. Given the fact that many, even modern, notebooks don't work properly with Intel ACPI, this is indeed very useful option for notebook owners. Obtained from: OpenBSD MFC after: 2 weeks --- sys/conf/files.i386 | 1 + sys/conf/options.i386 | 1 + sys/i386/conf/NOTES | 7 ++ sys/i386/cpufreq/p4tcc.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++ sys/i386/i386/p4tcc.c | 244 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 497 insertions(+) create mode 100644 sys/i386/cpufreq/p4tcc.c create mode 100644 sys/i386/i386/p4tcc.c diff --git a/sys/conf/files.i386 b/sys/conf/files.i386 index 3c30a55..59a786a 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 8c828bb..0ae3639 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 0ccd0d3..8022f94 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 0000000..20cef12 --- /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 0000000..20cef12 --- /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); -- cgit v1.1