diff options
author | Jordan Hargrave <jordan@cvs.openbsd.org> | 2006-06-30 04:03:14 +0000 |
---|---|---|
committer | Jordan Hargrave <jordan@cvs.openbsd.org> | 2006-06-30 04:03:14 +0000 |
commit | f2e5f9474ba638f4727b0f2da79926eb7cd0f400 (patch) | |
tree | 1dc5b45a06742005df33dbf655cb92aa82f1bd3c | |
parent | ebe8d416e75b4cbceb89cc2f924f2a5c42165e5a (diff) |
Added new code for GPE handling
Also works with ACPIEC; need to fixup acpiec.c to remove original interrupt handler
ok marco@
-rw-r--r-- | sys/dev/acpi/acpi.c | 235 | ||||
-rw-r--r-- | sys/dev/acpi/acpiec.c | 13 | ||||
-rw-r--r-- | sys/dev/acpi/acpivar.h | 12 |
3 files changed, 199 insertions, 61 deletions
diff --git a/sys/dev/acpi/acpi.c b/sys/dev/acpi/acpi.c index b75deb613af..c5cb6ad60da 100644 --- a/sys/dev/acpi/acpi.c +++ b/sys/dev/acpi/acpi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpi.c,v 1.52 2006/06/30 01:09:47 jordan Exp $ */ +/* $OpenBSD: acpi.c,v 1.53 2006/06/30 04:03:13 jordan Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> * Copyright (c) 2005 Jordan Hargrave <jordan@openbsd.org> @@ -75,12 +75,13 @@ void acpi_init_pm(struct acpi_softc *); void acpi_filtdetach(struct knote *); int acpi_filtread(struct knote *, long); +void __acpi_enable_gpe(struct acpi_softc *, int, int); +int acpi_gpe_level(struct acpi_softc *, int, void *); +int acpi_gpe_edge(struct acpi_softc *, int, void *); + #define ACPI_LOCK(sc) #define ACPI_UNLOCK(sc) -#define GPE0_LEN(sc) (sc->sc_pmregs[ACPIREG_GPE0_EN].size >> 3) -#define GPE1_LEN(sc) (sc->sc_pmregs[ACPIREG_GPE1_EN].size >> 3) - /* XXX move this into dsdt softc at some point */ extern struct aml_node aml_root; @@ -385,9 +386,10 @@ int acpi_read_pmreg(struct acpi_softc *sc, int reg, int offset) { bus_space_handle_t ioh; - bus_size_t size; + bus_size_t size, __size; int regval; + __size = 0; /* Special cases: 1A/1B blocks can be OR'ed together */ switch (reg) { case ACPIREG_PM1_EN: @@ -400,8 +402,25 @@ acpi_read_pmreg(struct acpi_softc *sc, int reg, int offset) return (acpi_read_pmreg(sc, ACPIREG_PM1A_CNT, offset) | acpi_read_pmreg(sc, ACPIREG_PM1B_CNT, offset)); case ACPIREG_GPE_STS: - case ACPIREG_GPE_EN: + __size = 1; + dnprintf(0, "read GPE_STS offset: %.2x %.2x %.2x\n", + offset, + sc->sc_fadt->gpe0_blk_len>>1, + sc->sc_fadt->gpe1_blk_len>>1); + if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) { + reg = ACPIREG_GPE0_STS; + } break; + case ACPIREG_GPE_EN: + __size = 1; + dnprintf(0, "read GPE_EN offset: %.2x %.2x %.2x\n", + offset, + sc->sc_fadt->gpe0_blk_len>>1, + sc->sc_fadt->gpe1_blk_len>>1); + if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) { + reg = ACPIREG_GPE0_EN; + } + break; } if (reg >= ACPIREG_MAXREG || sc->sc_pmregs[reg].size == 0) @@ -410,6 +429,8 @@ acpi_read_pmreg(struct acpi_softc *sc, int reg, int offset) regval = 0; ioh = sc->sc_pmregs[reg].ioh; size = sc->sc_pmregs[reg].size; + if (__size) + size = __size; if (size > 4) size = 4; @@ -436,8 +457,9 @@ void acpi_write_pmreg(struct acpi_softc *sc, int reg, int offset, int regval) { bus_space_handle_t ioh; - bus_size_t size; + bus_size_t size, __size; + __size = 0; /* Special cases: 1A/1B blocks can be written with same value */ switch (reg) { case ACPIREG_PM1_EN: @@ -452,6 +474,26 @@ acpi_write_pmreg(struct acpi_softc *sc, int reg, int offset, int regval) acpi_write_pmreg(sc, ACPIREG_PM1A_CNT, offset, regval); acpi_write_pmreg(sc, ACPIREG_PM1B_CNT, offset, regval); break; + case ACPIREG_GPE_STS: + __size = 1; + dnprintf(0, "write GPE_STS offset: %.2x %.2x %.2x %.2x\n", + offset, + sc->sc_fadt->gpe0_blk_len>>1, + sc->sc_fadt->gpe1_blk_len>>1, regval); + if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) { + reg = ACPIREG_GPE0_STS; + } + break; + case ACPIREG_GPE_EN: + __size = 1; + dnprintf(0, "write GPE_EN offset: %.2x %.2x %.2x %.2x\n", + offset, + sc->sc_fadt->gpe0_blk_len>>1, + sc->sc_fadt->gpe1_blk_len>>1, regval); + if (offset < (sc->sc_fadt->gpe0_blk_len >> 1)) { + reg = ACPIREG_GPE0_EN; + } + break; } /* All special case return here */ @@ -460,6 +502,8 @@ acpi_write_pmreg(struct acpi_softc *sc, int reg, int offset, int regval) ioh = sc->sc_pmregs[reg].ioh; size = sc->sc_pmregs[reg].size; + if (__size) + size = __size; if (size > 4) size = 4; switch (size) { @@ -728,9 +772,6 @@ acpi_attach(struct device *parent, struct device *self, void *aux) /* Find available sleeping states */ acpi_init_states(sc); - /* Initialize GPE handlers */ - acpi_init_gpes(sc); - /* Find available sleep/resume related methods. */ acpi_init_pm(sc); @@ -750,6 +791,9 @@ acpi_attach(struct device *parent, struct device *self, void *aux) /* Map Power Management registers */ acpi_map_pmregs(sc); + /* Initialize GPE handlers */ + acpi_init_gpes(sc); + /* * Take over ACPI control. Note that once we do this, we * effectively tell the system that we have ownership of @@ -1001,26 +1045,25 @@ int acpi_interrupt(void *arg) { struct acpi_softc *sc = (struct acpi_softc *)arg; - u_int32_t ec, processed, sts, en; + u_int32_t ec, processed, sts, en, idx, jdx; ec = 0; processed = 0; - sts = acpi_read_pmreg(sc, ACPIREG_GPE0_STS, 0); - en = acpi_read_pmreg(sc, ACPIREG_GPE0_EN, 0); - if (sts & en) { - dnprintf(10, "GPE interrupt: %.8x %.8x %.8x\n", - sts, en, sts & en); - /* disable interrupts until handled */ - acpi_write_pmreg(sc, ACPIREG_GPE0_EN, 0, en & ~sts); - - sc->sc_gpe_sts = sts; - sc->sc_gpe_en = en; - processed = 1; - if ((sc->sc_ec != NULL) && (sts & sc->sc_ec_gpemask)) { - ec = 1; - if ((sts & en) == sc->sc_ec_gpemask) - processed = 0; + dnprintf(40, "ACPI Interrupt\n"); + for (idx=0; idx<sc->sc_lastgpe; idx+=8) { + sts = acpi_read_pmreg(sc, ACPIREG_GPE_STS, idx>>3); + en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, idx>>3); + if (en & sts) { + dnprintf(10, "GPE block: %.2x %.2x %.2x\n", idx, sts, en); + 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; + processed = 1; + } + } } } @@ -1059,25 +1102,117 @@ acpi_interrupt(void *arg) } void +__acpi_enable_gpe(struct acpi_softc *sc, int gpe, int enable) +{ + uint8_t mask = (1L << (gpe & 7)); + uint8_t en; + + /* Read enabled register */ + en = acpi_read_pmreg(sc, ACPIREG_GPE_EN, gpe>>3); + dnprintf(0, "%sabling GPE %.2x (current: %sabled) %.2x\n", + enable ? "en" : "dis", gpe, + (en & mask) ? "en" : "dis", en); + if (enable) + en |= mask; + else + en &= ~mask; + acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, en); +} + +int +acpi_set_gpehandler(struct acpi_softc *sc, int gpe, int (*handler)(struct acpi_softc *, int, void *), void *arg, + const char *label) +{ + if (gpe >= sc->sc_lastgpe || handler == NULL) + return -EINVAL; + + if (sc->gpe_table[gpe].handler != NULL) { + dnprintf(10, "error: GPE %.2x already enabled!\n", gpe); + return -EBUSY; + } + + dnprintf(0, "Adding GPE handler %.2x (%s)\n", gpe, label); + sc->gpe_table[gpe].handler = handler; + sc->gpe_table[gpe].arg = arg; + + /* Defer enabling GPEs */ + + return (0); +} + +int +acpi_gpe_level(struct acpi_softc *sc, int gpe, void *arg) +{ + struct aml_node *node = arg; + struct aml_value res; + uint8_t mask; + + dnprintf(10, "handling Level-sensitive GPE %.2x\n", gpe); + mask = (1L << (gpe & 7)); + if (node != NULL) + aml_eval_object(sc, node, &res, 0, NULL); + acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask); + acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, mask); + + return (0); +} + +int +acpi_gpe_edge(struct acpi_softc *sc, int gpe, void *arg) +{ + + struct aml_node *node = arg; + struct aml_value res; + uint8_t mask; + + dnprintf(10, "handling Edge-sensitive GPE %.2x\n", gpe); + mask = (1L << (gpe & 7)); + if (node != NULL) + aml_eval_object(sc, node, &res, 0, NULL); + acpi_write_pmreg(sc, ACPIREG_GPE_STS, gpe>>3, mask); + acpi_write_pmreg(sc, ACPIREG_GPE_EN, gpe>>3, mask); + + return (0); +} + +void acpi_init_gpes(struct acpi_softc *sc) { struct aml_node *gpe; char name[12]; int idx, ngpe; + sc->sc_lastgpe = sc->sc_fadt->gpe0_blk_len << 2; + if (sc->sc_fadt->gpe1_blk_len) { + } + dnprintf(0, "Last GPE: %.2x\n", sc->sc_lastgpe); + + /* Allocate GPE table */ + sc->gpe_table = malloc(sc->sc_lastgpe * sizeof(struct gpe_block), M_DEVBUF, M_WAITOK); + memset(sc->gpe_table, 0, sc->sc_lastgpe * sizeof(struct gpe_block)); + ngpe = 0; memset(sc->sc_gpes, 0, sizeof(sc->sc_gpes)); - for (idx=0; idx<256; idx++) { + /* Clear GPE status */ + for (idx=0; idx<sc->sc_lastgpe; idx+=8) { + acpi_write_pmreg(sc, ACPIREG_GPE_EN, idx>>3, 0); + acpi_write_pmreg(sc, ACPIREG_GPE_STS, idx>>3, -1); + } + for (idx=0; idx<sc->sc_lastgpe; idx++) { /* Search Level-sensitive GPES */ sc->sc_gpes[ngpe].gpe_type = GPE_LEVEL; 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"); if (gpe == NULL) { /* Search Edge-sensitive GPES */ sc->sc_gpes[ngpe].gpe_type = GPE_EDGE; 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"); } if (gpe != NULL) { sc->sc_gpes[ngpe].gpe_number = idx; @@ -1403,9 +1538,7 @@ acpi_isr_thread(void *arg) { struct acpi_thread *thread = arg; struct acpi_softc *sc = thread->sc; - u_int32_t gpemask, gpe; - struct aml_value res; - u_int32_t sts, en; + u_int32_t gpe; /* * If we have an interrupt handler, we can get notification @@ -1431,20 +1564,11 @@ acpi_isr_thread(void *arg) } acpi_write_pmreg(sc, ACPIREG_PM1_EN, 0, flag); - /* Clear GPE interrupts */ -#if 0 - for (idx=0; idx<GPE0_LEN(sc); idx++) { - acpi_write_pmreg(sc, ACPIREG_GPE0_EN, idx, 0); - acpi_write_pmreg(sc, ACPIREG_GPE0_STS, idx, -1); - } - for (idx=0; idx<GPE1_LEN(sc); idx++) { - acpi_write_pmreg(sc, ACPIREG_GPE1_EN, idx, 0); - acpi_write_pmreg(sc, ACPIREG_GPE1_STS, idx, -1); + /* Enable handled GPEs here */ + for (gpe=0; gpe<sc->sc_lastgpe; gpe++) { + if (sc->gpe_table[gpe].handler) + __acpi_enable_gpe(sc, gpe, 1); } -#else - acpi_write_pmreg(sc, ACPIREG_GPE0_EN, 0, 0); - acpi_write_pmreg(sc, ACPIREG_GPE0_STS, 0, -1); -#endif /* Enable EC interrupt */ if (sc->sc_ec != NULL) @@ -1452,30 +1576,23 @@ acpi_isr_thread(void *arg) } while (thread->running) { - dnprintf(10, "sleep...\n"); + dnprintf(10, "sleep... %d\n", sc->sc_wakeup); while (sc->sc_wakeup) tsleep(sc, PWAIT, "acpi_idle", 0); sc->sc_wakeup = 1; dnprintf(10, "wakeup..\n"); - sts = sc->sc_gpe_sts; - en = sc->sc_gpe_en; - if (en & sts) { - sc->sc_gpe_en = 0; - sc->sc_gpe_sts = 0; - - gpemask = en & sts; - dnprintf(10, "softgpe: %x\n", en & sts); - for (gpe=0; gpe<sc->sc_maxgpe; gpe++) { - if (gpemask & (1L << sc->sc_gpes[gpe].gpe_number)) { - dnprintf(10, "Got GPE: %x %x\n", gpe, sc->sc_gpes[gpe].gpe_number); - aml_eval_object(sc, sc->sc_gpes[gpe].gpe_handler, &res, 0, NULL); + for (gpe=0; gpe < sc->sc_lastgpe; gpe++) { + struct gpe_block *pgpe = &sc->gpe_table[gpe]; + + if (pgpe->active) { + pgpe->active = 0; + dnprintf(0, "softgpe: %.2x\n", gpe); + if (pgpe->handler) { + pgpe->handler(sc, gpe, pgpe->arg); } } - acpi_write_pmreg(sc, ACPIREG_GPE0_STS, 0, en & sts); - acpi_write_pmreg(sc, ACPIREG_GPE0_EN, 0, en); } - if (sc->sc_powerbtn) { sc->sc_powerbtn = 0; diff --git a/sys/dev/acpi/acpiec.c b/sys/dev/acpi/acpiec.c index 14b5b9680b6..afdf7e819fb 100644 --- a/sys/dev/acpi/acpiec.c +++ b/sys/dev/acpi/acpiec.c @@ -1,4 +1,4 @@ -/* $OpenBSD: acpiec.c,v 1.1 2006/05/29 00:54:23 canacar Exp $ */ +/* $OpenBSD: acpiec.c,v 1.2 2006/06/30 04:03:13 jordan Exp $ */ /* * Copyright (c) 2006 Can Erkin Acar <canacar@openbsd.org> * @@ -58,6 +58,8 @@ void acpiec_sci_event(struct acpiec_softc *); void acpiec_get_events(struct acpiec_softc *); +int acpiec_gpehandler(struct acpi_softc *, int, void *); + struct aml_node *aml_find_name(struct acpi_softc *, struct aml_node *, const char *); @@ -332,6 +334,8 @@ acpiec_attach(struct device *parent, struct device *self, void *aux) dnprintf(10, "%s: GPE: %d (%x)\n", DEVNAME(sc), sc->sc_gpe, sc->sc_acpi->sc_ec_gpemask); + acpi_set_gpehandler(sc->sc_acpi, sc->sc_gpe, acpiec_gpehandler, sc, "acpiec"); + /* Enable EC interrupt */ acpi_enable_gpe(sc->sc_acpi, sc->sc_acpi->sc_ec_gpemask); @@ -353,6 +357,13 @@ acpiec_get_events(struct acpiec_softc *sc) } } +int +acpiec_gpehandler(struct acpi_softc *sc, int gpe, void *arg) +{ + dnprintf(10, "ACPIEC: got gpe\n"); + return (0); +} + void acpiec_handle_events(struct acpiec_softc *sc) { diff --git a/sys/dev/acpi/acpivar.h b/sys/dev/acpi/acpivar.h index 90ab5755f41..3e4fd899f32 100644 --- a/sys/dev/acpi/acpivar.h +++ b/sys/dev/acpi/acpivar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: acpivar.h,v 1.24 2006/06/30 01:09:47 jordan Exp $ */ +/* $OpenBSD: acpivar.h,v 1.25 2006/06/30 04:03:13 jordan Exp $ */ /* * Copyright (c) 2005 Thorsten Lockert <tholo@sigmasoft.com> * @@ -99,6 +99,12 @@ struct acpi_thread { volatile int running; }; +struct gpe_block { + int (*handler)(struct acpi_softc *, int, void *); + void *arg; + int active; +}; + struct acpi_softc { struct device sc_dev; @@ -146,6 +152,9 @@ struct acpi_softc { struct aml_node *gpe_handler; } sc_gpes[256]; int sc_maxgpe; + int sc_lastgpe; + + struct gpe_block *gpe_table; int sc_wakeup; u_int32_t sc_gpe_sts; @@ -202,6 +211,7 @@ void acpi_resume(struct acpi_softc *); void acpi_delay(struct acpi_softc *, int64_t); 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 *); void acpi_enable_gpe(struct acpi_softc *, u_int32_t); int acpiec_intr(struct acpiec_softc *); |