diff options
-rw-r--r-- | sys/dev/acpi/acpicpu.c | 141 |
1 files changed, 119 insertions, 22 deletions
diff --git a/sys/dev/acpi/acpicpu.c b/sys/dev/acpi/acpicpu.c index c10bbf7da3a..40983ef82ae 100644 --- a/sys/dev/acpi/acpicpu.c +++ b/sys/dev/acpi/acpicpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpicpu.c,v 1.53 2009/02/24 13:20:02 deraadt Exp $ */ +/* $OpenBSD: acpicpu.c,v 1.54 2009/06/10 03:42:20 gwk Exp $ */ /* * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> * @@ -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,17 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) } sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset; sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width; + + acpicpu_set_pdc(sc); + 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 +412,7 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) 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 +563,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 +576,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 +624,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 +649,6 @@ acpicpu_notify(struct aml_node *node, int notify_type, void *arg) 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 +660,8 @@ acpicpu_notify(struct aml_node *node, int notify_type, void *arg) } 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 +670,17 @@ acpicpu_set_notify(void (*func)(struct acpicpu_pss *, int)) { } 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 +704,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; } |