Hi there,

This will call the ACPI _PDC method, normally this ins't implemented on
systems with amd processors but the acpi spec says that we should try
and evaulate it anyway. In addition to the diff that was backed out
prior to the 4.5 release it will also attempt to evaluate the OSC method
on each acpicpu instance.

If you have a machine with an intel cpu of reasonable vintage you will
see more states after you apply this.

Index: sys/dev/acpi/acpicpu.c
===================================================================
RCS file: /cvs/src/sys/dev/acpi/acpicpu.c,v
retrieving revision 1.53
diff -u -p sys/dev/acpi/acpicpu.c
--- sys/dev/acpi/acpicpu.c      24 Feb 2009 13:20:02 -0000      1.53
+++ sys/dev/acpi/acpicpu.c      7 Jun 2009 04:03:21 -0000
@@ -41,12 +41,29 @@ int acpicpu_match(struct device *, void *, void *);
 void   acpicpu_attach(struct device *, struct device *, void *);
 int    acpicpu_notify(struct aml_node *, int, void *);
 void   acpicpu_setperf(int);
+void   acpicpu_setperf_ppc_change(struct acpicpu_pss *, int);
 
 #define ACPI_STATE_C0          0x00
 #define ACPI_STATE_C1          0x01
 #define ACPI_STATE_C2          0x02
 #define ACPI_STATE_C3          0x03
 
+#define ACPI_PDC_REVID         0x1
+#define ACPI_PDC_SMP           0xa
+#define ACPI_PDC_MSR           0x1
+
+/* _PDC Intel capabilities flags from linux */
+#define ACPI_PDC_P_FFH         0x0001
+#define ACPI_PDC_C_C1_HALT     0x0002
+#define ACPI_PDC_T_FFH         0x0004
+#define ACPI_PDC_SMP_C1PT      0x0008
+#define ACPI_PDC_SMP_C2C3      0x0010
+#define ACPI_PDC_SMP_P_SWCOORD 0x0020
+#define ACPI_PDC_SMP_C_SWCOORD 0x0040
+#define ACPI_PDC_SMP_T_SWCOORD 0x0080
+#define ACPI_PDC_C_C1_FFH      0x0100
+#define ACPI_PDC_C_C2C3_FFH    0x0200
+
 #define FLAGS_NO_C2            0x01
 #define FLAGS_NO_C3            0x02
 #define FLAGS_BMCHECK          0x04
@@ -121,6 +138,8 @@ int acpicpu_getpct(struct acpicpu_softc *);
 int    acpicpu_getpss(struct acpicpu_softc *);
 struct acpi_cstate *acpicpu_add_cstate(struct acpicpu_softc *, int, int, int,
     int);
+void   acpicpu_set_pdc(struct acpicpu_softc *);
+
 #if 0
 void    acpicpu_set_throttle(struct acpicpu_softc *, int);
 struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int);
@@ -170,6 +189,55 @@ acpicpu_find_cstate(struct acpicpu_softc *sc, int type
 }
 #endif
 
