> On 10/27/10 00:05, gn...@cvs.openbsd.org wrote:
>> Thank you very much for your problem report.
>> It has the internal identification `kernel/6501'.
>> The individual assigned to look at your
>> report is: bugs. 
>> 
>>> Category:       kernel
>>> Responsible:    bugs
>>> Synopsis:       CPU frequency scaling on AMD K10/Acer Aspire laptop.
>>> Arrival-Date:   Wed Oct 27 04:00:01 GMT 2010
>> 
> 
> Hi again,
> 
> I realize now this was essentially a feature request, but I'm wondering
> if anyone is playing with this at all?
> 
> Alternatively, is anyone aware of some published documentation that
> outlines the differences between AMD K8/K10 PowerNow/Cool'n'Quiet?
> 
> What changes are required to implement support for this on OpenBSD?
> 
> Thanks,
> -Bryan.

Hello, once again,

I finally sat down this weekend and started reading the datasheets
(K10/11 BKDG) and I believe I've got things working for these processors.

It's not perfect, but it works, at least manually using sysctl
hw.setperf and watching km0.temp and visually seeing performance changes
in several apps like openssl/glxgears.

I don't believe I have the delay right after the MSR write, and maybe
the developers would have preferred k8-powernow.c be extended.

If you want to silence the noise, change the #ifdef 1 to 0 in
k1x_transition()..

You can see a list of speeds gathered from ACPI tables using by grepping
for cpu0 in the dmesg.

I've only tested on a dual-core laptop, not tested on systems with 2
physical processors (..with multiple cores each).

Any opinions?

-Bryan.

--- /dev/null   Mon Jan 31 00:06:58 2011
+++ arch/amd64/amd64/k1x-pstate.c       Sun Jan 30 23:53:38 2011
@@ -0,0 +1,254 @@
+/*     $OpenBSD$ */
+/*
+ * Copyright (c) 2011 Brynet <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;
+extern int perflevel;
+
+#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_pss_changed(struct acpicpu_pss *, int);
+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 < 50; i++) {
+                       msr = rdmsr(MSR_K1X_STATUS);
+                       if (msr == fid)
+                               break;
+                       DELAY(50);
+               }
+               cfid = K1X_FID(msr);
+       }
+       if (cfid == fid) {
+               cpuspeed = cstate->state_table[level].freq;
+#if 1
+               (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_pss_changed(struct acpicpu_pss *pss, int npss)
+{
+       int curs, needtran;
+       struct k1x_cpu_state *cstate, *nstate;
+       u_int32_t ctrl;
+       u_int64_t msr;
+
+       msr = rdmsr(MSR_K1X_STATUS);
+       cstate = k1x_current_state;
+
+       nstate = malloc(sizeof(struct k1x_cpu_state), M_DEVBUF, M_NOWAIT);
+       if (!nstate)
+               return;
+
+       curs = k1x_acpi_states(nstate, pss, npss, msr);
+       needtran = 0;
+
+       if (curs < 0) {
+               curs = ((perflevel * npss) + 1) / 101;
+               if (curs >= npss)
+                       curs = npss - 1;
+               needtran = 1;
+       }
+
+       ctrl = pss[curs].pss_ctrl;
+       
+       nstate->n_states = npss;
+
+       if (needtran)
+               k1x_transition(nstate, curs);
+
+       free(cstate, M_DEVBUF);
+       k1x_current_state = nstate;
+}
+
+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;
+       acpicpu_set_notify(k1x_acpi_pss_changed);
+
+       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);

Reply via email to