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;
+ }
+ }
}