AMD PowerNow K7 driver written by Martin Vegiard via PR port-i386/26239.

Enabled by default on GENERIC and GENERIC_LAPTOP.

Imported 1 year later... but it's here finally.
This commit is contained in:
xtraeme 2005-12-31 17:55:55 +00:00
parent 384a271baa
commit c9f2b0fc40
9 changed files with 394 additions and 16 deletions

View File

@ -1,4 +1,4 @@
LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.556 $>
LIST OF CHANGES FROM LAST RELEASE: <$Revision: 1.557 $>
[Note: This file does not mention every change made to the NetBSD source tree.
@ -158,3 +158,5 @@ Changes from NetBSD 3.0 to NetBSD 4.0:
was done by UCHIYAMA Yasushi. [tsutsui 20051229]
rtw(4): Add support for GCT Semiconductor GRF5101
transceiver/synthesizer. [dyoung 20051229]
i386: Add support for AMD PowerNow technology.
Written by Martin Vegiard. [xtraeme 20051231]

View File

@ -1,4 +1,4 @@
.\" $NetBSD: options.4,v 1.299 2005/12/20 18:15:30 elad Exp $
.\" $NetBSD: options.4,v 1.300 2005/12/31 17:55:55 xtraeme Exp $
.\"
.\" Copyright (c) 1996
.\" Perry E. Metzger. All rights reserved.
@ -30,7 +30,7 @@
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
.\"
.Dd December 20, 2005
.Dd December 31, 2005
.Os
.Dt OPTIONS 4
.Sh NAME
@ -2290,6 +2290,10 @@ Allow any user to change the frequency of an
.Tn Enhanced SpeedStep Technology
capable
.Tn CPU .
.It Cd options POWERNOW_K7
Include support for the AMD
.Tn PowerNow! Technology
present in AMD Athlon Mobile processors.
.It Cd options CPURESET_DELAY=value
Specifies the time (in millisecond) to wait before doing a hardware reset
in the last phase of a reboot.

View File

@ -1,4 +1,4 @@
# $NetBSD: GENERIC,v 1.714 2005/12/20 05:35:28 thorpej Exp $
# $NetBSD: GENERIC,v 1.715 2005/12/31 17:55:55 xtraeme Exp $
#
# GENERIC machine description file
#
@ -22,7 +22,7 @@ include "arch/i386/conf/std.i386"
options INCLUDE_CONFIG_FILE # embed config file in kernel binary
#ident "GENERIC-$Revision: 1.714 $"
#ident "GENERIC-$Revision: 1.715 $"
maxusers 32 # estimated number of users
@ -43,6 +43,9 @@ options USER_LDT # user-settable LDT; used by WINE
options ENHANCED_SPEEDSTEP
#options EST_FREQ_USERWRITE # any user can set frequency
# AMD PowerNow! K7
options POWERNOW_K7
options MTRR # memory-type range register syscall support
# doesn't work with MP just yet..
#options PERFCTRS # performance-monitoring counters support

View File

@ -1,4 +1,4 @@
# $NetBSD: GENERIC_LAPTOP,v 1.161 2005/12/20 05:35:28 thorpej Exp $
# $NetBSD: GENERIC_LAPTOP,v 1.162 2005/12/31 17:55:55 xtraeme Exp $
# From: NetBSD: GENERIC,v 1.414 2001/07/30 19:59:05 ad Exp
#
# GENERIC_LAPTOP -- GENERIC with cardbus and some USB devices enabled
@ -8,7 +8,7 @@ include "arch/i386/conf/std.i386"
#options INCLUDE_CONFIG_FILE # embed config file in kernel binary
#ident "GENERIC-$Revision: 1.161 $"
#ident "GENERIC-$Revision: 1.162 $"
maxusers 32 # estimated number of users
@ -27,6 +27,9 @@ options USER_LDT # user-settable LDT; used by WINE
options ENHANCED_SPEEDSTEP
#options EST_FREQ_USERWRITE # any user can set frequency
# AMD PowerNow! K7
options POWERNOW_K7
options MTRR # memory-type range register syscall support
#options PERFCTRS # performance-monitoring counters support

View File

@ -1,4 +1,4 @@
# $NetBSD: files.i386,v 1.274 2005/12/11 02:11:50 christos Exp $
# $NetBSD: files.i386,v 1.275 2005/12/31 17:55:55 xtraeme Exp $
#
# new style config file for i386 architecture
#
@ -64,6 +64,9 @@ defparam opt_beep.h BEEP_ONHALT_PITCH BEEP_ONHALT_PERIOD
# Enhanced SpeedStep
defflag ENHANCED_SPEEDSTEP
# PowerNow K7
defflag POWERNOW_K7
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
@ -486,6 +489,9 @@ file arch/i386/bios/vesa_text.c vesatext
file arch/i386/i386/est.c enhanced_speedstep
defflag opt_est.h EST_FREQ_USERWRITE
# AMD PowerNow K7
file arch/i386/i386/powernow_k7.c powernow_k7
# Atheros 5210/5211/5212 Hardware Abstraction Layer (HAL)
object /athhal-i386-elf.hal.o ath

View File

@ -1,4 +1,4 @@
/* $NetBSD: est.c,v 1.13 2005/12/31 09:52:46 xtraeme Exp $ */
/* $NetBSD: est.c,v 1.14 2005/12/31 17:55:55 xtraeme Exp $ */
/*
* Copyright (c) 2003 Michael Eriksson.
* All rights reserved.
@ -84,7 +84,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: est.c,v 1.13 2005/12/31 09:52:46 xtraeme Exp $");
__KERNEL_RCSID(0, "$NetBSD: est.c,v 1.14 2005/12/31 17:55:55 xtraeme Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -353,9 +353,6 @@ static const struct fqlist pentium_m_dothan[] = {
ENTRY("2.00", pentium_m_n755),
ENTRY("2.10", pentium_m_n765),
ENTRY("2.13", pentium_m_n770),
ENTRY("1.00", pentium_m_n723),
ENTRY("1.10", pentium_m_n733),
ENTRY("1.40", pentium_m_n738),
};
#undef ENTRY

View File

@ -1,4 +1,4 @@
/* $NetBSD: identcpu.c,v 1.24 2005/12/26 19:23:59 perry Exp $ */
/* $NetBSD: identcpu.c,v 1.25 2005/12/31 17:55:55 xtraeme Exp $ */
/*-
* Copyright (c) 1999, 2000, 2001 The NetBSD Foundation, Inc.
@ -37,10 +37,11 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.24 2005/12/26 19:23:59 perry Exp $");
__KERNEL_RCSID(0, "$NetBSD: identcpu.c,v 1.25 2005/12/31 17:55:55 xtraeme Exp $");
#include "opt_cputype.h"
#include "opt_enhanced_speedstep.h"
#include "opt_powernow_k7.h"
#include <sys/param.h>
#include <sys/systm.h>
@ -130,6 +131,7 @@ static const char * const amd_brand[] = {
};
u_int cpu_serial[3];
u_int amd_powernow_probe(struct cpu_info *);
char cpu_brand_string[49];
static char amd_brand_name[48];
@ -913,6 +915,20 @@ amd_family5_setup(struct cpu_info *ci)
}
}
u_int
amd_powernow_probe(struct cpu_info *ci)
{
u_int32_t eax, ebx, ecx, edx;
CPUID(0x80000007, eax, ebx, ecx, edx);
/* checking for Freq ID control (FID) and Voltage ID control (VID) */
if ((edx & (0x2 | 0x4)) > 0)
return 1;
return 0;
}
/*
* Transmeta Crusoe LongRun Support by Tamotsu Hattori.
* Port from FreeBSD-current(August, 2001) to NetBSD by tshiozak.
@ -1430,4 +1446,10 @@ identifycpu(struct cpu_info *ci)
}
#endif /* ENHANCED_SPEEDSTEP */
#ifdef POWERNOW_K7
if (amd_powernow_probe (ci)) {
pnowk7_init(ci);
}
#endif /* POWERNOW_K7 */
}

