acpi/pstate: Fix the long standing P-State detection problem on Intel CPUs
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 29 Dec 2011 08:33:11 +0000 (16:33 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 29 Dec 2011 08:33:11 +0000 (16:33 +0800)
- Rename the ACPI_CAP to ACPI_PDC according to Intel's document.
  While I'm here, update the document revision.
- Remove unapplied comment about _OSC and _PDC's revision; Intel's
  document states clearly that it should be 1
- Fix the 'Count' argument for _OSC evaluation; the intergers in
  'Capabilities buffer' argument is 2
- Fix the buffer length of _OSC's 'Capabilities buffer'; we only
  pass 2 intergers.
- Perfer _OSC evaluation, fall back to _PDC evaluation only if _OSC
  evaluation fails.
- Add MD cpu features, so MD code could deliver proper settings.
  For AMD CPUs, the old configuration just works (AMD actually has
  no documents about _PDC and _OSC).
  For Intel CPUs w/ EST, it looks like P-State's _PCT will appear
  only when bit0 (P-State MSR), bit5 (P-State software coordination)
  and bit11 (P-State hardware coordination) are turned on.

sys/dev/acpica5/Makefile
sys/dev/acpica5/acpi_cpu.c
sys/dev/acpica5/acpi_cpu.h
sys/dev/acpica5/acpi_cpu_cstate.c
sys/dev/acpica5/acpivar.h
sys/platform/pc32/acpica5/acpi_cpu_machdep.c [new file with mode: 0644]
sys/platform/pc64/acpica5/acpi_cpu_machdep.c [new file with mode: 0644]

index 8b6ce7c..ec9292a 100644 (file)
@@ -98,6 +98,8 @@ SRCS+=                OsdCache.c
 
 # Machine-specific code for P-State
 SRCS+= acpi_pstate_machdep.c
+# Machine-specific code for CPU
+SRCS+= acpi_cpu_machdep.c
 # Machine-specific code such as sleep/wakeup
 SRCS+= acpi_machdep.c acpi_wakecode.h
 SRCS+= acpi_wakeup.c
index 51631ab..093bf1a 100644 (file)
@@ -150,7 +150,7 @@ acpi_cpu_attach(device_t dev)
     struct acpi_cpux_softc *sc = device_get_softc(dev);
     ACPI_HANDLE handle;
     device_t child;
-    int cpu_id;
+    int cpu_id, cpu_features;
     struct acpi_softc *acpi_sc;
 
     handle = acpi_get_handle(dev);
@@ -177,6 +177,63 @@ acpi_cpu_attach(device_t dev)
        return ENOMEM;
     }
 
+    /*
+     * Before calling any CPU methods, collect child driver feature hints
+     * and notify ACPI of them.  We support unified SMP power control
+     * so advertise this ourselves.  Note this is not the same as independent
+     * SMP control where each CPU can have different settings.
+     */
+    cpu_features = ACPI_PDC_MP_C1PXTX | ACPI_PDC_MP_C2C3;
+    cpu_features |= acpi_cpu_md_features();
+
+    /*
+     * CPU capabilities are specified as a buffer of 32-bit integers:
+     * revision, count, and one or more capabilities.
+     */
+    if (cpu_features) {
+       ACPI_OBJECT_LIST arglist;
+       uint32_t cap_set[3];
+       ACPI_OBJECT arg[4];
+       ACPI_STATUS status;
+
+       /* UUID needed by _OSC evaluation */
+       static uint8_t cpu_oscuuid[16] = {
+          0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29, 0xBE, 0x47,
+          0x9E, 0xBD, 0xD8, 0x70, 0x58, 0x71, 0x39, 0x53
+       };
+
+       arglist.Pointer = arg;
+       arglist.Count = 4;
+       arg[0].Type = ACPI_TYPE_BUFFER;
+       arg[0].Buffer.Length = sizeof(cpu_oscuuid);
+       arg[0].Buffer.Pointer = cpu_oscuuid;    /* UUID */
+       arg[1].Type = ACPI_TYPE_INTEGER;
+       arg[1].Integer.Value = 1;               /* revision */
+       arg[2].Type = ACPI_TYPE_INTEGER;
+       arg[2].Integer.Value = 2;               /* # of capabilities integers */
+       arg[3].Type = ACPI_TYPE_BUFFER;
+       arg[3].Buffer.Length = sizeof(cap_set[0]) * 2; /* capabilities buffer */
+       arg[3].Buffer.Pointer = (uint8_t *)cap_set;
+       cap_set[0] = 0;
+       cap_set[1] = cpu_features;
+       status = AcpiEvaluateObject(handle, "_OSC", &arglist, NULL);
+
+       if (!ACPI_SUCCESS(status)) {
+           if (bootverbose)
+               device_printf(dev, "_OSC failed, use _PDC\n");
+
+           arglist.Pointer = arg;
+           arglist.Count = 1;
+           arg[0].Type = ACPI_TYPE_BUFFER;
+           arg[0].Buffer.Length = sizeof(cap_set);
+           arg[0].Buffer.Pointer = (uint8_t *)cap_set;
+           cap_set[0] = 1; /* revision */
+           cap_set[1] = 1; /* # of capabilities integers */
+           cap_set[2] = cpu_features;
+           AcpiEvaluateObject(handle, "_PDC", &arglist, NULL);
+       }
+    }
+
     child = BUS_ADD_CHILD(dev, dev, 0, "cpu_cst", -1);
     if (child == NULL)
        return ENXIO;
