Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys Merge P-state support for acpicpu(4).



details:   https://anonhg.NetBSD.org/src/rev/2f2b66ba6a0f
branches:  trunk
changeset: 756954:2f2b66ba6a0f
user:      jruoho <jruoho%NetBSD.org@localhost>
date:      Sun Aug 08 16:58:41 2010 +0000

description:
Merge P-state support for acpicpu(4).

Remarks:

  1.    All processors (x86 or not) for which the vendor has implemented
        ACPI I/O access routines are supported. Native instructions are
        currently supported only for Intel's "Enhanced Speedstep". Code for
        "PowerNow!" (AMD) will be merged later. Native support for VIA's
        "PowerSaver" will be investigated.

  2.    Backwards compatibility with existing userland code is maintained.
        Comparable to the case with cpu_idle(9), the ACPI CPU driver
        installs alternative functions for the existing sysctl(8) controls.
        The "native" behavior (if any) is restored upon detachment.

  3.    The dynamic nature of ACPI-provided P-states needs more investigation.
        The maximum frequency induced (but not forced) by the firmware may
        change dynamically. Currently, the sysctl(8) controls error out with
        a value larger than the dynamic maximum. The code itself does not
        however yet react to the notifications from the firmware by changing
        the frequencies in-place. Presumably the system administrator should
        be able to choose whether to use dynamic or static frequencies.

diffstat:

 sys/arch/x86/acpi/acpi_cpu_md.c |  271 +++++++++++++++-
 sys/arch/x86/include/cpuvar.h   |    8 +-
 sys/arch/x86/x86/est.c          |   74 ++++-
 sys/dev/acpi/TODO               |    4 -
 sys/dev/acpi/acpi_cpu.c         |   31 +-
 sys/dev/acpi/acpi_cpu.h         |   60 ++-
 sys/dev/acpi/acpi_cpu_cstate.c  |  116 +------
 sys/dev/acpi/acpi_cpu_pstate.c  |  659 ++++++++++++++++++++++++++++++++++++++++
 sys/modules/acpicpu/Makefile    |    4 +-
 9 files changed, 1071 insertions(+), 156 deletions(-)

diffs (truncated from 1610 to 300 lines):

diff -r e0c237b45e96 -r 2f2b66ba6a0f sys/arch/x86/acpi/acpi_cpu_md.c
--- a/sys/arch/x86/acpi/acpi_cpu_md.c   Sun Aug 08 16:51:34 2010 +0000
+++ b/sys/arch/x86/acpi/acpi_cpu_md.c   Sun Aug 08 16:58:41 2010 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: acpi_cpu_md.c,v 1.4 2010/08/04 16:16:55 jruoho Exp $ */
+/* $NetBSD: acpi_cpu_md.c,v 1.5 2010/08/08 16:58:41 jruoho Exp $ */
 
 /*-
  * Copyright (c) 2010 Jukka Ruohonen <jruohonen%iki.fi@localhost>
@@ -27,25 +27,33 @@
  * SUCH DAMAGE.
  */
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.4 2010/08/04 16:16:55 jruoho Exp $");
+__KERNEL_RCSID(0, "$NetBSD: acpi_cpu_md.c,v 1.5 2010/08/08 16:58:41 jruoho Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/kcore.h>
+#include <sys/sysctl.h>
 #include <sys/xcall.h>
 
 #include <x86/cpu.h>
+#include <x86/cpufunc.h>
+#include <x86/cputypes.h>
 #include <x86/cpuvar.h>
+#include <x86/cpu_msr.h>
 #include <x86/machdep.h>
-#include <x86/cputypes.h>
 
 #include <dev/acpi/acpica.h>
 #include <dev/acpi/acpi_cpu.h>
 
-static char    native_idle_text[16];
-void         (*native_idle)(void) = NULL;
+static char      native_idle_text[16];
+void           (*native_idle)(void) = NULL;
 
-extern uint32_t        cpus_running;
+static int      acpicpu_md_pstate_sysctl_get(SYSCTLFN_PROTO);
+static int      acpicpu_md_pstate_sysctl_set(SYSCTLFN_PROTO);
+static int      acpicpu_md_pstate_sysctl_all(SYSCTLFN_PROTO);
+
+extern uint32_t cpus_running;
+extern struct acpicpu_softc **acpicpu_sc;
 
 uint32_t
 acpicpu_md_cap(void)
@@ -68,6 +76,12 @@
         if ((ci->ci_feat_val[1] & CPUID2_MONITOR) != 0)
                val |= ACPICPU_PDC_C_C1_FFH | ACPICPU_PDC_C_C2C3_FFH;
 
+       /*
+        * Set native P-states if EST is available.
+        */
+        if ((ci->ci_feat_val[1] & CPUID2_EST) != 0)
+               val |= ACPICPU_PDC_P_FFH;
+
        return val;
 }
 
@@ -81,13 +95,17 @@
                val |= ACPICPU_FLAG_C_BM;
 
        if ((ci->ci_feat_val[1] & CPUID2_MONITOR) != 0)
