summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorClaudio Jeker <claudio@cvs.openbsd.org>2011-05-23 09:52:25 +0000
committerClaudio Jeker <claudio@cvs.openbsd.org>2011-05-23 09:52:25 +0000
commit1c62370d57329d26ffe2c6fb203f87c82bbe7214 (patch)
tree4fcf0ed7b3850db4714b1972332ce49effb3f322
parent0c984989c891dc664204ca20f59257fd61f709ad (diff)
AMD K10/K11 pstate driver allows setperf and apm to change CPU
frequencies on newer AMD systems. Driver written by Bryan Steele / brynet gmail.com Put it in deraadt@
-rw-r--r--sys/arch/amd64/amd64/identcpu.c4
-rw-r--r--sys/arch/amd64/amd64/k1x-pstate.c207
-rw-r--r--sys/arch/amd64/conf/files.amd643
-rw-r--r--sys/arch/amd64/include/cpu.h6
4 files changed, 217 insertions, 3 deletions
diff --git a/sys/arch/amd64/amd64/identcpu.c b/sys/arch/amd64/amd64/identcpu.c
index 8abd2eae8c4..f2d71811169 100644
--- a/sys/arch/amd64/amd64/identcpu.c
+++ b/sys/arch/amd64/amd64/identcpu.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: identcpu.c,v 1.30 2010/09/07 16:22:48 mikeb Exp $ */
+/* $OpenBSD: identcpu.c,v 1.31 2011/05/23 09:52:24 claudio Exp $ */
/* $NetBSD: identcpu.c,v 1.1 2003/04/26 18:39:28 fvdl Exp $ */
/*
@@ -364,6 +364,8 @@ identifycpu(struct cpu_info *ci)
if ((ci->ci_signature & 0xF00) == 0xf00)
setperf_setup = k8_powernow_init;
}
+ if (ci->ci_family == 0x10 || ci->ci_family == 0x11)
+ setperf_setup = k1x_init;
}
if (cpu_ecxfeature & CPUIDECX_EST) {
diff --git a/sys/arch/amd64/amd64/k1x-pstate.c b/sys/arch/amd64/amd64/k1x-pstate.c
new file mode 100644
index 00000000000..9467bfaa1bf
--- /dev/null
+++ b/sys/arch/amd64/amd64/k1x-pstate.c
@@ -0,0 +1,207 @@
+/* $OpenBSD: k1x-pstate.c,v 1.1 2011/05/23 09:52:24 claudio Exp $ */
+/*
+ * Copyright (c) 2011 Bryan Steele <brynet@gmail.com>
+ *
+ * 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.
+ */
+/* AMD K10/K11 pstate driver */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/sysctl.h>
+
+#include <machine/cpu.h>
+#include <machine/cpufunc.h>
+#include <machine/bus.h>
+
+#include "acpicpu.h"
+
+#if NACPICPU > 0
+#include <dev/acpi/acpidev.h>
+#include <dev/acpi/acpivar.h>
+#endif
+
+extern int setperf_prio;
+
+#define MSR_K1X_LIMIT 0xc0010061
+#define MSR_K1X_CONTROL 0xc0010062
+#define MSR_K1X_STATUS 0xc0010063
+#define MSR_K1X_CONFIG 0xc0010064
+
+/* MSR_K1X_LIMIT */
+#define K1X_PSTATE_MAX_VAL(x) (((x) >> 4) & 0x7)
+#define K1X_PSTATE_LIMIT(x) (((x)) & 0x7)
+
+/* MSR_K1X_CONFIG */
+#define K1X_FID(x) ((x) & 0x3f)
+#define K1X_DID(x) (((x) >> 6) & 0x07)
+
+/* Maximum pstates */
+#define K1X_MAX_STATES 16
+
+struct k1x_state {
+ int freq;
+ u_int8_t fid;
+};
+
+struct k1x_cpu_state {
+ struct k1x_state state_table[K1X_MAX_STATES];
+ u_int n_states;
+};
+
+struct k1x_cpu_state *k1x_current_state;
+
+void k1x_transition(struct k1x_cpu_state *, int);
+
+#if NACPICPU > 0
+void k1x_acpi_init(struct k1x_cpu_state *, u_int64_t);
+void k1x_acpi_states(struct k1x_cpu_state *, struct acpicpu_pss *, int,
+ u_int64_t);
+#endif
+
+void
+k1x_setperf(int level)
+{
+ u_int i = 0;
+ struct k1x_cpu_state *cstate;
+
+ cstate = k1x_current_state;
+
+ i = ((level * cstate->n_states) + 1) / 101;
+ if (i >= cstate->n_states)
+ i = cstate->n_states - 1;
+
+ k1x_transition(cstate, i);
+}
+
+void
+k1x_transition(struct k1x_cpu_state *cstate, int level)
+{
+ u_int64_t msr;
+ int i, cfid, fid = cstate->state_table[level].fid;
+
+ msr = rdmsr(MSR_K1X_STATUS);
+ cfid = K1X_FID(msr);
+
+ if (fid == cfid)
+ return;
+
+ if (cfid != fid) {
+ wrmsr(MSR_K1X_CONTROL, fid);
+ for (i = 0; i < 100; i++) {
+ msr = rdmsr(MSR_K1X_STATUS);
+ if (msr == fid)
+ break;
+ DELAY(100);
+ }
+ cfid = K1X_FID(msr);
+ }
+ if (cfid == fid) {
+ cpuspeed = cstate->state_table[level].freq;
+#if 0
+ (void)printf("Target: %d Current: %d Pstate: %d\n",
+ cstate->state_table[level].freq,
+ cpuspeed, cfid);
+#endif
+ }
+}
+
+#if NACPICPU > 0
+
+void
+k1x_acpi_states(struct k1x_cpu_state *cstate, struct acpicpu_pss *pss,
+ int nstates, u_int64_t msr)
+{
+ struct k1x_state state;
+ int j, n;
+ u_int32_t ctrl;
+
+ for (n = 0; n < cstate->n_states; n++) {
+ ctrl = pss[n].pss_ctrl;
+ state.fid = K1X_FID(ctrl);
+ state.freq = pss[n].pss_core_freq;
+ j = n;
+ while (j > 0 && cstate->state_table[j - 1].freq > state.freq) {
+ memcpy(&cstate->state_table[j],
+ &cstate->state_table[j - 1],
+ sizeof(struct k1x_state));
+ --j;
+ }
+ memcpy(&cstate->state_table[j], &state,
+ sizeof(struct k1x_state));
+ }
+}
+
+void
+k1x_acpi_init(struct k1x_cpu_state *cstate, u_int64_t msr)
+{
+ struct acpicpu_pss *pss;
+
+ cstate->n_states = acpicpu_fetch_pss(&pss);
+ if (cstate->n_states == 0)
+ return;
+
+ k1x_acpi_states(cstate, pss, cstate->n_states, msr);
+
+ return;
+}
+
+#endif /* NACPICPU */
+
+void
+k1x_init(struct cpu_info *ci)
+{
+ u_int64_t msr;
+ u_int i;
+ struct k1x_cpu_state *cstate;
+ struct k1x_state *state;
+
+ if (setperf_prio > 1)
+ return;
+
+ cstate = malloc(sizeof(struct k1x_cpu_state), M_DEVBUF, M_NOWAIT);
+ if (!cstate)
+ return;
+
+ cstate->n_states = 0;
+
+#if NACPICPU > 0
+ msr = rdmsr(MSR_K1X_STATUS);
+ k1x_acpi_init(cstate, msr);
+#endif
+ if (cstate->n_states) {
+ printf("%s: %d MHz: speeds:",
+ ci->ci_dev->dv_xname, cpuspeed);
+ for (i = cstate->n_states; i > 0; i--) {
+ state = &cstate->state_table[i-1];
+ printf(" %d", state->freq);
+ }
+ printf(" MHz\n");
+ k1x_current_state = cstate;
+ cpu_setperf = k1x_setperf;
+ setperf_prio = 1;
+ return;
+ }
+ free(cstate, M_DEVBUF);
+}
diff --git a/sys/arch/amd64/conf/files.amd64 b/sys/arch/amd64/conf/files.amd64
index dbf3c01c4cf..86cc33959ca 100644
--- a/sys/arch/amd64/conf/files.amd64
+++ b/sys/arch/amd64/conf/files.amd64
@@ -1,4 +1,4 @@
-# $OpenBSD: files.amd64,v 1.61 2011/04/02 18:16:50 oga Exp $
+# $OpenBSD: files.amd64,v 1.62 2011/05/23 09:52:24 claudio Exp $
maxpartitions 16
maxusers 2 16 128
@@ -64,6 +64,7 @@ file arch/amd64/isa/clock.c
file arch/amd64/amd64/powernow-k8.c !small_kernel
file arch/amd64/amd64/est.c !small_kernel
+file arch/amd64/amd64/k1x-pstate.c !small_kernel
include "dev/rasops/files.rasops"
include "dev/wsfont/files.wsfont"
diff --git a/sys/arch/amd64/include/cpu.h b/sys/arch/amd64/include/cpu.h
index 7810242cf17..a6037b06b37 100644
--- a/sys/arch/amd64/include/cpu.h
+++ b/sys/arch/amd64/include/cpu.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cpu.h,v 1.66 2011/04/13 02:49:12 guenther Exp $ */
+/* $OpenBSD: cpu.h,v 1.67 2011/05/23 09:52:24 claudio Exp $ */
/* $NetBSD: cpu.h,v 1.1 2003/04/26 18:39:39 fvdl Exp $ */
/*-
@@ -318,6 +318,10 @@ void x86_bus_space_mallocok(void);
void k8_powernow_init(struct cpu_info *);
void k8_powernow_setperf(int);
+/* k1x-pstate.c */
+void k1x_init(struct cpu_info *);
+void k1x_setperf(int);
+
void est_init(struct cpu_info *);
void est_setperf(int);