summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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