/*- * Copyright (c) 1999 Takanori Watanabe * Copyright (c) 1999, 2000 Mitsuru IWASAKI * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include "opt_acpi.h" #include #include #include #include #include #include #include #include #include #include /* * ACPI events */ static void acpi_process_event(acpi_softc_t *sc, u_int32_t status_e, u_int32_t status_0, u_int32_t status_1); /* * ACPI events */ static void acpi_process_event(acpi_softc_t *sc, u_int32_t status_e, u_int32_t status_0, u_int32_t status_1) { int i; if (status_e & ACPI_PM1_PWRBTN_EN) { if (sc->ignore_events & ACPI_PM1_PWRBTN_EN) { ACPI_DEBUGPRINT("PWRBTN event ingnored\n"); } else { #if 1 acpi_set_sleeping_state(sc, ACPI_S_STATE_S5); #else /* * If there is ACPI userland daemon, * this event should be passed to it * so that the user can determine power policy. */ acpi_queue_event(sc, ACPI_EVENT_TYPE_FIXEDREG, 0); #endif } } if (status_e & ACPI_PM1_SLPBTN_EN) { if (sc->ignore_events & ACPI_PM1_SLPBTN_EN) { ACPI_DEBUGPRINT("SLPBTN event ingnored\n"); } else { #if 1 acpi_set_sleeping_state(sc, ACPI_S_STATE_S1); #else acpi_queue_event(sc, ACPI_EVENT_TYPE_FIXEDREG, 1); #endif } } for(i = 0; i < sc->facp_body->gpe0_len * 4; i++) if((status_0 & (1 << i)) && (sc->gpe0_mask & (1 << i))) acpi_queue_event(sc, ACPI_EVENT_TYPE_GPEREG, i); for(i = 0; i < sc->facp_body->gpe1_len * 4 ; i++) if((status_1 & (1 << i)) && (sc->gpe1_mask & (1 << i))) acpi_queue_event(sc, ACPI_EVENT_TYPE_GPEREG, i + sc->facp_body->gpe1_base); } void acpi_intr(void *data) { u_int32_t enable; u_int32_t status_e, status_0, status_1; u_int32_t val; int debug; acpi_softc_t *sc; sc = (acpi_softc_t *)data; debug = acpi_debug; /* Save debug level */ acpi_debug = 0; /* Shut up */ /* * Power Management 1 Status Registers */ status_e = enable = 0; acpi_io_pm1_status(sc, ACPI_REGISTER_INPUT, &status_e); /* * Get current interrupt mask */ acpi_io_pm1_enable(sc, ACPI_REGISTER_INPUT, &enable); /* * Disable events and re-enable again */ if ((status_e & enable) != 0) { acpi_debug = debug; /* OK, you can speak */ ACPI_DEBUGPRINT("pm1_status intr CALLED\n"); /* Disable all interrupt generation */ val = enable & (~ACPI_PM1_ALL_ENABLE_BITS); acpi_io_pm1_enable(sc, ACPI_REGISTER_OUTPUT, &val); /* Clear interrupt status */ val = enable & ACPI_PM1_ALL_ENABLE_BITS; acpi_io_pm1_status(sc, ACPI_REGISTER_OUTPUT, &val); /* Re-enable interrupt */ acpi_io_pm1_enable(sc, ACPI_REGISTER_OUTPUT, &enable); acpi_debug = 0; /* Shut up again */ } /* * General-Purpose Events 0 Status Registers */ status_0 = enable = 0; acpi_io_gpe0_status(sc, ACPI_REGISTER_INPUT, &status_0); /* * Get current interrupt mask */ acpi_io_gpe0_enable(sc, ACPI_REGISTER_INPUT, &enable); /* * Disable events and re-enable again */ if ((status_0 & enable) != 0) { acpi_debug = debug; /* OK, you can speak */ ACPI_DEBUGPRINT("gpe0_status intr CALLED\n"); /* Disable all interrupt generation */ val = enable & ~status_0; #if 0 /* or should we disable all? */ val = 0x0; #endif acpi_io_gpe0_enable(sc, ACPI_REGISTER_OUTPUT, &val); #if 0 /* Clear interrupt status */ val = enable; /* XXX */ acpi_io_gpe0_status(sc, ACPI_REGISTER_OUTPUT, &val); /* Re-enable interrupt */ acpi_io_gpe0_enable(sc, ACPI_REGISTER_OUTPUT, &enable); acpi_debug = 0; /* Shut up again */ #endif } /* * General-Purpose Events 1 Status Registers */ status_1 = enable = 0; acpi_io_gpe1_status(sc, ACPI_REGISTER_INPUT, &status_1); /* Get current interrupt mask */ acpi_io_gpe1_enable(sc, ACPI_REGISTER_INPUT, &enable); /* * Disable events and re-enable again */ if ((status_1 & enable) != 0) { acpi_debug = debug; /* OK, you can speak */ ACPI_DEBUGPRINT("gpe1_status intr CALLED\n"); /* Disable all interrupt generation */ val = enable & ~status_1; #if 0 /* or should we disable all? */ val = 0x0; #endif acpi_io_gpe1_enable(sc, ACPI_REGISTER_OUTPUT, &val); /* Clear interrupt status */ val = enable; /* XXX */ acpi_io_gpe1_status(sc, ACPI_REGISTER_OUTPUT, &val); /* Re-enable interrupt */ acpi_io_gpe1_enable(sc, ACPI_REGISTER_OUTPUT, &enable); acpi_debug = 0; /* Shut up again */ } acpi_debug = debug; /* Restore debug level */ /* do something to handle the events... */ acpi_process_event(sc, status_e, status_0, status_1); } static int acpi_set_gpe_bits(struct aml_name *name, va_list ap) { struct acpi_softc *sc = va_arg(ap, struct acpi_softc *); int *gpemask0 = va_arg(ap, int *); int *gpemask1 = va_arg(ap, int *); int gpenum; #define XDIGITTONUM(c) ((isdigit(c)) ? ((c) - '0') : ('A' <= (c)&& (c) <= 'F') ? ((c) - 'A' + 10) : 0) if (isxdigit(name->name[2]) && isxdigit(name->name[3])) { gpenum = XDIGITTONUM(name->name[2]) * 16 + XDIGITTONUM(name->name[3]); ACPI_DEBUGPRINT("GPENUM %d %d \n", gpenum, sc->facp_body->gpe0_len * 4); if (gpenum < (sc->facp_body->gpe0_len * 4)) { *gpemask0 |= (1 << gpenum); } else { *gpemask1 |= (1 << (gpenum - sc->facp_body->gpe1_base)); } } ACPI_DEBUGPRINT("GPEMASK %x %x\n", *gpemask0, *gpemask1); return 0; } void acpi_enable_events(acpi_softc_t *sc) { u_int32_t status; u_int32_t mask0, mask1; u_int32_t flags; /* * Setup PM1 Enable Registers Fixed Feature Enable Bits (4.7.3.1.2) * based on flags field of Fixed ACPI Description Table (5.2.5). */ acpi_io_pm1_enable(sc, ACPI_REGISTER_INPUT, &status); flags = sc->facp_body->flags; if ((flags & ACPI_FACP_FLAG_PWR_BUTTON) == 0) { status |= ACPI_PM1_PWRBTN_EN; } if ((flags & ACPI_FACP_FLAG_SLP_BUTTON) == 0) { status |= ACPI_PM1_SLPBTN_EN; } acpi_io_pm1_enable(sc, ACPI_REGISTER_OUTPUT, &status); #if 1 /* * XXX * This should be done based on level event handlers in * \_GPE scope (4.7.2.2.1.2). */ mask0 = mask1 = 0; aml_apply_foreach_found_objects(NULL, "\\_GPE._L", acpi_set_gpe_bits, sc, &mask0, &mask1); /* XXX correct? */ sc->gpe0_mask = mask0; sc->gpe1_mask = mask1; acpi_io_gpe0_enable(sc, ACPI_REGISTER_OUTPUT, &mask0); acpi_io_gpe1_enable(sc, ACPI_REGISTER_OUTPUT, &mask1); #endif /* print all event status for debugging */ acpi_io_pm1_status(sc, ACPI_REGISTER_INPUT, &status); acpi_io_pm1_enable(sc, ACPI_REGISTER_INPUT, &status); acpi_io_gpe0_status(sc, ACPI_REGISTER_INPUT, &status); acpi_io_gpe0_enable(sc, ACPI_REGISTER_INPUT, &status); acpi_io_gpe1_status(sc, ACPI_REGISTER_INPUT, &status); acpi_io_gpe1_enable(sc, ACPI_REGISTER_INPUT, &status); acpi_io_pm1_control(sc, ACPI_REGISTER_INPUT, &mask0, &mask1); acpi_io_pm2_control(sc, ACPI_REGISTER_INPUT, &status); acpi_io_pm_timer(sc, ACPI_REGISTER_INPUT, &status); } void acpi_disable_events(acpi_softc_t *sc) { u_int32_t zero; if (sc->enabled) { zero = 0; acpi_io_pm1_enable(sc, ACPI_REGISTER_OUTPUT, &zero); acpi_io_gpe0_enable(sc, ACPI_REGISTER_OUTPUT, &zero); acpi_io_gpe1_enable(sc, ACPI_REGISTER_OUTPUT, &zero); } } void acpi_clear_ignore_events(void *arg) { ((acpi_softc_t *)arg)->ignore_events = 0; ACPI_DEBUGPRINT("ignore events cleared\n"); } /* * Transition the rest of the system through state changes. */ int acpi_send_pm_event(acpi_softc_t *sc, u_int8_t state) { int error; error = 0; switch (state) { case ACPI_S_STATE_S0: if (sc->system_state != ACPI_S_STATE_S0) { DEVICE_RESUME(root_bus); } break; case ACPI_S_STATE_S1: case ACPI_S_STATE_S2: case ACPI_S_STATE_S3: case ACPI_S_STATE_S4: error = DEVICE_SUSPEND(root_bus); break; default: break; } return (error); } /* * Event-handler thread. */ void acpi_queue_event(acpi_softc_t *sc, int type, int arg) { struct acpi_event *ae; int s; ae = malloc(sizeof(*ae), M_TEMP, M_NOWAIT); if(ae == NULL) panic("acpi_queue_event: can't allocate event"); ae->ae_type = type; ae->ae_arg = arg; s = splhigh(); STAILQ_INSERT_TAIL(&sc->event, ae, ae_q); splx(s); wakeup(&sc->event); } void acpi_event_thread(void *arg) { acpi_softc_t *sc = arg; int s , gpe1_base = sc->facp_body->gpe1_base; u_int32_t status,bit; struct acpi_event *ae; const char numconv[] = {'0','1','2','3','4','5','6','7', '8','9','A','B','C','D','E','F',-1}; char gpemethod[] = "\\_GPE._LXX"; union aml_object argv; /* Dummy*/ while(1) { s = splhigh(); if ((ae = STAILQ_FIRST(&sc->event)) == NULL) { splx(s); tsleep(&sc->event, PWAIT, "acpiev", 0); continue; } else { splx(s); } s = splhigh(); STAILQ_REMOVE_HEAD_UNTIL(&sc->event, ae, ae_q); splx(s); switch(ae->ae_type) { case ACPI_EVENT_TYPE_GPEREG: sprintf(gpemethod, "\\_GPE._L%c%c", numconv[(ae->ae_arg / 0x10) & 0xf], numconv[ae->ae_arg & 0xf]); aml_invoke_method_by_name(gpemethod, 0, &argv); sprintf(gpemethod, "\\_GPE._E%c%c", numconv[(ae->ae_arg / 0x10) & 0xf], numconv[ae->ae_arg & 0xf]); aml_invoke_method_by_name(gpemethod, 0, &argv); s=splhigh(); if((ae->ae_arg < gpe1_base) || (gpe1_base == 0)){ bit = 1 << ae->ae_arg; ACPI_DEBUGPRINT("GPE0%x\n", bit); acpi_io_gpe0_status(sc, ACPI_REGISTER_OUTPUT, &bit); acpi_io_gpe0_enable(sc, ACPI_REGISTER_INPUT, &status); ACPI_DEBUGPRINT("GPE0%x\n", status); status |= bit; acpi_io_gpe0_enable(sc, ACPI_REGISTER_OUTPUT, &status); } else { bit = 1 << (ae->ae_arg - sc->facp_body->gpe1_base); acpi_io_gpe1_status(sc, ACPI_REGISTER_OUTPUT, &bit); acpi_io_gpe1_enable(sc, ACPI_REGISTER_INPUT, &status); status |= bit; acpi_io_gpe1_enable(sc, ACPI_REGISTER_OUTPUT, &status); } splx(s); break; } free(ae, M_TEMP); } ACPI_DEVPRINTF("????\n"); }