diff options
author | Jordan Hargrave <jordan@cvs.openbsd.org> | 2007-04-11 02:51:12 +0000 |
---|---|---|
committer | Jordan Hargrave <jordan@cvs.openbsd.org> | 2007-04-11 02:51:12 +0000 |
commit | c7b668727b36b3b3c2736807e98c67a109c6502b (patch) | |
tree | a1751cd62b120e5a8d974d987ecea90a35047626 /sys/dev/acpi/acpicpu.c | |
parent | 79334dc9d3d29b94d14061f326d00878afca233d (diff) |
Added changes for C-State
ok marco@
Diffstat (limited to 'sys/dev/acpi/acpicpu.c')
-rw-r--r-- | sys/dev/acpi/acpicpu.c | 208 |
1 files changed, 195 insertions, 13 deletions
diff --git a/sys/dev/acpi/acpicpu.c b/sys/dev/acpi/acpicpu.c index ab668065df8..8e1fb7694ac 100644 --- a/sys/dev/acpi/acpicpu.c +++ b/sys/dev/acpi/acpicpu.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpicpu.c,v 1.19 2007/01/31 23:30:51 gwk Exp $ */ +/* $OpenBSD: acpicpu.c,v 1.20 2007/04/11 02:51:11 jordan Exp $ */ /* * Copyright (c) 2005 Marco Peereboom <marco@openbsd.org> * @@ -22,6 +22,7 @@ #include <sys/systm.h> #include <sys/device.h> #include <sys/malloc.h> +#include <sys/queue.h> #include <machine/bus.h> #include <machine/cpu.h> @@ -41,8 +42,49 @@ void acpicpu_attach(struct device *, struct device *, void *); int acpicpu_notify(struct aml_node *, int, void *); void acpicpu_setperf(int); +#define ACPI_STATE_C0 0x00 +#define ACPI_STATE_C1 0x01 +#define ACPI_STATE_C2 0x02 +#define ACPI_STATE_C3 0x03 + +#define FLAGS_NO_C2 0x01 +#define FLAGS_NO_C3 0x02 +#define FLAGS_BMCHECK 0x04 +#define FLAGS_NOTHROTTLE 0x08 +#define FLAGS_NOPSS 0x10 + +#define CPU_THT_EN (1L << 4) +#define CPU_MAXSTATE(sc) (1L << (sc)->sc_duty_wid) +#define CPU_STATE(sc,pct) ((pct * CPU_MAXSTATE(sc) / 100) << (sc)->sc_duty_off) +#define CPU_STATEMASK(sc) ((CPU_MAXSTATE(sc) - 1) << (sc)->sc_duty_off) + +#define ACPI_MAX_C2_LATENCY 100 +#define ACPI_MAX_C3_LATENCY 1000 + +/* Make sure throttling bits are valid,a=addr,o=offset,w=width */ +#define valid_throttle(o,w,a) (a && w && (o+w)<=31 && (o>4 || (o+w)<=4)) + +struct acpi_cstate +{ + int type; + int latency; + int power; + int address; + + SLIST_ENTRY(acpi_cstate) link; +}; + struct acpicpu_softc { struct device sc_dev; + int sc_cpu; + + int sc_duty_wid; + int sc_duty_off; + int sc_pblk_addr; + int sc_pblk_len; + int sc_flags; + + SLIST_HEAD(,acpi_cstate) sc_cstates; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; @@ -56,8 +98,12 @@ struct acpicpu_softc { struct acpicpu_pct sc_pct; }; +void acpicpu_set_throttle(struct acpicpu_softc *, int); +void acpicpu_add_cstatepkg(struct aml_value *, void *); 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); +struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int); struct cfattach acpicpu_ca = { sizeof(struct acpicpu_softc), acpicpu_match, acpicpu_attach @@ -75,6 +121,90 @@ struct acpicpu_softc *acpicpu_sc[I386_MAXPROCS]; struct acpicpu_softc *acpicpu_sc[X86_MAXPROCS]; #endif +void +acpicpu_set_throttle(struct acpicpu_softc *sc, int level) +{ + uint32_t pbval; + + if (sc->sc_flags & FLAGS_NOTHROTTLE) + return; + + /* Disable throttling control */ + pbval = inl(sc->sc_pblk_addr); + outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN); + if (level < 100) { + pbval &= ~CPU_STATEMASK(sc); + pbval |= CPU_STATE(sc, level); + outl(sc->sc_pblk_addr, pbval & ~CPU_THT_EN); + outl(sc->sc_pblk_addr, pbval | CPU_THT_EN); + } +} + +struct acpi_cstate * +acpicpu_find_cstate(struct acpicpu_softc *sc, int type) +{ + struct acpi_cstate *cx; + + SLIST_FOREACH(cx, &sc->sc_cstates, link) + if (cx->type == type) + return cx; + return NULL; +} + +struct acpi_cstate * +acpicpu_add_cstate(struct acpicpu_softc *sc, int type, + int latency, int power, int address) +{ + struct acpi_cstate *cx; + + dnprintf(10," C%d: latency:.%4x power:%.4x addr:%.8x\n", + type, latency, power, address); + + switch (type) { + case ACPI_STATE_C2: + if (latency > ACPI_MAX_C2_LATENCY || !address || (sc->sc_flags & FLAGS_NO_C2)) + goto bad; + break; + case ACPI_STATE_C3: + if (latency > ACPI_MAX_C3_LATENCY || !address || (sc->sc_flags & FLAGS_NO_C3)) + goto bad; + break; + } + + cx = malloc(sizeof(struct acpi_cstate), M_DEVBUF, M_WAITOK); + memset(cx, 0, sizeof(struct acpi_cstate)); + + cx->type = type; + cx->power = power; + cx->latency = latency; + cx->address = address; + + SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link); + + return cx; + bad: + printf("acpicpu%d: C%d not supported\n", sc->sc_cpu, type); + return NULL; +} + +/* Found a _CST object, add new cstate for each entry */ +void +acpicpu_add_cstatepkg(struct aml_value *val, void *arg) +{ + struct acpicpu_softc *sc = arg; + +#ifdef ACPI_DEBUG + aml_showvalue(val, 0); +#endif + if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4) + return; + acpicpu_add_cstate(sc, val->v_package[1]->v_integer, + val->v_package[2]->v_integer, + val->v_package[3]->v_integer, + -1); +} + + int acpicpu_match(struct device *parent, void *match, void *aux) { @@ -95,18 +225,60 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) { struct acpicpu_softc *sc = (struct acpicpu_softc *)self; struct acpi_attach_args *aa = aux; + struct aml_value res; int i; sc->sc_acpi = (struct acpi_softc *)parent; - sc->sc_devnode = aa->aaa_node->child; + sc->sc_devnode = aa->aaa_node; + + SLIST_INIT(&sc->sc_cstates); sc->sc_pss = NULL; - printf(": %s: ", sc->sc_devnode->parent->name); + printf(": %s: ", sc->sc_devnode->name); + if (aml_evalnode(sc->sc_acpi, sc->sc_devnode, 0, NULL, &res) == 0) { + if (res.type == AML_OBJTYPE_PROCESSOR) { + sc->sc_cpu = res.v_processor.proc_id; + sc->sc_pblk_addr = res.v_processor.proc_addr; + sc->sc_pblk_len = res.v_processor.proc_len; + } + aml_freevalue(&res); + } + sc->sc_duty_off = sc->sc_acpi->sc_fadt->duty_offset; + sc->sc_duty_wid = sc->sc_acpi->sc_fadt->duty_width; + 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, + CPU_MAXSTATE(sc)); +#endif + + /* Get C-States from _CST or FADT */ + if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res) == 0) { + aml_foreachpkg(&res, 1, acpicpu_add_cstatepkg, sc); + aml_freevalue(&res); + } + else { + /* Some systems don't export a full PBLK, reduce functionality */ + if (sc->sc_pblk_len < 5) + sc->sc_flags |= FLAGS_NO_C2; + if (sc->sc_pblk_len < 6) + sc->sc_flags |= FLAGS_NO_C3; + acpicpu_add_cstate(sc, ACPI_STATE_C2, sc->sc_acpi->sc_fadt->p_lvl2_lat, + -1, sc->sc_pblk_addr + 4); + acpicpu_add_cstate(sc, ACPI_STATE_C3, sc->sc_acpi->sc_fadt->p_lvl3_lat, + -1, sc->sc_pblk_addr + 5); + } if (acpicpu_getpss(sc)) { /* XXX not the right test but has to do for now */ - printf("can't attach, no _PSS\n"); - return; + sc->sc_flags |= FLAGS_NOPSS; + goto nopss; } #ifdef ACPI_DEBUG @@ -125,11 +297,16 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) if (acpicpu_getpct(sc)) return; + /* Notify BIOS we are handing p-states */ + if (sc->sc_acpi->sc_fadt->pstate_cnt) + acpi_write_pmreg(sc->sc_acpi, ACPIREG_SMICMD, 0, + sc->sc_acpi->sc_fadt->pstate_cnt); + for (i = 0; i < sc->sc_pss_len; i++) printf("%d%s", sc->sc_pss[i].pss_core_freq, i < sc->sc_pss_len - 1 ? ", " : " MHz\n"); - aml_register_notify(sc->sc_devnode->parent, NULL, + aml_register_notify(sc->sc_devnode, NULL, acpicpu_notify, sc, ACPIDEV_NOPOLL); if (setperf_prio < 30) { @@ -138,6 +315,11 @@ acpicpu_attach(struct device *parent, struct device *self, void *aux) acpi_hasprocfvs = 1; } acpicpu_sc[sc->sc_dev.dv_unit] = sc; + return; + +nopss: + if (sc->sc_flags & FLAGS_NOTHROTTLE) + printf("no performance/throttling supported\n"); } int @@ -162,7 +344,7 @@ acpicpu_getpct(struct acpicpu_softc *sc) if (res.length != 2) { printf("%s: %s: invalid _PCT length\n", DEVNAME(sc), - sc->sc_devnode->parent->name); + sc->sc_devnode->name); return (1); } @@ -252,7 +434,7 @@ acpicpu_notify(struct aml_node *node, int notify_type, void *arg) struct acpicpu_softc *sc = arg; dnprintf(10, "acpicpu_notify: %.2x %s\n", notify_type, - sc->sc_devnode->parent->name); + sc->sc_devnode->name); switch (notify_type) { case 0x80: /* _PPC changed, retrieve new values */ @@ -279,11 +461,11 @@ acpicpu_setperf(int level) { sc = acpicpu_sc[cpu_number()]; dnprintf(10, "%s: acpicpu setperf level %d\n", - sc->sc_devnode->parent->name, level); + sc->sc_devnode->>name, level); if (level < 0 || level > 100) { dnprintf(10, "%s: acpicpu setperf illegal percentage\n", - sc->sc_devnode->parent->name); + sc->sc_devnode->name); return; } @@ -293,12 +475,12 @@ acpicpu_setperf(int level) { if (idx > sc->sc_pss_len) { /* XXX should never happen */ printf("%s: acpicpu setperf index out of range\n", - sc->sc_devnode->parent->name); + sc->sc_devnode->name); return; } dnprintf(10, "%s: acpicpu setperf index %d\n", - sc->sc_devnode->parent->name, idx); + sc->sc_devnode->name, idx); pss = &sc->sc_pss[idx]; @@ -353,5 +535,5 @@ acpicpu_setperf(int level) { cpuspeed = pss->pss_core_freq; else printf("%s: acpicpu setperf failed to alter frequency\n", - sc->sc_devnode->parent->name); + sc->sc_devnode->name); } |