summaryrefslogtreecommitdiff
path: root/sys/arch/i386
diff options
context:
space:
mode:
authorTed Unangst <tedu@cvs.openbsd.org>2004-07-14 05:34:15 +0000
committerTed Unangst <tedu@cvs.openbsd.org>2004-07-14 05:34:15 +0000
commit228f858cd931a333f7231e84d9de3d6a6a525421 (patch)
tree2986c9a7e6fb2b91f2876efe1214680fe7cf5390 /sys/arch/i386
parent1b846e86fa3a06aa5111f79f2d6ac63df04c78a7 (diff)
setperf driver for powernow in amd k7 cpus. derived from a diff to
tech-i386@netbsd by martin vegiard, and adapted to fit in here. as yet untested. ok deraadt@
Diffstat (limited to 'sys/arch/i386')
-rw-r--r--sys/arch/i386/conf/files.i3863
-rw-r--r--sys/arch/i386/i386/machdep.c3
-rw-r--r--sys/arch/i386/i386/powernow-k7.c243
-rw-r--r--sys/arch/i386/include/cpu.h4
4 files changed, 250 insertions, 3 deletions
diff --git a/sys/arch/i386/conf/files.i386 b/sys/arch/i386/conf/files.i386
index cfc875f878f..2009ce890b3 100644
--- a/sys/arch/i386/conf/files.i386
+++ b/sys/arch/i386/conf/files.i386
@@ -1,4 +1,4 @@
-# $OpenBSD: files.i386,v 1.125 2004/06/22 09:47:01 mickey Exp $
+# $OpenBSD: files.i386,v 1.126 2004/07/14 05:34:14 tedu Exp $
#
# new style config file for i386 architecture
#
@@ -34,6 +34,7 @@ file arch/i386/i386/ns_cksum.c ns
file arch/i386/i386/p4tcc.c !small_kernel & i686_cpu
file arch/i386/i386/pmap.c
file arch/i386/i386/powernow.c !small_kernel
+file arch/i386/i386/powernow-k7.c !small_kernel
file arch/i386/i386/process_machdep.c
file arch/i386/i386/procfs_machdep.c procfs
file arch/i386/i386/random.s
diff --git a/sys/arch/i386/i386/machdep.c b/sys/arch/i386/i386/machdep.c
index 1b62aff3beb..9c5ee727b97 100644
--- a/sys/arch/i386/i386/machdep.c
+++ b/sys/arch/i386/i386/machdep.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: machdep.c,v 1.305 2004/07/05 05:18:42 david Exp $ */
+/* $OpenBSD: machdep.c,v 1.306 2004/07/14 05:34:14 tedu Exp $ */
/* $NetBSD: machdep.c,v 1.214 1996/11/10 03:16:17 thorpej Exp $ */
/*-
@@ -1380,6 +1380,7 @@ amd_family6_setup(cpu_device, model, step)
pagezero = sse2_pagezero;
else
pagezero = i686_pagezero;
+ k7_powernow_init(curcpu()->ci_signature);
#endif
}
diff --git a/sys/arch/i386/i386/powernow-k7.c b/sys/arch/i386/i386/powernow-k7.c
new file mode 100644
index 00000000000..cf17dc06f0e
--- /dev/null
+++ b/sys/arch/i386/i386/powernow-k7.c
@@ -0,0 +1,243 @@
+/*
+ * 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 */
+
+#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/bus.h>
+
+#if 0
+/* WTF? */
+#define BIOS_START 0xe0000
+#define BIOS_END 0x20000
+#define BIOS_LEN BIOS_END - BIOS_START
+#endif
+#define BIOS_START 0xe0000
+#define BIOS_LEN 0x20000
+
+#define MSR_K7_CTL 0xC0010041
+#define CTL_SET_FID 0x0000000000010000ULL
+#define CTL_SET_VID 0x0000000000020000ULL
+
+#define cpufreq(x) k7pnow_fsb * k7pnow_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 {
+ uint32_t signature;
+ 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 k7pnow_freq_table_s {
+ unsigned int frequency;
+ struct state_s *state;
+};
+
+/* Taken from powernow-k7.c/Linux by Dave Jones */
+int k7pnow_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 */
+unsigned int k7pnow_fsb;
+unsigned int k7pnow_cur_freq;
+unsigned int k7pnow_ttime;
+unsigned int k7pnow_nstates;
+struct k7pnow_freq_table_s *k7pnow_freq_table;
+
+
+/* Prototypes */
+struct state_s *k7_powernow_getstates(uint32_t);
+
+struct state_s *
+k7_powernow_getstates(uint32_t signature)
+{
+ unsigned int i, j;
+ struct psb_s *psb;
+ struct pst_s *pst;
+ char *ptr;
+
+ /*
+ * Look in the 0xe0000 - 0x20000 physical address
+ * range for the pst tables; 16 byte blocks
+ */
+ if (bus_space_map(I386_BUS_SPACE_MEM, BIOS_START, BIOS_LEN, 0,
+ (bus_space_handle_t *)&ptr)) {
+ printf("k7_powernow: couldn't map BIOS\n");
+ return NULL;
+ }
+
+ 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);
+
+ k7pnow_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 (signature == pst->signature) {
+ /*
+ * XXX I need more info on this.
+ * For now, let's just ignore it
+ */
+ if ((signature & 0xFF) == 0x60)
+ return 0;
+
+ k7pnow_fsb = pst->fsb;
+ k7pnow_nstates = pst->n_states;
+ return (struct state_s *)ptr;
+ } else
+ ptr += sizeof(struct state_s) *
+ pst->n_states;
+ }
+ /* printf("No match was found for your CPUID\n"); */
+ return 0;
+ }
+ }
+ /* printf("Power state table not found\n"); */
+ return 0;
+}
+
+int
+k7_powernow_setperf(int level)
+{
+ unsigned int low, high, freq, i;
+ uint32_t sgtc, vid = 0, fid = 0;
+ uint64_t ctl;
+
+ high = k7pnow_freq_table[k7pnow_nstates - 1].frequency;
+ low = k7pnow_freq_table[0].frequency;
+ freq = low + (high - low) * level / 100;
+
+ for (i = 0; i < k7pnow_nstates; i++) {
+ /* Do we know how to set that frequency? */
+ if (k7pnow_freq_table[i].frequency >= freq) {
+ fid = k7pnow_freq_table[i].state->fid;
+ vid = k7pnow_freq_table[i].state->vid;
+ break;
+ }
+ }
+
+ 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 = k7pnow_ttime * 100) < 10000) sgtc = 10000;
+ ctl &= 0xFFF00000FFFFFFFFULL;
+ ctl |= (uint64_t)sgtc << 32;
+
+ if (k7pnow_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
+k7_powernow_init(uint32_t signature)
+{
+ unsigned int i, freq_names_len, len = 0;
+ struct state_s *s;
+ char *freq_names;
+
+ s = k7_powernow_getstates(signature);
+ if (s == 0)
+ return;
+
+ freq_names_len = k7pnow_nstates * (sizeof("9999 ")-1) + 1;
+ freq_names = malloc(freq_names_len, M_TEMP, M_WAITOK);
+
+ k7pnow_freq_table = malloc(sizeof(struct k7pnow_freq_table_s) *
+ k7pnow_nstates, M_TEMP, M_WAITOK);
+
+ for (i = 0; i < k7pnow_nstates; i++, s++) {
+ k7pnow_freq_table[i].frequency = cpufreq(s->fid);
+ k7pnow_freq_table[i].state = s;
+
+ /* XXX len += snprintf is an illegal idiom */
+ len += snprintf(freq_names + len, freq_names_len - len, "%d%s",
+ k7pnow_freq_table[i].frequency,
+ i < k7pnow_nstates - 1 ? " " : "");
+ }
+
+ /* On bootup the frequency should be at it's max */
+ k7pnow_cur_freq = k7pnow_freq_table[i-1].frequency;
+
+ printf("cpu0: AMD POWERNOW Available frequencies (Mhz): %s\n",
+ freq_names);
+ cpu_setperf = k7_powernow_setperf;
+}
diff --git a/sys/arch/i386/include/cpu.h b/sys/arch/i386/include/cpu.h
index f30153e7487..1c1b6d7c523 100644
--- a/sys/arch/i386/include/cpu.h
+++ b/sys/arch/i386/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.65 2004/06/25 08:41:19 art Exp $ */
+/* $OpenBSD: cpu.h,v 1.66 2004/07/14 05:34:14 tedu Exp $ */
/* $NetBSD: cpu.h,v 1.35 1996/05/05 19:29:26 christos Exp $ */
/*-
@@ -371,6 +371,8 @@ int p4tcc_setperf(int);
void k6_powernow_init(void);
int k6_powernow_setperf(int);
+void k7_powernow_init(uint32_t);
+int k7_powernow_setperf(int);
/* npx.c */