-               val |= ACPICPU_FLAG_C_MWAIT;
+               val |= ACPICPU_FLAG_C_FFH;
 
        switch (cpu_vendor) {
 
        case CPUVENDOR_INTEL:
+
                val |= ACPICPU_FLAG_C_BM | ACPICPU_FLAG_C_ARB;
 
+               if ((ci->ci_feat_val[1] & CPUID2_EST) != 0)
+                       val |= ACPICPU_FLAG_P_FFH;
+
                /*
                 * Bus master arbitration is not
                 * needed on some recent Intel CPUs.
@@ -124,8 +142,6 @@
 {
        const size_t size = sizeof(native_idle_text);
 
-       KASSERT(native_idle == NULL);
-
        x86_disable_intr();
        x86_cpu_idle_get(&native_idle, native_idle_text, size);
        x86_enable_intr();
@@ -137,9 +153,6 @@
 acpicpu_md_idle_start(void)
 {
 
-       KASSERT(native_idle != NULL);
-       KASSERT(native_idle_text[0] != '\0');
-
        x86_disable_intr();
        x86_cpu_idle_set(acpicpu_cstate_idle, "acpi");
        x86_enable_intr();
@@ -152,9 +165,6 @@
 {
        uint64_t xc;
 
-       KASSERT(native_idle != NULL);
-       KASSERT(native_idle_text[0] != '\0');
-
        x86_disable_intr();
        x86_cpu_idle_set(native_idle, native_idle_text);
        x86_enable_intr();
@@ -201,3 +211,234 @@
                break;
        }
 }
+
+int
+acpicpu_md_pstate_start(void)
+{
+
+       switch (cpu_vendor) {
+
+       case CPUVENDOR_INTEL:
+               est_sysctl_get = acpicpu_md_pstate_sysctl_get;
+               est_sysctl_set = acpicpu_md_pstate_sysctl_set;
+               est_sysctl_all = acpicpu_md_pstate_sysctl_all;
+               break;
+
+       default:
+               return ENODEV;
+       }
+
+       return 0;
+}
+
+int
+acpicpu_md_pstate_stop(void)
+{
+
+       switch (cpu_vendor) {
+
+       case CPUVENDOR_INTEL:
+               est_sysctl_get = NULL;
+               est_sysctl_set = NULL;
+               est_sysctl_all = NULL;
+               break;
+
+       default:
+               return ENODEV;
+       }
+
+       return 0;
+}
+
+static int
+acpicpu_md_pstate_sysctl_get(SYSCTLFN_ARGS)
+{
+       struct cpu_info *ci = curcpu();
+       struct acpicpu_softc *sc;
+       struct sysctlnode node;
+       uint32_t freq;
+       int err;
+
+       /*
+        * We can use any ACPI CPU to manipulate the
+        * frequencies. In MP environments all CPUs
+        * are mandated to support the same number of
+        * P-states and each state must have identical
+        * parameters across CPUs.
+        */
+       sc = acpicpu_sc[ci->ci_acpiid];
+
+       if (sc == NULL)
+               return ENXIO;
+
+       err = acpicpu_pstate_get(sc, &freq);
+
+       if (err != 0)
+               return err;
+
+       node = *rnode;
+       node.sysctl_data = &freq;
+
+       err = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+       if (err != 0 || newp == NULL)
+               return err;
+
+       return 0;
+}
+
+static int
+acpicpu_md_pstate_sysctl_set(SYSCTLFN_ARGS)
+{
+       struct cpu_info *ci = curcpu();
+       struct acpicpu_softc *sc;
+       struct sysctlnode node;
+       uint32_t freq;
+       int err;
+
+       sc = acpicpu_sc[ci->ci_acpiid];
+
+       if (sc == NULL)
+               return ENXIO;
+
+       err = acpicpu_pstate_get(sc, &freq);
+
+       if (err != 0)
+               return err;
+
+       node = *rnode;
+       node.sysctl_data = &freq;
+
+       err = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+       if (err != 0 || newp == NULL)
+               return err;
+
+       err = acpicpu_pstate_set(sc, freq);
+
+       if (err != 0)
+               return err;
+
+       return 0;
+}
+
+static int
+acpicpu_md_pstate_sysctl_all(SYSCTLFN_ARGS)
+{
+       struct cpu_info *ci = curcpu();
+       struct acpicpu_softc *sc;
+       struct sysctlnode node;
+       char buf[1024];
+       size_t len;
+       uint32_t i;
+       int err;
+
+       sc = acpicpu_sc[ci->ci_acpiid];
+
+       if (sc == NULL)
+               return ENXIO;
+
+       (void)memset(&buf, 0, sizeof(buf));
+
+       mutex_enter(&sc->sc_mtx);
+
+       for (len = 0, i = sc->sc_pstate_max; i < sc->sc_pstate_count; i++) {
+
+               if (sc->sc_pstate[i].ps_freq == 0)
+                       continue;
+
+               len += snprintf(buf + len, sizeof(buf) - len, "%u%s",
+                   sc->sc_pstate[i].ps_freq,
+                   i < (sc->sc_pstate_count - 1) ? " " : "");
+       }
+
+       mutex_exit(&sc->sc_mtx);
+
+       node = *rnode;
+       node.sysctl_data = buf;
+
+       err = sysctl_lookup(SYSCTLFN_CALL(&node));
+
+       if (err != 0 || newp == NULL)
+               return err;
+
+       return 0;
+}
+
+int
+acpicpu_md_pstate_get(struct acpicpu_softc *sc, uint32_t *freq)
+{
+       struct acpicpu_pstate *ps;
+       uint64_t val;
+       uint32_t i;
+
+       switch (cpu_vendor) {
+
+       case CPUVENDOR_INTEL:
+
+               val = rdmsr(MSR_PERF_STATUS);
+               val = val & 0xffff;
+
+               mutex_enter(&sc->sc_mtx);
+
+               for (i = sc->sc_pstate_max; i < sc->sc_pstate_count; i++) {
+
+                       ps = &sc->sc_pstate[i];
+
+                       if (ps->ps_freq == 0)
+                               continue;
+
+                       if (val == ps->ps_status) {
+                               mutex_exit(&sc->sc_mtx);
+                               *freq = ps->ps_freq;
+                               return 0;
+                       }
+               }
+
+               mutex_exit(&sc->sc_mtx);
+
+               return EIO;



Home | Main Index | Thread Index | Old Index