Actually add the main file for Pentium 4 Thermal Control Circuit support.
authorJeroen Ruigrok/asmodai <asmodai@dragonflybsd.org>
Tue, 27 Jul 2004 10:55:57 +0000 (10:55 +0000)
committerJeroen Ruigrok/asmodai <asmodai@dragonflybsd.org>
Tue, 27 Jul 2004 10:55:57 +0000 (10:55 +0000)
Obtained from:          FreeBSD(from OpenBSD)
Submitted by:           Johannes Hofmann <Johannes.Hofmann@gmx.de>
Reminded by:            YONETANI Tomokazu <qhwt+dragonfly-commits@les.ath.cx>
OK'd by:                dillon
Feeling like an idiot:  asmodai

sys/i386/i386/p4tcc.c [new file with mode: 0644]
sys/platform/pc32/i386/p4tcc.c [new file with mode: 0644]

diff --git a/sys/i386/i386/p4tcc.c b/sys/i386/i386/p4tcc.c
new file mode 100644 (file)
index 0000000..9d9ebaa
--- /dev/null
@@ -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@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.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/i386/i386/p4tcc.c,v 1.3.2.1 2004/03/03 15:24:15 sobomax Exp $
+ * $DragonFly: src/sys/i386/i386/Attic/p4tcc.c,v 1.1 2004/07/27 10:55:57 asmodai Exp $
+ */
+
+#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);
diff --git a/sys/platform/pc32/i386/p4tcc.c b/sys/platform/pc32/i386/p4tcc.c
new file mode 100644 (file)
index 0000000..05c475a
--- /dev/null
@@ -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@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.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/sys/i386/i386/p4tcc.c,v 1.3.2.1 2004/03/03 15:24:15 sobomax Exp $
+ * $DragonFly: src/sys/platform/pc32/i386/p4tcc.c,v 1.1 2004/07/27 10:55:57 asmodai Exp $
+ */
+
+#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);