diff options
author | Claudio Jeker <claudio@cvs.openbsd.org> | 2011-05-23 09:52:25 +0000 |
---|---|---|
committer | Claudio Jeker <claudio@cvs.openbsd.org> | 2011-05-23 09:52:25 +0000 |
commit | 1c62370d57329d26ffe2c6fb203f87c82bbe7214 (patch) | |
tree | 4fcf0ed7b3850db4714b1972332ce49effb3f322 | |
parent | 0c984989c891dc664204ca20f59257fd61f709ad (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.c | 4 | ||||
-rw-r--r-- | sys/arch/amd64/amd64/k1x-pstate.c | 207 | ||||
-rw-r--r-- | sys/arch/amd64/conf/files.amd64 | 3 | ||||
-rw-r--r-- | sys/arch/amd64/include/cpu.h | 6 |
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); |