Can we get some tests on this?
On Sun, Jun 07, 2009 at 02:48:24PM -0500, jor...@peereboom.us wrote:
> 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;
> + }
> + }
> }