From: deraadt Date: Tue, 20 Jul 2010 12:14:10 +0000 (+0000) Subject: Handle the acpi interrupt controller with proper edge/level handling, X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=4fd192ced05d972cf36fefeb8f1c2ffea8b526e0;p=openbsd Handle the acpi interrupt controller with proper edge/level handling, wrapped in spltty to avoid racing against the interrupt controller. Repair the gpe bit masking code while there, and do operations in the right order ok kettenis mlarkin, help from jordan at figuring out the order of operations. --- diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index 92481a2136f..0b24da67a79 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.181 2010/07/20 04:04:00 matthew Exp $ */ +/* $OpenBSD: acpi.c,v 1.182 2010/07/20 12:14:10 deraadt Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * Copyright (c) 2005 Jordan Hargrave @@ -1551,9 +1551,8 @@ int acpi_interrupt(void *arg) { struct acpi_softc *sc = (struct acpi_softc *)arg; - u_int32_t processed, sts, en, idx, jdx; - - processed = 0; + u_int32_t processed = 0, idx, jdx; + u_int8_t sts, en; #if 0 acpi_add_gpeblock(sc, sc->sc_fadt->gpe0_blk, sc->sc_fadt->gpe0_blk_len>>1, 0); @@ -1568,11 +1567,23 @@ acpi_interrupt(void *arg) if (en & sts) { dnprintf(10, "GPE block: %.2x %.2x %.2x\n", idx, sts, en); + /* Mask the GPE until it is serviced */ acpi_write_pmreg(sc, ACPIREG_GPE_EN, idx>>3, en & ~sts); for (jdx = 0; jdx < 8; jdx++) { if (en & sts & (1L << jdx)) { /* Signal this GPE */ sc->gpe_table[idx+jdx].active = 1; + + /* + * Edge interrupts need their STS bits + * cleared now. Level interrupts will + * have their STS bits cleared just + * before they are re-enabled. + */ + if (sc->gpe_table[idx+jdx].edge) + acpi_write_pmreg(sc, + ACPIREG_GPE_STS, idx>>3, + 1L << jdx); processed = 1; } } @@ -1584,18 +1595,21 @@ acpi_interrupt(void *arg) if (sts & en) { dnprintf(10,"GEN interrupt: %.4x\n", sts & en); acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en & ~sts); - acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, en); - acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, en); - if (sts & ACPI_PM1_PWRBTN_STS) + if (sts & ACPI_PM1_PWRBTN_STS) { + acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, + ACPI_PM1_PWRBTN_STS); sc->sc_powerbtn = 1; - if (sts & ACPI_PM1_SLPBTN_STS) + } + if (sts & ACPI_PM1_SLPBTN_STS) { + acpi_write_pmreg(sc, ACPIREG_PM1_STS, 0, + ACPI_PM1_SLPBTN_STS); sc->sc_sleepbtn = 1; + } processed = 1; } if (processed) { - sc->sc_threadwaiting = 0; - wakeup(sc); + acpi_wakeup(sc); } return (processed); @@ -1656,8 +1670,10 @@ acpi_enable_onegpe(struct acpi_softc *sc, int gpe, int enable) { uint8_t mask = (1L << (gpe & 7)); uint8_t en; + int s; /* Read enabled register */ + s = spltty(); en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3); dnprintf(50, "%sabling GPE %.2x (current: %sabled) %.2x\n", enable ? "en" : "dis", gpe, (en & mask) ? "en" : "dis", en); @@ -1666,11 +1682,12 @@ acpi_enable_onegpe(struct acpi_softc *sc, int gpe, int enable) else en &= ~mask; acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en); + splx(s); } int acpi_set_gpehandler(struct acpi_softc *sc, int gpe, int (*handler) - (struct acpi_softc *, int, void *), void *arg, const char *label) + (struct acpi_softc *, int, void *), void *arg, int edge) { struct gpe_block *ptbl; @@ -1681,9 +1698,10 @@ acpi_set_gpehandler(struct acpi_softc *sc, int gpe, int (*handler) dnprintf(10, "error: GPE %.2x already enabled\n", gpe); return -EBUSY; } - dnprintf(50, "Adding GPE handler %.2x (%s)\n", gpe, label); + dnprintf(50, "Adding GPE handler %.2x (%s)\n", gpe, edge ? "edge" : "level"); ptbl->handler = handler; ptbl->arg = arg; + ptbl->edge = edge; return (0); } @@ -1692,14 +1710,18 @@ int acpi_gpe_level(struct acpi_softc *sc, int gpe, void *arg) { struct aml_node *node = arg; - uint8_t mask; + uint8_t mask, en; + int s; dnprintf(10, "handling Level-sensitive GPE %.2x\n", gpe); - mask = (1L << (gpe & 7)); - aml_evalnode(sc, node, 0, NULL, NULL); + + s = spltty(); + mask = (1L << (gpe & 7)); acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask); - acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, mask); + en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3); + acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask); + splx(s); return (0); } @@ -1709,14 +1731,17 @@ acpi_gpe_edge(struct acpi_softc *sc, int gpe, void *arg) { struct aml_node *node = arg; - uint8_t mask; + uint8_t mask, en; + int s; dnprintf(10, "handling Edge-sensitive GPE %.2x\n", gpe); - mask = (1L << (gpe & 7)); - aml_evalnode(sc, node, 0, NULL, NULL); - acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask); - acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, mask); + + s = spltty(); + mask = (1L << (gpe & 7)); + en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3); + acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en | mask); + splx(s); return (0); } @@ -1814,10 +1839,10 @@ acpi_init_gpeblock(struct acpi_softc *sc, int reg, int len, int base) snprintf(gpestr, sizeof(gpestr), "\\_GPE._L%.2X", base+i); h = aml_searchnode(&aml_root, gpestr); - if (acpi_set_gpehandler(sc, base+i, acpi_gpe_level, h, "level") != 0) { + if (acpi_set_gpehandler(sc, base+i, acpi_gpe_level, h, 0) != 0) { snprintf(gpestr, sizeof(gpestr), "\\_GPE._E%.2X", base+i); h = aml_searchnode(&aml_root, gpestr); - acpi_set_gpehandler(sc, base+i, acpi_gpe_edge, h, "edge"); + acpi_set_gpehandler(sc, base+i, acpi_gpe_edge, h, 1); } } } @@ -1908,15 +1933,13 @@ acpi_init_gpes(struct acpi_softc *sc) snprintf(name, sizeof(name), "\\_GPE._L%.2X", idx); gpe = aml_searchname(&aml_root, name); if (gpe != NULL) - acpi_set_gpehandler(sc, idx, acpi_gpe_level, gpe, - "level"); + acpi_set_gpehandler(sc, idx, acpi_gpe_level, gpe, 0); if (gpe == NULL) { /* Search Edge-sensitive GPES */ snprintf(name, sizeof(name), "\\_GPE._E%.2X", idx); gpe = aml_searchname(&aml_root, name); if (gpe != NULL) - acpi_set_gpehandler(sc, idx, acpi_gpe_edge, gpe, - "edge"); + acpi_set_gpehandler(sc, idx, acpi_gpe_edge, gpe, 1); } } aml_find_node(&aml_root, "_PRW", acpi_foundprw, sc); @@ -2313,6 +2336,7 @@ acpi_thread(void *arg) struct acpi_thread *thread = arg; struct acpi_softc *sc = thread->sc; u_int32_t gpe; + int s; /* * If we have an interrupt handler, we can get notification @@ -2329,6 +2353,7 @@ acpi_thread(void *arg) sc->sc_threadwaiting = 1; /* Enable Sleep/Power buttons if they exist */ + s = spltty(); flag = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0); if (!(sc->sc_fadt->flags & FADT_PWR_BUTTON)) { flag |= ACPI_PM1_PWRBTN_EN; @@ -2337,6 +2362,7 @@ acpi_thread(void *arg) flag |= ACPI_PM1_SLPBTN_EN; } acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, flag); + splx(s); /* Enable handled GPEs here */ for (gpe = 0; gpe < sc->sc_lastgpe; gpe++) { @@ -2346,13 +2372,17 @@ acpi_thread(void *arg) } while (thread->running) { - dnprintf(10, "sleep... %d\n", sc->sc_threadwaiting); - while (sc->sc_threadwaiting) - tsleep(sc, PWAIT, "acpi_idle", 0); + s = spltty(); + while (sc->sc_threadwaiting) { + dnprintf(10, "acpi going to sleep...\n"); + tsleep(sc, PWAIT, "acpi0", 0); + } sc->sc_threadwaiting = 1; - dnprintf(10, "wakeup..\n"); - if (aml_busy) + splx(s); + if (aml_busy) { + printf("skipping %d\n", aml_busy); continue; + } for (gpe = 0; gpe < sc->sc_lastgpe; gpe++) { struct gpe_block *pgpe = &sc->gpe_table[gpe]; @@ -2365,18 +2395,33 @@ acpi_thread(void *arg) } } if (sc->sc_powerbtn) { - sc->sc_powerbtn = 0; + uint8_t en; + sc->sc_powerbtn = 0; + dnprintf(1,"power button pressed\n"); aml_notify_dev(ACPI_DEV_PBD, 0x80); - dnprintf(1,"power button pressed\n"); + /* Reset the latch and re-enable the GPE */ + s = spltty(); + en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0); + acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, + en | ACPI_PM1_PWRBTN_STS); + splx(s); + } if (sc->sc_sleepbtn) { - sc->sc_sleepbtn = 0; + uint8_t en; + sc->sc_sleepbtn = 0; + dnprintf(1,"sleep button pressed\n"); aml_notify_dev(ACPI_DEV_SBD, 0x80); - dnprintf(1,"sleep button pressed\n"); + /* Reset the latch and re-enable the GPE */ + s = spltty(); + en = acpi_read_pmreg(sc, ACPIREG_PM1_EN, 0); + acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, + en | ACPI_PM1_SLPBTN_STS); + splx(s); } /* handle polling here to keep code non-concurrent*/ diff --git a/sys/dev/acpi/acpiec.c b/sys/dev/acpi/acpiec.c index a9f7e7cd38e..827c7fcd7ad 100644 --- a/sys/dev/acpi/acpiec.c +++ b/sys/dev/acpi/acpiec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpiec.c,v 1.30 2010/07/20 12:10:51 deraadt Exp $ */ +/* $OpenBSD: acpiec.c,v 1.31 2010/07/20 12:14:10 deraadt Exp $ */ /* * Copyright (c) 2006 Can Erkin Acar * @@ -284,7 +284,7 @@ acpiec_attach(struct device *parent, struct device *self, void *aux) #ifndef SMALL_KERNEL acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, - sc, "acpiec"); + sc, 1); #endif printf("\n"); @@ -309,15 +309,11 @@ int acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) { struct acpiec_softc *sc = arg; - u_int8_t mask, stat; + u_int8_t mask, stat, en; + int s; dnprintf(10, "ACPIEC: got gpe\n"); - /* Reset GPE event */ - mask = (1L << (gpe & 7)); - acpi_write_pmreg(acpi_sc, ACPIREG_GPE_STS, gpe>>3, mask); - acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, mask); - do { if (sc->sc_gotsci) acpiec_sci_event(sc); @@ -331,6 +327,14 @@ acpiec_gpehandler(struct acpi_softc *acpi_sc, int gpe, void *arg) sc->sc_gotsci = 1; } while (sc->sc_gotsci); + /* Unmask the GPE which was blocked at interrupt time */ + s = spltty(); + mask = (1L << (gpe & 7)); + acpi_write_pmreg(acpi_sc, ACPIREG_GPE_STS, gpe>>3, mask); + en = acpi_read_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3); + acpi_write_pmreg(acpi_sc, ACPIREG_GPE_EN, gpe>>3, en | mask); + splx(s); + return (0); } diff --git a/sys/dev/acpi/acpivar.h b/sys/dev/acpi/acpivar.h index d87f1ec1697..9f62bbcb9ea 100644 --- a/sys/dev/acpi/acpivar.h +++ b/sys/dev/acpi/acpivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: acpivar.h,v 1.62 2010/07/20 12:12:19 deraadt Exp $ */ +/* $OpenBSD: acpivar.h,v 1.63 2010/07/20 12:14:10 deraadt Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert * @@ -153,6 +153,7 @@ struct gpe_block { int (*handler)(struct acpi_softc *, int, void *); void *arg; int active; + int edge; }; struct acpi_devlist { @@ -310,7 +311,7 @@ void acpi_wakeup(void *); int acpi_gasio(struct acpi_softc *, int, int, uint64_t, int, int, void *); int acpi_set_gpehandler(struct acpi_softc *, int, - int (*)(struct acpi_softc *, int, void *), void *, const char *); + int (*)(struct acpi_softc *, int, void *), void *, int); void acpi_enable_gpe(struct acpi_softc *, u_int32_t); int acpiec_intr(struct acpiec_softc *); diff --git a/sys/dev/acpi/dsdt.c b/sys/dev/acpi/dsdt.c index 112280fe3c8..f690b152aa5 100644 --- a/sys/dev/acpi/dsdt.c +++ b/sys/dev/acpi/dsdt.c @@ -1,4 +1,4 @@ -/* $OpenBSD: dsdt.c,v 1.168 2010/07/20 12:12:20 deraadt Exp $ */ +/* $OpenBSD: dsdt.c,v 1.169 2010/07/20 12:14:10 deraadt Exp $ */ /* * Copyright (c) 2005 Jordan Hargrave * @@ -519,9 +519,13 @@ aml_setbit(u_int8_t *pb, int bit, int val) void acpi_poll(void *arg) { + int s; + + s = spltty(); acpi_softc->sc_poll = 1; acpi_softc->sc_threadwaiting = 0; wakeup(acpi_softc); + splx(s); timeout_add_sec(&acpi_softc->sc_dev_timeout, 10); } @@ -743,7 +747,7 @@ aml_lockfield(struct aml_scope *scope, struct aml_value *field) void aml_unlockfield(struct aml_scope *scope, struct aml_value *field) { - int st, x; + int st, x, s; if (AML_FIELD_LOCK(field->v_field.flags) != AML_FIELD_LOCK_ON) return; @@ -758,9 +762,11 @@ aml_unlockfield(struct aml_scope *scope, struct aml_value *field) return; /* Signal others if someone waiting */ + s = spltty(); x = acpi_read_pmreg(acpi_softc, ACPIREG_PM1_CNT, 0); x |= ACPI_PM1_GBL_RLS; acpi_write_pmreg(acpi_softc, ACPIREG_PM1_CNT, 0, x); + splx(s); return; }