p4tcc.c from FreeBSD
Johannes Hofmann
Johannes.Hofmann at gmx.de
Sun Jul 25 04:50:52 PDT 2004
This patch adds Pentium 4 Thermal Control Circuit (TCC) support from
FreeBSD (originally from OpenBSD, I think).
It can be used to reduce the CPU speed without ACPI support and might be
useful on machines where ACPI has problems.
In my tests it actually slowed things down, but I have no results about
battery life yet.
Johannes
Index: sys/conf/files.i386
===================================================================
RCS file: /home/dcvs/src/sys/conf/files.i386,v
retrieving revision 1.25
diff -u -r1.25 files.i386
--- sys/conf/files.i386 5 Jul 2004 00:07:32 -0000 1.25
+++ sys/conf/files.i386 22 Jul 2004 19:00:45 -0000
@@ -213,6 +213,7 @@
i386/i386/mpboot.s optional smp
i386/i386/mplock.s optional smp
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
Index: sys/conf/options.i386
===================================================================
RCS file: /home/dcvs/src/sys/conf/options.i386,v
retrieving revision 1.6
diff -u -r1.6 options.i386
--- sys/conf/options.i386 29 Apr 2004 12:11:15 -0000 1.6
+++ sys/conf/options.i386 22 Jul 2004 18:52:45 -0000
@@ -50,6 +50,7 @@
CPU_DIRECT_MAPPED_CACHE opt_cpu.h
CPU_DISABLE_5X86_LSSER opt_cpu.h
CPU_ELAN opt_cpu.h
+CPU_ENABLE_TCC opt_cpu.h
CPU_FASTER_5X86_FPU opt_cpu.h
CPU_I486_ON_386 opt_cpu.h
CPU_IORT opt_cpu.h
Index: sys/i386/conf/LINT
===================================================================
RCS file: /home/dcvs/src/sys/i386/conf/LINT,v
retrieving revision 1.32
diff -u -r1.32 LINT
--- sys/i386/conf/LINT 20 Jul 2004 18:48:02 -0000 1.32
+++ sys/i386/conf/LINT 25 Jul 2004 10:29:17 -0000
@@ -172,6 +172,12 @@
#
# CPU_DISABLE_SSE disables SSE/MMX2 instructions support.
#
+# CPU_ENABLE_TCC enables Thermal Control Circuitry (TCC) found in some
+# Pentium(tm) 4 and (possibly) later CPUs. When enabled and detected,
+# TCC supports restricting power consumption using the hw.p4tcc.*
+# sysctls. This operates independently of SpeedStep and is useful on
+# systems where other mechanisms such as apm(4) or acpi(4) don't work.
+#
# CPU_FASTER_5X86_FPU enables faster FPU exception handler.
#
# CPU_I486_ON_386 enables CPU cache on i486 based CPU upgrade products
@@ -237,6 +243,7 @@
options CPU_DIRECT_MAPPED_CACHE
options CPU_DISABLE_5X86_LSSER
options CPU_ELAN
+options CPU_ENABLE_TCC
options CPU_DISABLE_SSE
options CPU_FASTER_5X86_FPU
options CPU_I486_ON_386
Index: sys/i386/include/specialreg.h
===================================================================
RCS file: /home/dcvs/src/sys/i386/include/specialreg.h,v
retrieving revision 1.2
diff -u -r1.2 specialreg.h
--- sys/i386/include/specialreg.h 17 Jun 2003 04:28:36 -0000 1.2
+++ sys/i386/include/specialreg.h 22 Jul 2004 19:07:07 -0000
@@ -169,6 +169,10 @@
#define MSR_MC3_STATUS 0x411
#define MSR_MC3_ADDR 0x412
#define MSR_MC3_MISC 0x413
+#define MSR_THERM_CONTROL 0x19a
+#define MSR_THERM_INTERRUPT 0x19b
+#define MSR_THERM_STATUS 0x19c
+
/*
* Constants related to MTRRs
Index: sys/i386/i386/p4tcc.c
===================================================================
RCS file: sys/i386/i386/p4tcc.c
diff -N sys/i386/i386/p4tcc.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ sys/i386/i386/p4tcc.c 25 Jul 2004 10:44:50 -0000
@@ -0,0 +1,251 @@
+/* $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 at xxxxxxxxxxx>
+ * 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.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/i386/i386/p4tcc.c,v 1.3.2.1 2004/03/03 15:24:15 sobomax Exp $
+ * $DragonFly:$
+ */
+
+#include <sys/cdefs.h>
+
+#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;
+static u_int p4tcc_performance;
+static struct sysctl_ctx_list p4tcc_sysctl_ctx;
+static struct sysctl_oid *p4tcc_sysctl_tree;
+
+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])
+
+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[0].rlevel)
+ percentage = tcc[0].rlevel;
+ 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[0].rlevel)
+ arg = tcc[0].rlevel;
+
+ /* 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] = tcc[TCC_LEVELS - 2];
+ break;
+ case 0x07: /* errata N44 and P18 */
+ case 0x0a:
+ case 0x12:
+ case 0x13:
+ /* hang at 12.5 and 25 */
+ tcc[TCC_LEVELS - 1] = tcc[TCC_LEVELS - 2] = tcc[TCC_LEVELS - 3];
+ break;
+ default:
+ break;
+ }
+
+ p4tcc_economy = tcc[TCC_LEVELS - 1].rlevel;
+ p4tcc_performance = tcc[0].rlevel;
+
+ p4tcc_percentage = p4tcc_getperf();
+ printf("Pentium 4 TCC support enabled, current performance %u%%\n",
+ p4tcc_percentage);
+
+ sysctl_ctx_init(&p4tcc_sysctl_ctx);
+ p4tcc_sysctl_tree = SYSCTL_ADD_NODE(&p4tcc_sysctl_ctx,
+ SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO, "p4tcc", CTLFLAG_RD, 0,
+ "Pentium 4 Thermal Control Circuitry support");
+ SYSCTL_ADD_PROC(&p4tcc_sysctl_ctx,
+ SYSCTL_CHILDREN(p4tcc_sysctl_tree), 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_CHILDREN(p4tcc_sysctl_tree), 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_CHILDREN(p4tcc_sysctl_tree), 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);
More information about the Submit
mailing list