On Thu, May 19, 2011 at 10:35:55PM +0200, Claudio Jeker wrote: > On Thu, May 19, 2011 at 03:52:02PM -0400, Brynet wrote: > > 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. > > > > Since my new amd64 runs i386 at the moment I ported the code over.
... > Here the updated diff including i386 support. Updated version with some minor cleanups in the k1x-pstate.c code. Anyone willing to OK this? -- :wq Claudio 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 19 May 2011 20:00:02 -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/amd64/k1x-pstate.c =================================================================== RCS file: arch/amd64/amd64/k1x-pstate.c diff -N arch/amd64/amd64/k1x-pstate.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/amd64/amd64/k1x-pstate.c 21 May 2011 10:36:12 -0000 @@ -0,0 +1,207 @@ +/* $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); +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); +} Index: arch/amd64/conf/files.amd64 =================================================================== RCS file: /cvs/src/sys/arch/amd64/conf/files.amd64,v retrieving revision 1.61 diff -u -p -r1.61 files.amd64 --- arch/amd64/conf/files.amd64 2 Apr 2011 18:16:50 -0000 1.61 +++ arch/amd64/conf/files.amd64 19 May 2011 20:00:02 -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.66 diff -u -p -r1.66 cpu.h --- arch/amd64/include/cpu.h 13 Apr 2011 02:49:12 -0000 1.66 +++ arch/amd64/include/cpu.h 19 May 2011 20:00:02 -0000 @@ -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); Index: arch/i386/conf/files.i386 =================================================================== RCS file: /cvs/src/sys/arch/i386/conf/files.i386,v retrieving revision 1.202 diff -u -p -r1.202 files.i386 --- arch/i386/conf/files.i386 30 Apr 2011 15:33:18 -0000 1.202 +++ arch/i386/conf/files.i386 19 May 2011 20:01:57 -0000 @@ -37,6 +37,7 @@ 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/powernow-k8.c !small_kernel +file arch/i386/i386/k1x-pstate.c !small_kernel file arch/i386/i386/process_machdep.c file arch/i386/i386/procfs_machdep.c procfs file arch/i386/i386/sys_machdep.c Index: arch/i386/i386/k1x-pstate.c =================================================================== RCS file: arch/i386/i386/k1x-pstate.c diff -N arch/i386/i386/k1x-pstate.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ arch/i386/i386/k1x-pstate.c 21 May 2011 10:36:55 -0000 @@ -0,0 +1,207 @@ +/* $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); +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); +} Index: arch/i386/i386/machdep.c =================================================================== RCS file: /cvs/src/sys/arch/i386/i386/machdep.c,v retrieving revision 1.494 diff -u -p -r1.494 machdep.c --- arch/i386/i386/machdep.c 30 Apr 2011 15:33:18 -0000 1.494 +++ arch/i386/i386/machdep.c 19 May 2011 20:10:07 -0000 @@ -1345,6 +1345,8 @@ amd_family6_setperf_setup(struct cpu_inf k8_powernow_init(); break; } + if (ci->ci_family == 0x10 || ci->ci_family == 0x11) + k1x_init(ci); } #endif Index: arch/i386/include/cpu.h =================================================================== RCS file: /cvs/src/sys/arch/i386/include/cpu.h,v retrieving revision 1.119 diff -u -p -r1.119 cpu.h --- arch/i386/include/cpu.h 22 Apr 2011 15:48:43 -0000 1.119 +++ arch/i386/include/cpu.h 19 May 2011 20:02:38 -0000 @@ -403,6 +403,9 @@ void k7_powernow_setperf(int); /* powernow-k8.c */ void k8_powernow_init(void); void k8_powernow_setperf(int); +/* k1x-pstate.c */ +void k1x_init(struct cpu_info *); +void k1x_setperf(int); #endif /* npx.c */