View File

@ -0,0 +1,338 @@
/* $NetBSD: powernow_k7.c,v 1.1 2005/12/31 17:55:55 xtraeme Exp $ */
/*
* Copyright (c) 2004 Martin Végiard.
* 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.
*/
/* AMD PowerNow! K7 driver */
/* Sysctl related code was adapted from NetBSD's i386/est.c for compatibility */
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: powernow_k7.c,v 1.1 2005/12/31 17:55:55 xtraeme Exp $");
#include <sys/types.h>
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <dev/isa/isareg.h>
#include <machine/cpu.h>
#include <machine/isa_machdep.h>
#define BIOS_START 0xe0000
#define BIOS_END 0x20000
#define BIOS_LEN BIOS_END - BIOS_START
#define MSR_K7_CTL 0xC0010041
#define CTL_SET_FID 0x0000000000010000ULL
#define CTL_SET_VID 0x0000000000020000ULL
#define cpufreq(x) fsb * fid_codes[x] / 10
struct psb_s {
char signature[10]; /* AMDK7PNOW! */
uint8_t version;
uint8_t flags;
uint16_t ttime; /* Min Settling time */
uint8_t reserved;
uint8_t n_pst;
};
struct pst_s {
cpuid_t cpuid;
uint8_t fsb; /* Front Side Bus frequency (Mhz) */
uint8_t fid; /* Max Frequency code */
uint8_t vid; /* Max Voltage code */
uint8_t n_states; /* Number of states */
};
struct state_s {
uint8_t fid; /* Frequency code */
uint8_t vid; /* Voltage code */
};
struct freq_table_s {
unsigned int frequency;
struct state_s *state;
};
/* Taken from powernow-k7.c/Linux by Dave Jones */
static int fid_codes[32] = {
110, 115, 120, 125, 50, 55, 60, 65,
70, 75, 80, 85, 90, 95, 100, 105,
30, 190, 40, 200, 130, 135, 140, 210,
150, 225, 160, 165, 170, 180, -1, -1
};
/* Static variables */
static unsigned int fsb;
static unsigned int cur_freq;
static unsigned int ttime;
static unsigned int n_states;
static struct freq_table_s *freq_table;
static int powernow_node_target, powernow_node_current;
/* Prototypes */
struct state_s *pnowk7_getstates(cpuid_t cpuid);
int pnowk7_setstate(unsigned int freq);
static int powernow_sysctl_helper(SYSCTLFN_ARGS);
static int
powernow_sysctl_helper(SYSCTLFN_ARGS)
{
struct sysctlnode node;
int fq, oldfq, error;
if (freq_table == NULL)
return (EOPNOTSUPP);
node = *rnode;
node.sysctl_data = &fq;
oldfq = 0;
if (rnode->sysctl_num == powernow_node_target)
fq = oldfq = cur_freq;
else if (rnode->sysctl_num == powernow_node_current)
fq = cur_freq;
else
return (EOPNOTSUPP);
error = sysctl_lookup(SYSCTLFN_CALL(&node));
if (error || newp == NULL)
return (error);
/* support writing to ...frequency.target */
if (rnode->sysctl_num == powernow_node_target && fq != oldfq) {
if (pnowk7_setstate(fq) == 0)
{
cur_freq = fq;
} else
aprint_normal("\nInvalid CPU frequency request\n");
}
return (0);
}
struct state_s *
pnowk7_getstates(cpuid_t cpuid)
{
unsigned int i, j;
char *ptr;
struct psb_s *psb;
struct pst_s *pst;
/*
* Look in the 0xe0000 - 0x20000 physical address
* range for the pst tables; 16 byte blocks
*/
ptr = (char *)ISA_HOLE_VADDR(BIOS_START);
for (i = 0; i < BIOS_LEN; i += 16, ptr += 16) {
if (memcmp(ptr, "AMDK7PNOW!", 10) == 0)
{
psb = (struct psb_s *) ptr;
ptr += sizeof(struct psb_s);
ttime = psb->ttime;
/* Only this version is supported */
if (psb->version != 0x12)
return 0;
/* Find the right PST */
for (j = 0; j < psb->n_pst; j++) {
pst = (struct pst_s *) ptr;
ptr += sizeof(struct pst_s);
/* Use the first PST with matching CPUID */
if ((cpuid == pst->cpuid) ||
(cpuid == (pst->cpuid - 0x100)))
/*
* XXX
* some braindead BIOS seems to expect
* signature 0x780 from Athlon. Let's try to
* subtract 0x100 from the BIOS supplied values
* to fix.
*/
{
/*
* XXX
* I need more info on this.
* For now, let's just ignore it
*/
if ((cpuid & 0xFF) == 0x60)
return 0;
fsb = pst->fsb;
n_states = pst->n_states;
return (struct state_s *) ptr;
} else
ptr += sizeof(struct state_s) *
pst->n_states;
}
/* aprint_normal("No match was found for your CPUID\n"); */
return 0;
}
}
/* aprint_normal("Power state table not found\n"); */
return 0;
}
int
pnowk7_setstate(unsigned int freq)
{
unsigned int i;
u_int32_t sgtc, vid, fid;
u_int64_t ctl;
vid = fid = 0;
for (i = 0; i < n_states; i++) {
/* Do we know how to set that frequency? */
if (freq_table[i].frequency == freq)
{
fid = freq_table[i].state->fid;
vid = freq_table[i].state->vid;
}
}
if (fid == 0 || vid == 0)
return -1;
/* Get CTL and only modify fid/vid/sgtc */
ctl = rdmsr(MSR_K7_CTL);
/* FID */
ctl &= 0xFFFFFFFFFFFFFF00ULL;
ctl |= fid;
/* VID */
ctl &= 0xFFFFFFFFFFFF00FFULL;
ctl |= vid << 8;
/* SGTC */
if ((sgtc = ttime * 100) < 10000) sgtc = 10000;
ctl &= 0xFFF00000FFFFFFFFULL;
ctl |= (u_int64_t)sgtc << 32;
if (cur_freq > freq) {
wrmsr(MSR_K7_CTL, ctl | CTL_SET_FID);
wrmsr(MSR_K7_CTL, ctl | CTL_SET_VID);
} else {
wrmsr(MSR_K7_CTL, ctl | CTL_SET_VID);
wrmsr(MSR_K7_CTL, ctl | CTL_SET_FID);
}
ctl = rdmsr(MSR_K7_CTL);
return 0;
}
void
pnowk7_init(struct cpu_info *ci)
{
int rc;
unsigned int i, freq_names_len, len = 0;
char *freq_names, *cpuname;
const struct sysctlnode *node, *pnownode, *freqnode;
struct state_s *s = pnowk7_getstates(ci->ci_signature);
cpuname = ci->ci_dev->dv_xname;
if (s == 0) {
/* aprint_normal("AMD PowerNow not supported\n"); */
return;
}
freq_names_len = n_states * (sizeof("9999 ")-1) + 1;
freq_names = malloc(freq_names_len, M_SYSCTLDATA, M_WAITOK);
freq_table = malloc(sizeof(struct freq_table_s) * n_states, \
M_TEMP, M_WAITOK);
for (i = 0; i < n_states; i++, s++) {
freq_table[i].frequency = cpufreq(s->fid);
freq_table[i].state = s;
len += snprintf(freq_names + len, freq_names_len - len, "%d%s",
freq_table[i].frequency, i < n_states - 1 ? " " : "");
}
/* On bootup the frequency should be at it's max */
cur_freq = freq_table[i-1].frequency;
/* Create sysctl machdep.powernow.frequency. */
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, &pnownode,
0, CTLTYPE_NODE, "powernow", NULL,
NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
if ((rc = sysctl_createv(NULL, 0, &pnownode, &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,
powernow_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
powernow_node_target = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_INT, "current", NULL,
powernow_sysctl_helper, 0, NULL, 0, CTL_CREATE, CTL_EOL)) != 0)
goto err;
powernow_node_current = node->sysctl_num;
if ((rc = sysctl_createv(NULL, 0, &freqnode, &node,
0, CTLTYPE_STRING, "available", NULL,
NULL, 0, freq_names, freq_names_len, CTL_CREATE, CTL_EOL)) != 0)
goto err;
/* aprint_normal("AMD PowerNow supported current frequency : %d Mhz\n", \
cur_freq); */
aprint_normal("%s: AMD PowerNow! Technology supported\n", cpuname);
aprint_normal("%s: available frequencies (Mhz): %s\n",
cpuname, freq_names);
return;
err:
aprint_normal("%s: sysctl_createv failed (rc = %d)\n", __func__, rc);
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: cpu.h,v 1.120 2005/12/26 19:23:59 perry Exp $ */
/* $NetBSD: cpu.h,v 1.121 2005/12/31 17:55:55 xtraeme Exp $ */
/*-
* Copyright (c) 1990 The Regents of the University of California.
@ -425,6 +425,9 @@ void x86_bus_space_mallocok(void);
/* est.c */
void est_init(struct cpu_info *);
/* pnow_k7.c */
void pnowk7_init(struct cpu_info *);
#endif /* _KERNEL */
/*