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