Sending out an initial attempt at implementing C-states for APCI CPUs. The C-states are used to implement the CPU idle loop per CPU.
Please send dmesgs of booting using this patch. Index: acpicpu.c =================================================================== RCS file: /cvs/src/sys/dev/acpi/acpicpu.c,v retrieving revision 1.53 diff -u -p -u -p -b -r1.53 acpicpu.c --- acpicpu.c 24 Feb 2009 13:20:02 -0000 1.53 +++ acpicpu.c 7 Jun 2009 19:47:34 -0000 @@ -41,6 +41,10 @@ int acpicpu_match(struct device *, void void acpicpu_attach(struct device *, struct device *, void *); int acpicpu_notify(struct aml_node *, int, void *); void acpicpu_setperf(int); +void acpicpu_idle(void); + +#define C2_OVERHEAD 4 +#define C3_OVERHEAD 4 #define ACPI_STATE_C0 0x00 #define ACPI_STATE_C1 0x01 @@ -65,14 +69,20 @@ void acpicpu_setperf(int); /* 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)) +TAILQ_HEAD(acpi_cstatehead, acpi_cstate); + struct acpi_cstate { int type; int latency; int power; int address; + int enter; + + int pthr, dthr; + int pcount, dcount; - SLIST_ENTRY(acpi_cstate) link; + TAILQ_ENTRY(acpi_cstate) link; }; struct acpicpu_softc { @@ -85,7 +95,9 @@ struct acpicpu_softc { int sc_pblk_len; int sc_flags; - SLIST_HEAD(,acpi_cstate) sc_cstates; + int sc_prevsleep; + struct acpi_cstate *sc_cx; + struct acpi_cstatehead sc_cstates; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; @@ -121,6 +133,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); +u_int acpicpu_ticks(struct acpicpu_softc *, u_int, u_int); +int acpicpu_get_cstaddr(union acpi_resource *, void *); #if 0 void acpicpu_set_throttle(struct acpicpu_softc *, int); struct acpi_cstate *acpicpu_find_cstate(struct acpicpu_softc *, int); @@ -163,31 +177,36 @@ acpicpu_find_cstate(struct acpicpu_softc { struct acpi_cstate *cx; - SLIST_FOREACH(cx, &sc->sc_cstates, link) + TAILQ_FOREACH(cx, &sc->sc_cstates, link) { if (cx->type == type) return cx; + } return (NULL); } #endif struct acpi_cstate * -acpicpu_add_cstate(struct acpicpu_softc *sc, int type, int latency, int power, +acpicpu_add_cstate(struct acpicpu_softc *sc, + int type, int latency, int power, int address) { struct acpi_cstate *cx; + printf("C%d: latency:%d power:%d addr:%.4x\n", + type, latency, power, address); + 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)) + 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)) + sc->sc_flags & FLAGS_NO_C3) goto bad; break; } @@ -199,7 +218,19 @@ acpicpu_add_cstate(struct acpicpu_softc cx->latency = latency; cx->address = address; - SLIST_INSERT_HEAD(&sc->sc_cstates, cx, link); + if (type == ACPI_STATE_C1) + cx->pthr = 10; + else if (type == ACPI_STATE_C2) { + cx->dthr = 1; + cx->pthr = 4; + } + else if (type == ACPI_STATE_C3) + cx->dthr = 1; + + TAILQ_INSERT_TAIL(&sc->sc_cstates, cx, link); + + if (sc->sc_cx == NULL) + sc->sc_cx = cx; return (cx); bad: @@ -207,21 +238,34 @@ acpicpu_add_cstate(struct acpicpu_softc return (NULL); } +int +acpicpu_get_cstaddr(union acpi_resource *crs, void *arg) +{ + struct acpi_gas *gas = arg; + + if (AML_CRSTYPE(crs) == LR_GENREGISTER) + memcpy(gas, crs->pad+3, sizeof(*gas)); + return 0; +} + /* 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; + struct acpi_gas gas; -#if defined(ACPI_DEBUG) && !defined(SMALL_KERNEL) - aml_showvalue(val, 0); -#endif if (val->type != AML_OBJTYPE_PACKAGE || val->length != 4) return; + memset(&gas, 0, sizeof(gas)); + aml_parse_resource(val->v_package[0]->length, + val->v_package[0]->v_buffer, + acpicpu_get_cstaddr, &gas); acpicpu_add_cstate(sc, val->v_package[1]->v_integer, val->v_package[2]->v_integer, - val->v_package[3]->v_integer, -1); + val->v_package[3]->v_integer, + gas.address); } @@ -249,12 +293,13 @@ acpicpu_attach(struct device *parent, st int i; struct acpi_cstate *cx; u_int32_t status = 0; + u_int32_t pdc[4]; sc->sc_acpi = (struct acpi_softc *)parent; sc->sc_devnode = aa->aaa_node; acpicpu_sc[sc->sc_dev.dv_unit] = sc; - SLIST_INIT(&sc->sc_cstates); + TAILQ_INIT(&sc->sc_cstates); sc->sc_pss = NULL; @@ -270,7 +315,7 @@ acpicpu_attach(struct device *parent, st 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 +#if 1 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, @@ -280,20 +325,29 @@ acpicpu_attach(struct device *parent, st CPU_MAXSTATE(sc)); #endif + pdc[0] = 1; + pdc[1] = 1; + pdc[2] = -1; + memset(&res, 0, sizeof(res)); + res.type = AML_OBJTYPE_BUFFER; + res.v_buffer = (void *)pdc; + if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PDC", 1, &res, 0)) + printf("_PDC exists\n"); + /* Get C-States from _CST or FADT */ if (!aml_evalname(sc->sc_acpi, sc->sc_devnode, "_CST", 0, NULL, &res)) { + printf("Eval _CST\n"); 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_C1, 0, -1, 0); + if (sc->sc_pblk_len >= 5) acpicpu_add_cstate(sc, ACPI_STATE_C2, sc->sc_acpi->sc_fadt->p_lvl2_lat, -1, sc->sc_pblk_addr + 4); + if (sc->sc_pblk_len >= 6) acpicpu_add_cstate(sc, ACPI_STATE_C3, sc->sc_acpi->sc_fadt->p_lvl3_lat, -1, sc->sc_pblk_addr + 5); @@ -352,11 +406,11 @@ acpicpu_attach(struct device *parent, st * Nicely enumerate what power management capabilities * ACPI CPU provides. */ - if (!SLIST_EMPTY(&sc->sc_cstates)) { + if (!TAILQ_EMPTY(&sc->sc_cstates)) { printf(":"); i = 0; - SLIST_FOREACH(cx, &sc->sc_cstates, link) { + TAILQ_FOREACH(cx, &sc->sc_cstates, link) { if (i++) printf(","); switch (cx->type) { @@ -378,7 +432,7 @@ acpicpu_attach(struct device *parent, st if (!(sc->sc_flags & (FLAGS_NOPSS | FLAGS_NOPCT)) || !(sc->sc_flags & FLAGS_NOPSS)) { - printf("%c ", SLIST_EMPTY(&sc->sc_cstates) ? ':' : ','); + printf("%c ", TAILQ_EMPTY(&sc->sc_cstates) ? ':' : ','); /* * If acpicpu is itself providing the capability to transition @@ -394,24 +448,26 @@ acpicpu_attach(struct device *parent, st printf("PSS"); } + if (sc->sc_cx != NULL) + cpu_idle_cycle_fcn = acpicpu_idle; + printf("\n"); } int acpicpu_getppc(struct acpicpu_softc *sc) { - struct aml_value res; + int64_t ppc; sc->sc_ppc = 0; - if (aml_evalname(sc->sc_acpi, sc->sc_devnode, "_PPC", 0, NULL, &res)) { + if (aml_evalinteger(sc->sc_acpi, sc->sc_devnode, "_PPC", 0, NULL, &ppc)) { dnprintf(10, "%s: no _PPC\n", DEVNAME(sc)); return (1); } - sc->sc_ppc = aml_val2int(&res); + sc->sc_ppc = ppc; dnprintf(10, "%s: _PPC: %d\n", DEVNAME(sc), sc->sc_ppc); - aml_freevalue(&res); return (0); } @@ -670,4 +726,82 @@ acpicpu_setperf(int level) } else printf("%s: acpicpu setperf failed to alter frequency\n", sc->sc_devnode->name); +} + +u_int +acpicpu_ticks(struct acpicpu_softc *sc, u_int t1, u_int t2) +{ + if (t2 >= t1) + return t2 - t1; + else if (sc->sc_acpi->sc_fadt->flags & FADT_TMR_VAL_EXT) + return ((0xFFFFFFFF - t1) + t2 + 1); + else + return ((0x00FFFFFF - t1) + t2 + 1) & 0x00FFFFFF; +} + +void +acpicpu_idle(void) +{ + struct acpicpu_softc *sc; + struct acpi_cstate *cx, *cxdemote, *cxpromote; + struct timeval ts, te, td; + u_int32_t uSec; + int bm; + + sc = acpicpu_sc[cpu_number()]; + + /* Get current states */ + cx = sc->sc_cx; + cxdemote = TAILQ_PREV(cx, acpi_cstatehead, link); + cxpromote = TAILQ_NEXT(cx, link); + + cx->enter++; + switch (cx->type) { + case ACPI_STATE_C1: + __asm__ __volatile__("hlt"); + td.tv_sec = 0; + td.tv_usec = 1; + break; + case ACPI_STATE_C2: + microtime(&ts); + inb(cx->address); + microtime(&te); + timersub(&te, &ts, &td); + break; + case ACPI_STATE_C3: + + /* Disable BM ARB */ + acpi_write_pmreg(sc->sc_acpi, ACPIREG_PM2_CNT, 1, + ACPI_PM2_ARB_DIS); + + microtime(&ts); + inb(cx->address); + microtime(&te); + + /* Enable BM ARB */ + bm = inb(sc->sc_acpi->sc_fadt->pm2_cnt_blk); + outb(sc->sc_acpi->sc_fadt->pm2_cnt_blk, bm & ~ACPI_PM2_ARB_DIS); + + timersub(&te, &ts, &td); + break; + } + uSec = td.tv_sec * 1000000 + td.tv_usec; + if (cxpromote && uSec > cxpromote->latency) { + cx->pcount++; + cx->dcount = 0; + if (cx->pcount >= cx->pthr) { + printf("promoting C%d to C%d\n", + cx->type, cxpromote->type); + sc->sc_cx = cxpromote; + } + } + if (cxdemote && uSec < cxdemote->latency) { + cx->dcount++; + cx->pcount = 0; + if (cx->dcount >= cx->dthr) { + printf("demoting C%d to C%d\n", + cx->type, cxdemote->type); + sc->sc_cx = cxdemote; + } + } }