+
+void
+acpicpu_set_pdc(struct acpicpu_softc *sc)
+{
+       struct aml_value cmd, osc_cmd[4];
+       struct aml_value res;
+       uint32_t buf[3];
+
+       /* 4077A616-290C-47BE-9EBD-D87058713953 */
+       static uint8_t cpu_oscuuid[16] = { 0x16, 0xA6, 0x77, 0x40, 0x0C, 0x29,
+                                          0xBE, 0x47, 0x9E, 0xBD, 0xD8, 0x70,
+                                          0x58, 0x71, 0x39, 0x53 };
+       /* Evaluate _PDC */
+       memset(&cmd, 0, sizeof(cmd));
+       cmd.type = AML_OBJTYPE_BUFFER;
+       cmd.v_buffer = (uint8_t *)&buf;
+       cmd.length = sizeof(buf);
+
+       buf[0] = ACPI_PDC_REVID;
+       buf[1] = 1;
+       buf[2] = ACPI_PDC_C_C1_HALT | ACPI_PDC_P_FFH | ACPI_PDC_C_C1_FFH
+           | ACPI_PDC_C_C2C3_FFH | ACPI_PDC_SMP_P_SWCOORD | ACPI_PDC_SMP_C2C3
+           | ACPI_PDC_SMP_C1PT;
+
+       aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PDC", 1, &cmd, &res);
+
+       /* Evalualte _OSC */
+       memset(&osc_cmd, 0, sizeof(cmd) * 4);
+       osc_cmd[0].type = AML_OBJTYPE_BUFFER;
+       osc_cmd[0].v_buffer = (uint8_t *)&cpu_oscuuid;
+       osc_cmd[0].length = sizeof(cpu_oscuuid);
+
+       osc_cmd[1].type = AML_OBJTYPE_INTEGER;
+       osc_cmd[1].v_integer = 1;
+       osc_cmd[1].length = 1;
+
+       osc_cmd[2].type = AML_OBJTYPE_INTEGER;
+       osc_cmd[2].v_integer = 1;
+       osc_cmd[2].length = 1;
+
+       buf[0] = 0;
+       osc_cmd[3].type = AML_OBJTYPE_BUFFER;
+       osc_cmd[3].v_buffer = (int8_t *)&buf;
+       osc_cmd[3].length = sizeof(buf);
+
+       aml_evalname(sc->sc_acpi, sc->sc_devnode, "_OSC", 4, osc_cmd, &res);
+}
+
+
 struct acpi_cstate *
 acpicpu_add_cstate(struct acpicpu_softc *sc, int type, int latency, int power,
     int address)
@@ -268,15 +336,21 @@ acpicpu_attach(struct device *parent, struct device *s
        }
        sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset;
        sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width;
+
+#if defined(amd64)
+       if (strcmp(cpu_vendor, "GenuineIntel") == 0)
+               if (cpu_ecxfeature & CPUIDECX_EST)
+                       acpicpu_set_pdc(sc);
+#endif
+
        if (!valid_throttle(sc->sc_duty_off, sc->sc_duty_wid, sc->sc_pblk_addr))
                sc->sc_flags |= FLAGS_NOTHROTTLE;
 #ifdef ACPI_DEBUG
        printf(": %s: ", sc->sc_devnode->name);
-       printf("\n: hdr:%x pblk:%x,%x duty:%x,%x pstate:%x (%d throttling 
states)\n",
-               sc->sc_acpi->sc_fadt->hdr_revision,
-               sc->sc_pblk_addr, sc->sc_pblk_len,
-               sc->sc_duty_off, sc->sc_duty_wid,
-               sc->sc_acpi->sc_fadt->pstate_cnt,
+       printf("\n: hdr:%x pblk:%x,%x duty:%x,%x pstate:%x "
+              "(%d throttling states)\n", sc->sc_acpi->sc_fadt->hdr_revision,
+               sc->sc_pblk_addr, sc->sc_pblk_len, sc->sc_duty_off,
+               sc->sc_duty_wid, sc->sc_acpi->sc_fadt->pstate_cnt,
                CPU_MAXSTATE(sc));
 #endif
 
@@ -342,6 +416,7 @@ acpicpu_attach(struct device *parent, struct device *s
                            DEVNAME(sc), status, sc->sc_level);
                        if (setperf_prio < 30) {
                                cpu_setperf = acpicpu_setperf;
+                               acpicpu_set_notify(acpicpu_setperf_ppc_change);
                                setperf_prio = 30;
                                acpi_hasprocfvs = 1;
                        }
