summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Hargrave <jordan@cvs.openbsd.org>2006-06-30 04:03:14 +0000
committerJordan Hargrave <jordan@cvs.openbsd.org>2006-06-30 04:03:14 +0000
commitf2e5f9474ba638f4727b0f2da79926eb7cd0f400 (patch)
tree1dc5b45a06742005df33dbf655cb92aa82f1bd3c
parentebe8d416e75b4cbceb89cc2f924f2a5c42165e5a (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.c235
-rw-r--r--sys/dev/acpi/acpiec.c13
-rw-r--r--sys/dev/acpi/acpivar.h12
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 *);