summaryrefslogtreecommitdiff
path: root/sys/arch/amd64
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2007-05-29 06:31:45 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2007-05-29 06:31:45 +0000
commit05f81c945b10f986b9e4ca4939581b2d66e70814 (patch)
treeda613429f60c21cd49f4a3e115dae7fc10c9adfd /sys/arch/amd64
parent3623ba65f6f3938704e0176cf7e3d6dfdaf6a179 (diff)
Some improvements for better intel cpu support.
Add EST support from i386, minus the tables Also add in support for CPU temperature sensors, based on diff to tech by Pierre Riteau. ok deraadt gwk
Diffstat (limited to 'sys/arch/amd64')
-rw-r--r--sys/arch/amd64/amd64/est.c232
-rw-r--r--sys/arch/amd64/amd64/identcpu.c59
-rw-r--r--sys/arch/amd64/conf/files.amd645
-rw-r--r--sys/arch/amd64/include/cpu.h9
-rw-r--r--sys/arch/amd64/include/specialreg.h15
5 files changed, 315 insertions, 5 deletions
diff --git a/sys/arch/amd64/amd64/est.c b/sys/arch/amd64/amd64/est.c
new file mode 100644
index 00000000000..60d1ff0be65
--- /dev/null
+++ b/sys/arch/amd64/amd64/est.c
@@ -0,0 +1,232 @@
+/* $OpenBSD: est.c,v 1.1 2007/05/29 06:31:44 tedu Exp $ */
+/*
+ * Copyright (c) 2003 Michael Eriksson.
+ * 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.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * 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.
+ */
+
+
+/*
+ * This is a driver for Intel's Enhanced SpeedStep, as implemented in
+ * Pentium M processors.
+ *
+ * Reference documentation:
+ *
+ * - IA-32 Intel Architecture Software Developer's Manual, Volume 3:
+ * System Programming Guide.
+ * Section 13.14, Enhanced Intel SpeedStep technology.
+ * Table B-2, MSRs in Pentium M Processors.
+ * http://www.intel.com/design/pentium4/manuals/245472.htm
+ *
+ * - Intel Pentium M Processor Datasheet.
+ * Table 5, Voltage and Current Specifications.
+ * http://www.intel.com/design/mobile/datashts/252612.htm
+ *
+ * - Intel Pentium M Processor on 90 nm Process with 2-MB L2 Cache Datasheet
+ * Table 3-4, Voltage and Current Specifications.
+ * http://www.intel.com/design/mobile/datashts/302189.htm
+ *
+ * - Linux cpufreq patches, speedstep-centrino.c.
+ * Encoding of MSR_PERF_CTL and MSR_PERF_STATUS.
+ * http://www.codemonkey.org.uk/projects/cpufreq/cpufreq-2.4.22-pre6-1.gz
+ */
+
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/specialreg.h>
+
+#define CPUVENDOR_INTEL 0
+#define CPUVENDOR_VIA 8
+
+
+/* Convert MHz and mV into IDs for passing to the MSR. */
+#define ID16(MHz, mV, bus_clk) \
+ ((((MHz * 100 + 50) / bus_clk) << 8) | ((mV ? mV - 700 : 0) >> 4))
+
+/* Possible bus speeds (multiplied by 100 for rounding) */
+#define BUS100 10000
+#define BUS133 13333
+#define BUS166 16666
+#define BUS200 20000
+
+#define MSR2MHZ(msr, bus) \
+ (((((int) (msr) >> 8) & 0xff) * (bus) + 50) / 100)
+#define MSR2MV(msr) \
+ (((int) (msr) & 0xff) * 16 + 700)
+
+struct fqlist {
+ int vendor: 5;
+ unsigned bus_clk : 1;
+ unsigned n : 5;
+ const u_int16_t *table;
+};
+
+static const struct fqlist *est_fqlist;
+
+static u_int16_t fake_table[3];
+static struct fqlist fake_fqlist;
+
+extern int setperf_prio;
+extern int perflevel;
+
+int bus_clock;
+
+void
+est_init(struct cpu_info *ci)
+{
+ const char *cpu_device = ci->ci_dev->dv_xname;
+ int vendor = -1;
+ int i, mhz, mv, low, high, bus;
+ u_int64_t msr;
+ u_int16_t idhi, idlo, cur;
+ u_int8_t crhi, crlo, crcur;
+
+ if (setperf_prio > 3)
+ return;
+ msr = rdmsr(MSR_FSB_FREQ);
+ bus = msr & 0x07;
+ switch (bus) {
+ case 5:
+ bus_clock = 10000;
+ break;
+ case 1:
+ bus_clock = 13333;
+ break;
+ case 3:
+ bus_clock = 16666;
+ break;
+ case 0:
+ bus_clock = 26666;
+ break;
+ case 4:
+ bus_clock = 33333;
+ break;
+ default:
+ printf("unknown bus %d\n", bus);
+ }
+
+ if (bus_clock == 0) {
+ printf("%s: EST: unknown system bus clock\n", cpu_device);
+ return;
+ }
+
+ msr = rdmsr(MSR_PERF_STATUS);
+ idhi = (msr >> 32) & 0xffff;
+ idlo = (msr >> 48) & 0xffff;
+ cur = msr & 0xffff;
+ crhi = (idhi >> 8) & 0xff;
+ crlo = (idlo >> 8) & 0xff;
+ crcur = (cur >> 8) & 0xff;
+ if (crlo == 0 || crhi == crlo) {
+ /*
+ * Don't complain about these cases, and silently disable EST:
+ * - A lowest clock ratio of 0, which seems to happen on all
+ * Pentium 4's that report EST.
+ * - An equal highest and lowest clock ratio, which happens on
+ * at least the Core 2 Duo X6800, maybe on newer models too.
+ */
+ return;
+ }
+ if (crhi == 0 || crcur == 0 || crlo > crhi ||
+ crcur < crlo || crcur > crhi) {
+ /*
+ * Do complain about other weirdness, because we first want to
+ * know about it, before we decide what to do with it.
+ */
+ printf("%s: EST: strange msr value 0x%016llx\n",
+ cpu_device, msr);
+ return;
+ }
+ if (est_fqlist == NULL) {
+ printf("%s: unknown Enhanced SpeedStep CPU, msr 0x%016llx\n",
+ cpu_device, msr);
+
+ /*
+ * Generate a fake table with the power states we know.
+ */
+ fake_table[0] = idhi;
+ if (cur == idhi || cur == idlo) {
+ printf("%s: using only highest and lowest power "
+ "states\n", cpu_device);
+
+ fake_table[1] = idlo;
+ fake_fqlist.n = 2;
+ } else {
+ printf("%s: using only highest, current and lowest "
+ "power states\n", cpu_device);
+
+ fake_table[1] = cur;
+ fake_table[2] = idlo;
+ fake_fqlist.n = 3;
+ }
+ fake_fqlist.vendor = vendor;
+ fake_fqlist.table = fake_table;
+ est_fqlist = &fake_fqlist;
+ }
+
+ mhz = MSR2MHZ(cur, bus_clock);
+ mv = MSR2MV(cur);
+ printf("%s: Enhanced SpeedStep %d MHz (%d mV)", cpu_device, mhz, mv);
+
+ low = MSR2MHZ(est_fqlist->table[est_fqlist->n - 1], bus_clock);
+ high = MSR2MHZ(est_fqlist->table[0], bus_clock);
+ perflevel = (mhz - low) * 100 / (high - low);
+
+ /*
+ * OK, tell the user the available frequencies.
+ */
+ printf(": speeds: ");
+ for (i = 0; i < est_fqlist->n; i++)
+ printf("%d%s", MSR2MHZ(est_fqlist->table[i], bus_clock),
+ i < est_fqlist->n - 1 ? ", " : " MHz\n");
+
+ cpu_setperf = est_setperf;
+ setperf_prio = 3;
+}
+
+void
+est_setperf(int level)
+{
+ int i;
+ uint64_t msr;
+
+ if (est_fqlist == NULL)
+ return;
+
+ i = ((level * est_fqlist->n) + 1) / 101;
+ if (i >= est_fqlist->n)
+ i = est_fqlist->n - 1;
+ i = est_fqlist->n - 1 - i;
+
+ msr = rdmsr(MSR_PERF_CTL);
+ msr &= ~0xffffULL;
+ msr |= est_fqlist->table[i];
+ wrmsr(MSR_PERF_CTL, msr);
+ cpuspeed = MSR2MHZ(est_fqlist->table[i], bus_clock);
+}
diff --git a/sys/arch/amd64/amd64/identcpu.c b/sys/arch/amd64/amd64/identcpu.c
index 5d6695e7e2b..0dbf1949607 100644
--- a/sys/arch/amd64/amd64/identcpu.c
+++ b/sys/arch/amd64/amd64/identcpu.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: identcpu.c,v 1.12 2007/05/06 03:37:08 gwk Exp $ */
+/* $OpenBSD: identcpu.c,v 1.13 2007/05/29 06:31:44 tedu Exp $ */
/* $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */
/*
@@ -109,6 +109,44 @@ cpu_amd64speed(int *freq)
return (0);
}
+#ifndef SMALL_KERNEL
+void intelcore_update_sensor(void *args);
+/*
+ * Temperature read on the CPU is relative to the maximum
+ * temperature supported by the CPU, Tj(Max).
+ * Poorly documented, refer to:
+ * http://softwarecommunity.intel.com/isn/Community/
+ * en-US/forums/thread/30228638.aspx
+ * Basically, depending on a bit in one msr, the max is either 85 or 100.
+ * Then we subtract the temperature portion of thermal status from
+ * max to get current temperature.
+ */
+void
+intelcore_update_sensor(void *args)
+{
+ struct cpu_info *ci = (struct cpu_info *) args;
+ u_int64_t msr;
+ int max = 100;
+
+ if (rdmsr(MSR_TEMPERATURE_TARGET) & MSR_TEMPERATURE_TARGET_LOW_BIT)
+ max = 85;
+
+ msr = rdmsr(MSR_THERM_STATUS);
+ if (msr & MSR_THERM_STATUS_VALID_BIT) {
+ ci->ci_sensor.value = max - MSR_THERM_STATUS_TEMP(msr);
+ /* micro degress */
+ ci->ci_sensor.value *= 1000000;
+ /* kelvin */
+ ci->ci_sensor.value += 273150000;
+ ci->ci_sensor.flags &= ~SENSOR_FINVALID;
+ } else {
+ ci->ci_sensor.value = 0;
+ ci->ci_sensor.flags |= SENSOR_FINVALID;
+ }
+}
+
+#endif
+
void (*setperf_setup)(struct cpu_info *);
void
@@ -183,6 +221,7 @@ identifycpu(struct cpu_info *ci)
x86_print_cacheinfo(ci);
+#ifndef SMALL_KERNEL
if (pnfeatset > 0x80000007) {
CPUID(0x80000007, dummy, dummy, dummy, pnfeatset);
@@ -192,6 +231,24 @@ identifycpu(struct cpu_info *ci)
}
}
+ if (!strncmp(cpu_model, "Intel", 5)) {
+ if (cpu_ecxfeature & CPUIDECX_EST) {
+ setperf_setup = est_init;
+ }
+ CPUID(0x06, val, dummy, dummy, dummy);
+ if (val & 0x1) {
+ strlcpy(ci->ci_sensordev.xname, ci->ci_dev->dv_xname,
+ sizeof(ci->ci_sensordev.xname));
+ ci->ci_sensor.type = SENSOR_TEMP;
+ sensor_task_register(ci, intelcore_update_sensor, 5);
+ sensor_attach(&ci->ci_sensordev, &ci->ci_sensor);
+ sensordev_install(&ci->ci_sensordev);
+ }
+ }
+
+#endif
+
+
/* AuthenticAMD: h t u A i t n e */
if (vendor[0] == 0x68747541 && vendor[1] == 0x69746e65 &&
vendor[2] == 0x444d4163) /* DMAc */
diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64
index c59f4bad515..626b98299eb 100644
--- a/sys/arch/amd64/conf/files.amd64
+++ b/sys/arch/amd64/conf/files.amd64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.amd64,v 1.29 2007/05/28 02:13:44 krw Exp $
+# $OpenBSD: files.amd64,v 1.30 2007/05/29 06:31:44 tedu Exp $
maxpartitions 16
maxusers 2 16 128
@@ -58,7 +58,8 @@ file arch/amd64/amd64/kgdb_machdep.c kgdb
# Basic clock - required
file arch/amd64/isa/clock.c
-file arch/amd64/amd64/powernow-k8.c
+file arch/amd64/amd64/powernow-k8.c !small_kernel
+file arch/amd64/amd64/est.c !small_kernel
include "dev/mii/files.mii"
diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h
index ef0bce98095..c99ec07058f 100644
--- a/sys/arch/amd64/include/cpu.h
+++ b/sys/arch/amd64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.29 2007/05/10 17:59:24 deraadt Exp $ */
+/* $OpenBSD: cpu.h,v 1.30 2007/05/29 06:31:44 tedu Exp $ */
/* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */
/*-
@@ -55,6 +55,7 @@
#include <sys/device.h>
#include <sys/lock.h>
#include <sys/sched.h>
+#include <sys/sensors.h>
struct cpu_info {
struct device *ci_dev;
@@ -127,6 +128,9 @@ struct cpu_info {
struct x86_64_tss ci_doubleflt_tss;
char *ci_doubleflt_stack;
+
+ struct ksensordev ci_sensordev;
+ struct ksensor ci_sensor;
};
#define CPUF_BSP 0x0001 /* CPU is the original BSP */
@@ -308,6 +312,9 @@ void x86_bus_space_mallocok(void);
void k8_powernow_init(struct cpu_info *);
void k8_powernow_setperf(int);
+void est_init(struct cpu_info *);
+void est_setperf(int);
+
#ifdef MULTIPROCESSOR
/* mp_setperf.c */
void mp_setperf_init(void);
diff --git a/sys/arch/amd64/include/specialreg.h b/sys/arch/amd64/include/specialreg.h
index 6b24513bef4..cdc85739ed6 100644
--- a/sys/arch/amd64/include/specialreg.h
+++ b/sys/arch/amd64/include/specialreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: specialreg.h,v 1.8 2007/04/27 01:57:16 dlg Exp $ */
+/* $OpenBSD: specialreg.h,v 1.9 2007/05/29 06:31:44 tedu Exp $ */
/* $NetBSD: specialreg.h,v 1.1 2003/04/26 18:39:48 fvdl Exp $ */
/* $NetBSD: x86/specialreg.h,v 1.2 2003/04/25 21:54:30 fvdl Exp $ */
@@ -173,6 +173,7 @@
#define MSR_BIOS_SIGN 0x08b
#define MSR_PERFCTR0 0x0c1
#define MSR_PERFCTR1 0x0c2
+#define MSR_FSB_FREQ 0x0cd /* Core Duo/Solo only */
#define MSR_MTRRcap 0x0fe
#define MSR_BBL_CR_ADDR 0x116 /* PII+ only */
#define MSR_BBL_CR_DECC 0x118 /* PII+ only */
@@ -188,6 +189,14 @@
#define MSR_MCG_CTL 0x17b
#define MSR_EVNTSEL0 0x186
#define MSR_EVNTSEL1 0x187
+#define MSR_PERF_STATUS 0x198 /* Pentium M */
+#define MSR_PERF_CTL 0x199 /* Pentium M */
+#define MSR_THERM_CONTROL 0x19a
+#define MSR_THERM_INTERRUPT 0x19b
+#define MSR_THERM_STATUS 0x19c
+#define MSR_THERM_STATUS_VALID_BIT 0x80000000
+#define MSR_THERM_STATUS_TEMP(msr) ((msr >> 16) & 0x7f)
+#define MSR_THERM2_CTL 0x19d /* Pentium M */
#define MSR_DEBUGCTLMSR 0x1d9
#define MSR_LASTBRANCHFROMIP 0x1db
#define MSR_LASTBRANCHTOIP 0x1dc
@@ -620,3 +629,7 @@
#define K7_BP1_MATCH 0xdd
#define K7_BP2_MATCH 0xde
#define K7_BP3_MATCH 0xdf
+
+/* not documented anywhere, see intelcore_update_sensor() */
+#define MSR_TEMPERATURE_TARGET 0xee
+#define MSR_TEMPERATURE_TARGET_LOW_BIT 0x40000000