index 98ddfe5..356f892 100644 (file)
@@ -43,4 +43,6 @@ struct acpi_cpux_softc {
        struct sysctl_oid       *pcpu_sysctl_tree;      /* hw.acpi.cpuX */
 };
 
+uint32_t       acpi_cpu_md_features(void);
+
 #endif /* !__ACPI_CPU_H__ */
index 209256a..028d77b 100644 (file)
@@ -79,7 +79,6 @@ struct acpi_cpu_softc {
     struct acpi_cx      cpu_cx_states[MAX_CX_STATES];
     int                         cpu_cx_count;  /* Number of valid Cx states. */
     int                         cpu_prev_sleep;/* Last idle sleep duration. */
-    int                         cpu_features;  /* Child driver supported features. */
     /* Runtime state. */
     int                         cpu_non_c3;    /* Index of lowest non-C3 state. */
     u_int               cpu_cx_stats[MAX_CX_STATES];/* Cx usage history. */
@@ -235,20 +234,11 @@ static int
 acpi_cpu_cst_attach(device_t dev)
 {
     ACPI_BUFFER                   buf;
-    ACPI_OBJECT                   arg[4], *obj;
-    ACPI_OBJECT_LIST      arglist;
+    ACPI_OBJECT                   *obj;
     struct mdglobaldata          *md;
     struct acpi_cpu_softc *sc;
     ACPI_STATUS                   status;
-    u_int                 features;
-    int                           cpu_id, drv_count, i;
-    driver_t             **drivers;
-    uint32_t              cap_set[3];
-
-    /* UUID needed by _OSC evaluation */
-    static uint8_t cpu_oscuuid[16] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29,
-                                      0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70,
-                                      0x58, 0x71, 0x39, 0x53 };
+    int                           cpu_id;
 
     ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__);
 
@@ -291,60 +281,6 @@ acpi_cpu_cst_attach(device_t dev)
        AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_cpu_startup, NULL);
     }
 
-    /*
-     * Before calling any CPU methods, collect child driver feature hints
-     * and notify ACPI of them.  We support unified SMP power control
-     * so advertise this ourselves.  Note this is not the same as independent
-     * SMP control where each CPU can have different settings.
-     */
-    sc->cpu_features = ACPI_CAP_SMP_SAME | ACPI_CAP_SMP_SAME_C3;
-    if (devclass_get_drivers(acpi_cpu_cst_devclass,
-                            &drivers, &drv_count) == 0) {
-       for (i = 0; i < drv_count; i++) {
-           if (ACPI_GET_FEATURES(drivers[i], &features) == 0)
-               sc->cpu_features |= features;
-       }
-       kfree(drivers, M_TEMP);
-    }
-
-    /*
-     * CPU capabilities are specified as a buffer of 32-bit integers:
-     * revision, count, and one or more capabilities.  The revision of
-     * "1" is not specified anywhere but seems to match Linux.
-     */
-    if (sc->cpu_features) {
-       arglist.Pointer = arg;
-       arglist.Count = 1;
-       arg[0].Type = ACPI_TYPE_BUFFER;
-       arg[0].Buffer.Length = sizeof(cap_set);
-       arg[0].Buffer.Pointer = (uint8_t *)cap_set;
-       cap_set[0] = 1; /* revision */
-       cap_set[1] = 1; /* number of capabilities integers */
-       cap_set[2] = sc->cpu_features;
-       AcpiEvaluateObject(sc->cpu_handle, "_PDC", &arglist, NULL);
-
-       /*
-        * On some systems we need to evaluate _OSC so that the ASL
-        * loads the _PSS and/or _PDC methods at runtime.
-        *
-        * TODO: evaluate failure of _OSC.
-        */
-       arglist.Pointer = arg;
-       arglist.Count = 4;
-       arg[0].Type = ACPI_TYPE_BUFFER;
-       arg[0].Buffer.Length = sizeof(cpu_oscuuid);
-       arg[0].Buffer.Pointer = cpu_oscuuid;    /* UUID */
-       arg[1].Type = ACPI_TYPE_INTEGER;
-       arg[1].Integer.Value = 1;               /* revision */
-       arg[2].Type = ACPI_TYPE_INTEGER;
-       arg[2].Integer.Value = 1;               /* count */
-       arg[3].Type = ACPI_TYPE_BUFFER;
-       arg[3].Buffer.Length = sizeof(cap_set); /* Capabilities buffer */
-       arg[3].Buffer.Pointer = (uint8_t *)cap_set;
-       cap_set[0] = 0;
-       AcpiEvaluateObject(sc->cpu_handle, "_OSC", &arglist, NULL);
-    }
-
     /* Probe for Cx state support. */
     acpi_cpu_cx_probe(sc);
 