@@ -492,7 +567,7 @@ int
 acpicpu_getpss(struct acpicpu_softc *sc)
 {
        struct aml_value        res;
-       int                     i;
+       int                     i, c, cf;
 
        if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PSS", 0, NULL, &res)) {
                dprintf("%s: no _PSS\n", DEVNAME(sc));
@@ -505,21 +580,39 @@ acpicpu_getpss(struct acpicpu_softc *sc)
        sc->sc_pss = malloc(res.length * sizeof *sc->sc_pss, M_DEVBUF,
            M_WAITOK | M_ZERO);
 
+       c = 0;
        for (i = 0; i < res.length; i++) {
-               sc->sc_pss[i].pss_core_freq = aml_val2int(
-                   res.v_package[i]->v_package[0]);
-               sc->sc_pss[i].pss_power = aml_val2int(
+               cf = aml_val2int(res.v_package[i]->v_package[0]);
+
+               /* This heuristic comes from FreeBSDs 
+                * dev/acpica/acpi_perf.c to weed out invalid PSS entries.
+                */
+               if (cf == sc->sc_pss[c].pss_core_freq) {
+                       printf("%s: struck PSS entry, core frequency equals "
+                           " last\n", sc->sc_dev.dv_xname);
+                       continue;
+               }
+
+               if (cf == 0xFFFF || cf == 0x9999 || cf == 99999 || cf == 0) {
+                       printf("%s: struck PSS entry, inappropriate core "
+                           "frequency value\n", sc->sc_dev.dv_xname);
+                       continue;
+               }
+
+               sc->sc_pss[c].pss_core_freq = cf;
+               sc->sc_pss[c].pss_power = aml_val2int(
                    res.v_package[i]->v_package[1]);
-               sc->sc_pss[i].pss_trans_latency = aml_val2int(
+               sc->sc_pss[c].pss_trans_latency = aml_val2int(
                    res.v_package[i]->v_package[2]);
-               sc->sc_pss[i].pss_bus_latency = aml_val2int(
+               sc->sc_pss[c].pss_bus_latency = aml_val2int(
                    res.v_package[i]->v_package[3]);
-               sc->sc_pss[i].pss_ctrl = aml_val2int(
+               sc->sc_pss[c].pss_ctrl = aml_val2int(
                    res.v_package[i]->v_package[4]);
-               sc->sc_pss[i].pss_status = aml_val2int(
+               sc->sc_pss[c].pss_status = aml_val2int(
                    res.v_package[i]->v_package[5]);
+               c++;
        }
-       sc->sc_pss_len = res.length;
+       sc->sc_pss_len = c;
 
        aml_freevalue(&res);
 
@@ -535,8 +628,6 @@ acpicpu_fetch_pss(struct acpicpu_pss **pss)
         * XXX: According to the ACPI spec in an SMP system all processors
         * are supposed to support the same states. For now we pray
         * the bios ensures this...
-        * XXX part deux: this needs to account for _PPC as well
-        * when AC is removed the nr of _PSS entries can go down
         */
 
        sc = acpicpu_sc[0];
@@ -562,9 +653,6 @@ acpicpu_notify(struct aml_node *node, int notify_type,
                if (sc->sc_notify)
                        sc->sc_notify(sc->sc_pss, sc->sc_pss_len);
 
-               /* reset performance to current percentage */
-               /* XXX will fail for amd64 for now */
-               cpu_setperf(sc->sc_level);
                break;
        default:
                printf("%s: unhandled cpu event %x\n", DEVNAME(sc),
@@ -576,7 +664,8 @@ acpicpu_notify(struct aml_node *node, int notify_type,
 }
 
 void
-acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int)) {
+acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int))
+{
        struct acpicpu_softc    *sc;
 
        sc = acpicpu_sc[0];
@@ -585,6 +674,17 @@ acpicpu_set_notify(void (*func)(struct acpicpu_pss *, 
 }
 
 void
+acpicpu_setperf_ppc_change(struct acpicpu_pss *pss, int npss)
+{
+       struct acpicpu_softc    *sc;
+
+       sc = acpicpu_sc[0];
+
+       if (sc != NULL)
+               cpu_setperf(sc->sc_level);
+}
+
+void
 acpicpu_setperf(int level)
 {
        struct acpicpu_softc    *sc;
@@ -608,7 +708,8 @@ acpicpu_setperf(int level)
         * the duty cycle method instead of pss exclusively
         */
        if (sc->sc_flags & FLAGS_NOPSS || sc->sc_flags & FLAGS_NOPCT) {
-               dnprintf(10, "%s: acpicpu no _PSS or _PCT\n", 
sc->sc_devnode->name);
+               dnprintf(10, "%s: acpicpu no _PSS or _PCT\n",
+                   sc->sc_devnode->name);
                return;
        }

Reply via email to