summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsobomax <sobomax@FreeBSD.org>2004-01-18 21:06:56 +0000
committersobomax <sobomax@FreeBSD.org>2004-01-18 21:06:56 +0000
commit9b2b699a8fb552eba62fd0727edb13222390107d (patch)
treebf6028ce1324596dc8af429a5b4bd42b151af156
parentf23872a5c565b1e5fcf00b2fbddf0856ae69a02b (diff)
downloadFreeBSD-src-9b2b699a8fb552eba62fd0727edb13222390107d.zip
FreeBSD-src-9b2b699a8fb552eba62fd0727edb13222390107d.tar.gz
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
-rw-r--r--sys/conf/files.i3861
-rw-r--r--sys/conf/options.i3861
-rw-r--r--sys/i386/conf/NOTES7
-rw-r--r--sys/i386/cpufreq/p4tcc.c244
-rw-r--r--sys/i386/i386/p4tcc.c244
5 files changed, 497 insertions, 0 deletions
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 <sobomax@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_cpu.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/power.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+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 <sobomax@FreeBSD.org>
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_cpu.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/power.h>
+#include <sys/sysctl.h>
+#include <sys/types.h>
+
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+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);
OpenPOWER on IntegriCloud