index 353809a..be98784 100644 (file)
@@ -198,17 +198,19 @@ extern struct lock acpi_lock;
  * Various features and capabilities for the acpi_get_features() method.
  * In particular, these are used for the ACPI 3.0 _PDC and _OSC methods.
  * See the Intel document titled "Processor Driver Capabilities Bit
- * Definitions", number 302223-002.
+ * Definitions", number 302223-005.
  */
-#define ACPI_CAP_PERF_MSRS     (1 << 0) /* Intel SpeedStep PERF_CTL MSRs */
-#define ACPI_CAP_C1_IO_HALT    (1 << 1) /* Intel C1 "IO then halt" sequence */
-#define ACPI_CAP_THR_MSRS      (1 << 2) /* Intel OnDemand throttling MSRs */
-#define ACPI_CAP_SMP_SAME      (1 << 3) /* MP C1, Px, and Tx (all the same) */
-#define ACPI_CAP_SMP_SAME_C3   (1 << 4) /* MP C2 and C3 (all the same) */
-#define ACPI_CAP_SMP_DIFF_PX   (1 << 5) /* MP Px (different, using _PSD) */
-#define ACPI_CAP_SMP_DIFF_CX   (1 << 6) /* MP Cx (different, using _CSD) */
-#define ACPI_CAP_SMP_DIFF_TX   (1 << 7) /* MP Tx (different, using _TSD) */
-#define ACPI_CAP_SMP_C1_NATIVE (1 << 8) /* MP C1 support other than halt */
+#define ACPI_PDC_PX_MSR                (1 << 0) /* Intel SpeedStep PERF_CTL MSRs */
+#define ACPI_PDC_MP_C1_IO_HALT (1 << 1) /* Intel C1 "IO then halt" sequence */
+#define ACPI_PDC_TX_MSR                (1 << 2) /* Intel OnDemand throttling MSRs */
+#define ACPI_PDC_MP_C1PXTX     (1 << 3) /* MP C1, Px, and Tx */
+#define ACPI_PDC_MP_C2C3       (1 << 4) /* MP C2 and C3 */
+#define ACPI_PDC_MP_PX_SWCORD  (1 << 5) /* MP Px, using _PSD */
+#define ACPI_PDC_MP_CX_SWCORD  (1 << 6) /* MP Cx, using _CSD */
+#define ACPI_PDC_MP_TX_SWCORD  (1 << 7) /* MP Tx, using _TSD */
+#define ACPI_PDC_MP_C1_NATIVE  (1 << 8) /* MP C1 support other than halt */
+#define ACPI_PDC_MP_C2C3_NATIVE        (1 << 9) /* MP C2 and C3 support */
+#define ACPI_PDC_PX_HWCORD     (1 << 11)/* Hardware coordination of Px */
 
 /*
  * Quirk flags.
diff --git a/sys/platform/pc32/acpica5/acpi_cpu_machdep.c b/sys/platform/pc32/acpica5/acpi_cpu_machdep.c
new file mode 100644 (file)
index 0000000..4a87be5
--- /dev/null
@@ -0,0 +1,26 @@
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+#include "acpi.h"
+#include "acpivar.h"
+#include "acpi_cpu.h"
+
+uint32_t
+acpi_cpu_md_features(void)
+{
+       if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+               if (cpu_feature2 & CPUID2_EST) {
+                       return (ACPI_PDC_PX_MSR |
+                           ACPI_PDC_MP_PX_SWCORD |
+                           ACPI_PDC_PX_HWCORD);
+               }
+       }
+       return 0;
+}
diff --git a/sys/platform/pc64/acpica5/acpi_cpu_machdep.c b/sys/platform/pc64/acpica5/acpi_cpu_machdep.c
new file mode 100644 (file)
index 0000000..4a87be5
--- /dev/null
@@ -0,0 +1,26 @@
+#include <sys/param.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/sysctl.h>
+
+#include <machine/cputypes.h>
+#include <machine/md_var.h>
+#include <machine/specialreg.h>
+
+#include "acpi.h"
+#include "acpivar.h"
+#include "acpi_cpu.h"
+
+uint32_t
+acpi_cpu_md_features(void)
+{
+       if (cpu_vendor_id == CPU_VENDOR_INTEL) {
+               if (cpu_feature2 & CPUID2_EST) {
+                       return (ACPI_PDC_PX_MSR |
+                           ACPI_PDC_MP_PX_SWCORD |
+                           ACPI_PDC_PX_HWCORD);
+               }
+       }
+       return 0;
+}