Integrate support for Enhanced SpeedStep Technology (est) that

Michael Eriksson posted to port-i386 on 20031102, with various
modifications by me to work in the new sysctl(9) framework.

The code is enabled with 'options ENHANCED_SPEEDSTEP', and if
the CPU supports EST the following sysctl(8) nodes appear
(with the values that a Dell Inspiron 8600 + WUXGA with a
1.4GHz Pentium M CPU supports):
	machdep.est.cpu_brand = Intel(R) Pentium(R) M processor 1400MHz
	machdep.est.frequency.target = 1400
	machdep.est.frequency.current = 1400
	machdep.est.frequency.available = 1400 1200 1000 800 600

If EST support isn't available, the "machdep.est" sysctl sub-MIB
is not created.

Once we have a more general "CPU frequency" control API we can
migrate this code to using that.

Thanks to Michael Erikkson for providing this code!
This commit is contained in:
lukem 2004-04-30 02:05:42 +00:00
parent 3da1e3206a
commit 2df026b026
6 changed files with 248 additions and 107 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: files.i386,v 1.255 2004/04/18 18:36:56 fvdl Exp $
# $NetBSD: files.i386,v 1.256 2004/04/30 02:05:42 lukem Exp $
#
# new style config file for i386 architecture
#
@ -61,6 +61,9 @@ defflag opt_beep.h BEEP_ONHALT
defparam opt_beep.h BEEP_ONHALT_COUNT
defparam opt_beep.h BEEP_ONHALT_PITCH BEEP_ONHALT_PERIOD
# Enhanced SpeedStep
defflag ENHANCED_SPEEDSTEP
file arch/i386/i386/autoconf.c
file arch/i386/i386/db_dbgreg.S ddb | kstack_check_dr0
file arch/i386/i386/db_disasm.c ddb
@ -470,4 +473,7 @@ device vesatext
attach vesatext at vesabios
file arch/i386/bios/vesa_text.c vesatext
# Enhanced SpeedStep
file arch/i386/i386/est.c enhanced_speedstep
include "arch/i386/conf/majors.i386"

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpu.c,v 1.18 2004/02/20 17:35:01 yamt Exp $ */
/* $NetBSD: cpu.c,v 1.19 2004/04/30 02:05:43 lukem Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@ -71,7 +71,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.18 2004/02/20 17:35:01 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: cpu.c,v 1.19 2004/04/30 02:05:43 lukem Exp $");
#include "opt_ddb.h"
#include "opt_multiprocessor.h"
@ -580,6 +580,7 @@ cpu_hatch(void *v)
cpu_probe_features(ci);
cpu_feature &= ci->ci_feature_flags;
cpu_feature2 &= ci->ci_feature2_flags;
#ifdef DEBUG
if (ci->ci_flags & CPUF_PRESENT)

View File

@ -1,3 +1,4 @@
/* $NetBSD: est.c,v 1.2 2004/04/30 02:05:43 lukem Exp $ */
/*
* Copyright (c) 2003 Michael Eriksson.
* All rights reserved.
@ -24,14 +25,45 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*-
* Copyright (c) 2004 The NetBSD Foundation, Inc.
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
* This is a driver for Intel's Enhanced SpeedStep Technology (EST),
* 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.
@ -46,10 +78,15 @@
* 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/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: est.c,v 1.2 2004/04/30 02:05:43 lukem Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <machine/cpu.h>
#include <machine/specialreg.h>
@ -144,7 +181,7 @@ static const struct fq_info pentium_m_1700[] = {
struct fqlist {
const char *brand_tag;
int n;
size_t tablec;
const struct fq_info *table;
};
@ -166,7 +203,7 @@ static const struct fqlist pentium_m[] = {
struct est_cpu {
const char *brand_prefix;
const char *brand_suffix;
int n;
size_t listc;
const struct fqlist *list;
};
@ -178,25 +215,72 @@ static const struct est_cpu est_cpus[] = {
},
};
#define NCPUS (sizeof(est_cpus) / sizeof(est_cpus[0]))
#define NESTCPUS (sizeof(est_cpus) / sizeof(est_cpus[0]))
#define MSRVALUE(mhz, mv) ((((mhz) / 100) << 8) | (((mv) - 700) / 16))
#define MSR2MHZ(msr) ((((int) (msr) >> 8) & 0xff) * 100)
#define MSR2MV(msr) (((int) (msr) & 0xff) * 16 + 700)
static const struct fqlist *est_fqlist;
static const struct fqlist *est_fqlist; /* not NULL if functional */
static int est_node_target, est_node_current;
static const char est_desc[] = "Enhanced SpeedStep";
static int
est_sysctl_helper(SYSCTLFN_ARGS)
{
struct sysctlnode node;
int fq, oldfq, error;
if (est_fqlist == NULL)
return (EOPNOTSUPP);
node = *rnode;
node.sysctl_data = &fq;
oldfq = 0;
if (rnode->sysctl_num == est_node_target)
fq = oldfq = MSR2MHZ(rdmsr(MSR_PERF_CTL));
else if (rnode->sysctl_num == est_node_current)
fq = MSR2MHZ(rdmsr(MSR_PERF_STATUS));
else
return (EOPNOTSUPP);
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return (error);
/* support writing to ...frequency.target */
if (rnode->sysctl_num == est_node_target && fq != oldfq) {
int i;
u_int64_t msr;
for (i = est_fqlist->tablec - 1; i > 0; i--)
if (est_fqlist->table[i].mhz >= fq)
break;
fq = est_fqlist->table[i].mhz;
msr = (rdmsr(MSR_PERF_CTL) & ~0xffffULL) |
MSRVALUE(est_fqlist->table[i].mhz,
est_fqlist->table[i].mv);
wrmsr(MSR_PERF_CTL, msr);
}
return (0);
}
void
est_init(ci)
struct cpu_info *ci;
est_init(struct cpu_info *ci)
{
int i, j, n, mhz, mv;
const struct est_cpu *cpu;
u_int64_t msr;
char *tag;
const struct fqlist *fql;
const struct est_cpu *cpu;
const struct fqlist *fql;
struct sysctlnode *node, *estnode, *freqnode;
u_int64_t msr;
int i, j, rc;
int mhz, mv;
size_t len, freq_len;
char *tag, *freq_names;
if ((cpu_feature2 & CPUID2_EST) == 0)
return;
@ -204,106 +288,104 @@ est_init(ci)
msr = rdmsr(MSR_PERF_STATUS);
mhz = MSR2MHZ(msr);
mv = MSR2MV(msr);
printf("%s: Enhanced SpeedStep running at %d MHz (%d mV)\n",
ci->ci_dev->dv_xname, mhz, mv);
aprint_normal("%s: %s running at %d MHz (%d mV)\n",
ci->ci_dev->dv_xname, est_desc, mhz, mv);
/*
* Look for a CPU matching cpu_brand_string.
*/
for (i = 0; est_fqlist == NULL && i < NCPUS; i++) {
for (i = 0; est_fqlist == NULL && i < NESTCPUS; i++) {
cpu = &est_cpus[i];
n = strlen(cpu->brand_prefix);
if (strncmp(cpu->brand_prefix, cpu_brand_string, n) != 0)
len = strlen(cpu->brand_prefix);
if (strncmp(cpu->brand_prefix, cpu_brand_string, len) != 0)
continue;
tag = cpu_brand_string + n;
for (j = 0; j < cpu->n; j++) {
tag = cpu_brand_string + len;
for (j = 0; j < cpu->listc; j++) {
fql = &cpu->list[j];
n = strlen(fql->brand_tag);
if (!strncmp(fql->brand_tag, tag, n) &&
!strcmp(cpu->brand_suffix, tag + n)) {
len = strlen(fql->brand_tag);
if (!strncmp(fql->brand_tag, tag, len) &&
!strcmp(cpu->brand_suffix, tag + len)) {
est_fqlist = fql;
break;
}
}
}
if (est_fqlist == NULL) {
printf("%s: unknown EST cpu\n", ci->ci_dev->dv_xname);
aprint_normal("%s: unknown %s CPU\n",
ci->ci_dev->dv_xname, est_desc);
return;
}
/*
* Check that the current operating point is in our list.
*/
for (i = est_fqlist->n - 1; i >= 0; i--)
for (i = est_fqlist->tablec - 1; i >= 0; i--)
if (est_fqlist->table[i].mhz == mhz &&
est_fqlist->table[i].mv == mv)
break;
if (i < 0) {
printf("%s: EST operating point not in table\n",
ci->ci_dev->dv_xname);
aprint_normal("%s: %s operating point not in table\n",
ci->ci_dev->dv_xname, est_desc);
est_fqlist = NULL; /* flag as not functional */
return;
}
/*
* OK, set the flag and tell the user the available frequencies.
* OK, tell the user the available frequencies.
*/
i386_has_est = 1;
printf("%s: available speeds: ", ci->ci_dev->dv_xname);
for (i = 0; i < est_fqlist->n; i++)
printf("%d%s", est_fqlist->table[i].mhz,
i < est_fqlist->n - 1 ? ", " : " MHz\n");
}
int
est_get_target_fq()
{
return MSR2MHZ(rdmsr(MSR_PERF_CTL));
}
int
est_set_target_fq(fq)
int fq;
{
int i;
u_int64_t msr;
if (est_fqlist == NULL)
return EOPNOTSUPP;
for (i = est_fqlist->n - 1; i > 0; i--)
if (est_fqlist->table[i].mhz >= fq)
break;
fq = est_fqlist->table[i].mhz;
msr = (rdmsr(MSR_PERF_CTL) & ~0xffffULL) |
MSRVALUE(est_fqlist->table[i].mhz, est_fqlist->table[i].mv);
wrmsr(MSR_PERF_CTL, msr);
return 0;
}
int
est_get_current_fq()
{
return MSR2MHZ(rdmsr(MSR_PERF_STATUS));
}
int
est_get_min_fq()
{
if (est_fqlist == NULL)
return 0;
return est_fqlist->table[est_fqlist->n - 1].mhz;
}
int
est_get_max_fq()
{
if (est_fqlist == NULL)
return 0;
return est_fqlist->table[0].mhz;
freq_len = est_fqlist->tablec * (sizeof("9999 ")-1) + 1;
freq_names = malloc(freq_len, M_SYSCTLDATA, M_WAITOK);
freq_names[0] = '\0';
len = 0;
for (i = 0; i < est_fqlist->tablec; i++) {
len += snprintf(freq_names + len, freq_len - len, "%d%s",
est_fqlist->table[i].mhz,
i < est_fqlist->tablec - 1 ? " " : "");
}
aprint_normal("%s: %s frequencies available (MHz): %s\n",
ci->ci_dev->dv_xname, est_desc, freq_names);
/*
* Setup the sysctl sub-tree machdep.est.*
*/
if ((rc = sysctl_createv(NULL, 0, NULL, &node,
CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &node, &estnode,
0, CTLTYPE_NODE, "est", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &estnode, &node,
0, CTLTYPE_STRING, "cpu_brand", NULL,
NULL, 0, &cpu_brand_string, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &estnode, &freqnode,
0, CTLTYPE_NODE, "frequency", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
est_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
est_node_target = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_INT, "current", NULL,
est_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
est_node_current = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_STRING, "available", NULL,
NULL, 0, freq_names, freq_len, CTL_CREATE, CTL_EOL)) != 0)
goto err;
return;
err:
aprint_normal("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: identcpu.c,v 1.12 2004/04/22 00:34:52 itojun Exp $ */
/* $NetBSD: identcpu.c,v 1.13 2004/04/30 02:05:43 lukem Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@ -37,9 +37,10 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.12 2004/04/22 00:34:52 itojun Exp $");
__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.13 2004/04/30 02:05:43 lukem Exp $");
#include "opt_cputype.h"
#include "opt_enhanced_speedstep.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -126,6 +127,7 @@ static const char * const amd_brand[] = {
};
u_int cpu_serial[3];
char cpu_brand_string[49];
static char amd_brand_name[48];
void cyrix6x86_cpu_setup(struct cpu_info *);
@ -688,6 +690,7 @@ cpu_probe_base_features(struct cpu_info *ci)
int iterations, i, j;
u_int8_t desc;
u_int32_t dummy1, dummy2, miscbytes;
u_int32_t brand[12];
if (ci->ci_cpuid_level < 0)
return;
@ -698,10 +701,22 @@ cpu_probe_base_features(struct cpu_info *ci)
ci->ci_vendor[1]);
ci->ci_vendor[3] = 0;
CPUID(0x80000000, brand[0], brand[1], brand[2], brand[3]);
if (brand[0] >= 0x80000004) {
CPUID(0x80000002, brand[0], brand[1], brand[2], brand[3]);
CPUID(0x80000003, brand[4], brand[5], brand[6], brand[7]);
CPUID(0x80000004, brand[8], brand[9], brand[10], brand[11]);
for (i = 0; i < 48; i++)
if (((char *) brand)[i] != ' ')
break;
memcpy(cpu_brand_string, ((char *) brand) + i, 48 - i);
}
if (ci->ci_cpuid_level < 1)
return;
CPUID(1, ci->ci_signature, miscbytes, dummy1, ci->ci_feature_flags);
CPUID(1, ci->ci_signature, miscbytes, ci->ci_feature2_flags,
ci->ci_feature_flags);
/* Brand is low order 8 bits of ebx */
ci->ci_brand_id = miscbytes & 0xff;
@ -797,7 +812,6 @@ amd_family6_probe(struct cpu_info *ci)
{
u_int32_t lfunc;
u_int32_t descs[4];
u_int32_t brand[12];
char *p;
int i;
@ -811,15 +825,11 @@ amd_family6_probe(struct cpu_info *ci)
ci->ci_feature_flags |= descs[3];
}
if (lfunc < 0x80000004)
if (*cpu_brand_string == '\0')
return;
CPUID(0x80000002, brand[0], brand[1], brand[2], brand[3]);
CPUID(0x80000003, brand[4], brand[5], brand[6], brand[7]);
CPUID(0x80000004, brand[8], brand[9], brand[10], brand[11]);
for (i = 1; i < sizeof(amd_brand) / sizeof(amd_brand[0]); i++)
if ((p = strstr((char *)brand, amd_brand[i])) != NULL) {
if ((p = strstr(cpu_brand_string, amd_brand[i])) != NULL) {
ci->ci_brand_id = i;
strlcpy(amd_brand_name, p, sizeof(amd_brand_name));
break;
@ -1217,8 +1227,31 @@ identifycpu(struct cpu_info *ci)
}
}
if (ci->ci_feature2_flags) {
bitmask_snprintf(ci->ci_feature2_flags,
CPUID2_FLAGS, buf, sizeof(buf));
printf("%s: features2 %s\n", cpuname, buf);
}
if (*cpu_brand_string != '\0')
printf("%s: \"%s\"\n", cpuname, cpu_brand_string);
x86_print_cacheinfo(ci);
if (cpu_feature & CPUID_TM) {
if (rdmsr(MSR_MISC_ENABLE) & (1 << 3)) {
if ((cpu_feature2 & CPUID2_TM2) &&
(rdmsr(MSR_THERM2_CTL) & (1 << 16)))
printf("%s: using thermal monitor 2\n",
cpuname);
else
printf("%s: using thermal monitor 1\n",
cpuname);
} else
printf("%s: running without thermal monitor!\n",
cpuname);
}
if (ci->ci_cpuid_level >= 3 && (ci->ci_feature_flags & CPUID_PN)) {
printf("%s: serial number %04X-%04X-%04X-%04X-%04X-%04X\n",
cpuname,
@ -1328,4 +1361,15 @@ identifycpu(struct cpu_info *ci)
} else
i386_use_fxsave = 0;
#endif /* I686_CPU */
#ifdef ENHANCED_SPEEDSTEP
if (cpu_feature2 & CPUID2_EST) {
if (rdmsr(MSR_MISC_ENABLE) & (1 << 16))
est_init(ci);
else
printf("%s: Enhanced SpeedStep disabled by BIOS\n",
cpuname);
}
#endif /* ENHANCED_SPEEDSTEP */
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: machdep.c,v 1.552 2004/03/24 15:34:49 atatat Exp $ */
/* $NetBSD: machdep.c,v 1.553 2004/04/30 02:05:43 lukem Exp $ */
/*-
* Copyright (c) 1996, 1997, 1998, 2000 The NetBSD Foundation, Inc.
@ -72,7 +72,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.552 2004/03/24 15:34:49 atatat Exp $");
__KERNEL_RCSID(0, "$NetBSD: machdep.c,v 1.553 2004/04/30 02:05:43 lukem Exp $");
#include "opt_beep.h"
#include "opt_compat_ibcs2.h"
@ -226,6 +226,7 @@ int physmem;
int dumpmem_low;
int dumpmem_high;
unsigned int cpu_feature;
unsigned int cpu_feature2;
int cpu_class;
int i386_fpu_present;
int i386_fpu_exception;
@ -1354,6 +1355,7 @@ init386(paddr_t first_avail)
cpu_probe_features(&cpu_info_primary);
cpu_feature = cpu_info_primary.ci_feature_flags;
cpu_feature2 = cpu_info_primary.ci_feature2_flags;
lwp0.l_addr = proc0paddr;
cpu_info_primary.ci_curpcb = &lwp0.l_addr->u_pcb;

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpu.h,v 1.113 2004/02/20 17:35:01 yamt Exp $ */
/* $NetBSD: cpu.h,v 1.114 2004/04/30 02:05:43 lukem Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
@ -118,7 +118,8 @@ struct cpu_info {
int32_t ci_cpuid_level;
u_int32_t ci_signature; /* X86 cpuid type */
u_int32_t ci_feature_flags;/* X86 CPUID feature bits */
u_int32_t ci_feature_flags;/* X86 %edx CPUID feature bits */
u_int32_t ci_feature2_flags;/* X86 %ecx CPUID feature bits */
u_int32_t ci_cpu_class; /* CPU class */
u_int32_t ci_brand_id; /* Intel brand id */
u_int32_t ci_vendor[4]; /* vendor string */
@ -338,8 +339,10 @@ struct cpu_cpuid_nameclass {
extern int biosbasemem;
extern int biosextmem;
extern unsigned int cpu_feature;
extern unsigned int cpu_feature2;
extern int cpu;
extern int cpu_class;
extern char cpu_brand_string[];
extern const struct cpu_nocpuid_nameclass i386_nocpuid_cpus[];
extern const struct cpu_cpuid_nameclass i386_cpuid_cpus[];
@ -430,6 +433,9 @@ void x86_bus_space_mallocok(void);
#include <machine/psl.h> /* Must be after struct cpu_info declaration */
/* est.c */
void est_init(struct cpu_info *);
#endif /* _KERNEL */
/*