Adds support for family 10h/11h AMD processors, not sure if it's the best way to do this.. but it works and noticably lowers the temperatures reported by the die sensors.
# sysctl hw.setperf=0 # echo "apmd_flags=\"-C\"" >> /etc/rc.conf.local Just putting it here again for testing, could also be easily adapted for amd64 running on i386, but I don't do that. "Works for me", tease me about my last name and I'll cut you. -Bryan. --- /dev/null Wed Feb 2 02:26:51 2011 +++ arch/amd64/amd64/k1x-pstate.c Wed Feb 2 02:26:49 2011 @@ -0,0 +1,215 @@ +/* $OpenBSD$ */ +/* + * Copyright (c) 2011 Bryan Steele <bry...@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); +int 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 + +int +k1x_acpi_states(struct k1x_cpu_state *cstate, struct acpicpu_pss *pss, + int nstates, u_int64_t msr) +{ + struct k1x_state state; + int j, k, n; + u_int32_t ctrl; + + k = -1; + + for (n = 0; n < cstate->n_states; n++) { + if ((K1X_FID(msr) == K1X_FID(pss[n].pss_status))) + k = 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)); + } + + return k; +} + +void +k1x_acpi_init(struct k1x_cpu_state *cstate, u_int64_t msr) +{ + int curs; + u_int32_t ctrl; + struct acpicpu_pss *pss; + + cstate->n_states = acpicpu_fetch_pss(&pss); + if (cstate->n_states == 0) + return; + + curs = k1x_acpi_states(cstate, pss, cstate->n_states, msr); + ctrl = pss[curs].pss_ctrl; + + 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); +} Index: arch/amd64/amd64/identcpu.c =================================================================== RCS file: /cvs/src/sys/arch/amd64/amd64/identcpu.c,v retrieving revision 1.30 diff -u -p -r1.30 identcpu.c --- arch/amd64/amd64/identcpu.c 7 Sep 2010 16:22:48 -0000 1.30 +++ arch/amd64/amd64/identcpu.c 31 Jan 2011 05:05:48 -0000 @@ -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) { Index: arch/amd64/conf/files.amd64 =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/files.amd64,v retrieving revision 1.60 diff -u -p -r1.60 files.amd64 --- arch/amd64/conf/files.amd64 6 Dec 2010 20:10:17 -0000 1.60 +++ arch/amd64/conf/files.amd64 31 Jan 2011 05:05:48 -0000 @@ -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" Index: arch/amd64/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/amd64/include/cpu.h,v retrieving revision 1.62 diff -u -p -r1.62 cpu.h --- arch/amd64/include/cpu.h 29 Nov 2010 00:04:09 -0000 1.62 +++ arch/amd64/include/cpu.h 31 Jan 2011 05:05:48 -0000 @@ -317,6 +317,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);