/* * Handle a GPE sent to us. */ static void EcGpeHandler(void *Context) { struct acpi_ec_softc *sc = Context; int csrvalue; /* * If EC is locked, the intr must process EcRead/Write wait only. * Query request must be pending. */ if (EcIsLocked(sc)){ csrvalue = EC_GET_CSR(sc); if (csrvalue & EC_EVENT_SCI) sc->ec_pendquery = 1; if ((csrvalue & EC_FLAG_OUTPUT_BUFFER) || !(csrvalue & EC_FLAG_INPUT_BUFFER)) { sc->ec_csrvalue = csrvalue; wakeup((void *)&sc->ec_csrvalue); } }else{ /* Queue GpeQuery Handler */ if (ACPI_FAILURE(AcpiOsQueueForExecution(OSD_PRIORITY_HIGH, EcGpeQueryHandler,Context))) { printf("QueryHandler Queuing Failed\n"); } } return; }
/* * The GPE handler is called when IBE/OBF or SCI events occur. We are * called from an unknown lock context. */ static uint32_t EcGpeHandler(ACPI_HANDLE GpeDevice, UINT32 GpeNumber, void *Context) { struct acpi_ec_softc *sc = Context; ACPI_STATUS Status; EC_STATUS EcStatus; KASSERT(Context != NULL, ("EcGpeHandler called with NULL")); /* * Notify EcWaitEvent() that the status register is now fresh. If we * didn't do this, it wouldn't be possible to distinguish an old IBE * from a new one, for example when doing a write transaction (writing * address and then data values.) */ atomic_add_int(&sc->ec_gencount, 1); wakeup(sc); /* * If the EC_SCI bit of the status register is set, queue a query handler. * It will run the query and _Qxx method later, under the lock. */ EcStatus = EC_GET_CSR(sc); if ((EcStatus & EC_EVENT_SCI) && !sc->ec_sci_pend) { Status = AcpiOsExecute(OSL_GPE_HANDLER, EcGpeQueryHandler, Context); if (ACPI_SUCCESS(Status)) { sc->ec_sci_pend = TRUE; return (0); } else { kprintf("EcGpeHandler: queuing GPE query handler failed\n"); } } return (ACPI_REENABLE_GPE); }
static ACPI_STATUS EcWaitEvent(struct acpi_ec_softc *sc, EC_EVENT Event) { EC_STATUS EcStatus; UINT32 i = 0; if (!EcIsLocked(sc)) ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev), "EcWaitEvent called without EC lock!\n"); /* * Stall 1us: * ---------- * Stall for 1 microsecond before reading the status register * for the first time. This allows the EC to set the IBF/OBF * bit to its proper state. * * XXX it is not clear why we read the CSR twice. */ AcpiOsStall(1); EcStatus = EC_GET_CSR(sc); /* * Wait For Event: * --------------- * Poll the EC status register to detect completion of the last * command. Wait up to 10ms (in 10us chunks) for this to occur. */ for (i = 0; i < 1000; i++) { EcStatus = EC_GET_CSR(sc); if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) && (EcStatus & EC_FLAG_OUTPUT_BUFFER)) return(AE_OK); if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) && !(EcStatus & EC_FLAG_INPUT_BUFFER)) return(AE_OK); AcpiOsStall(10); } return(AE_ERROR); }
static ACPI_STATUS EcCommand(struct acpi_ec_softc *sc, EC_COMMAND cmd) { ACPI_STATUS status; EC_EVENT event; EC_STATUS ec_status; u_int gen_count; ACPI_SERIAL_ASSERT(ec); /* Don't use burst mode if user disabled it. */ if (!ec_burst_mode && cmd == EC_COMMAND_BURST_ENABLE) return (AE_ERROR); /* Decide what to wait for based on command type. */ switch (cmd) { case EC_COMMAND_READ: case EC_COMMAND_WRITE: case EC_COMMAND_BURST_DISABLE: event = EC_EVENT_INPUT_BUFFER_EMPTY; break; case EC_COMMAND_QUERY: case EC_COMMAND_BURST_ENABLE: event = EC_EVENT_OUTPUT_BUFFER_FULL; break; default: device_printf(sc->ec_dev, "EcCommand: invalid command %#x\n", cmd); return (AE_BAD_PARAMETER); } /* * Ensure empty input buffer before issuing command. * Use generation count of zero to force a quick check. */ status = EcWaitEvent(sc, EC_EVENT_INPUT_BUFFER_EMPTY, 0); if (ACPI_FAILURE(status)) return (status); /* Run the command and wait for the chosen event. */ CTR1(KTR_ACPI, "ec running command %#x", cmd); gen_count = sc->ec_gencount; EC_SET_CSR(sc, cmd); status = EcWaitEvent(sc, event, gen_count); if (ACPI_SUCCESS(status)) { /* If we succeeded, burst flag should now be present. */ if (cmd == EC_COMMAND_BURST_ENABLE) { ec_status = EC_GET_CSR(sc); if ((ec_status & EC_FLAG_BURST_MODE) == 0) status = AE_ERROR; } } else device_printf(sc->ec_dev, "EcCommand: no response to %#x\n", cmd); return (status); }
/* * Wait for an event interrupt for a specific condition. */ static ACPI_STATUS EcWaitEventIntr(struct acpi_ec_softc *sc, EC_EVENT Event) { EC_STATUS EcStatus; int i; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Event); /* XXX this should test whether interrupts are available some other way */ if (cold || acpi_ec_event_driven) return_ACPI_STATUS(EcWaitEvent(sc, Event)); if (!EcIsLocked(sc)) ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev), "EcWaitEventIntr called without EC lock!\n"); EcStatus = EC_GET_CSR(sc); /* XXX waiting too long? */ for(i = 0; i < 10; i++){ /* * Check EC status against the desired event. */ if ((Event == EC_EVENT_OUTPUT_BUFFER_FULL) && (EcStatus & EC_FLAG_OUTPUT_BUFFER)) return_ACPI_STATUS(AE_OK); if ((Event == EC_EVENT_INPUT_BUFFER_EMPTY) && !(EcStatus & EC_FLAG_INPUT_BUFFER)) return_ACPI_STATUS(AE_OK); sc->ec_csrvalue = 0; if (ACPI_MSLEEP(&sc->ec_csrvalue, &acpi_mutex, PZERO, "EcWait", 1) != EWOULDBLOCK){ EcStatus = sc->ec_csrvalue; }else{ EcStatus = EC_GET_CSR(sc); } } return_ACPI_STATUS(AE_ERROR); }
static ACPI_STATUS EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event) { ACPI_STATUS status; EC_STATUS ec_status; status = AE_NO_HARDWARE_RESPONSE; ec_status = EC_GET_CSR(sc); if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) { sc->ec_burstactive = FALSE; } if (EVENT_READY(event, ec_status)) { status = AE_OK; } return (status); }
static ACPI_STATUS EcCheckStatus(struct acpi_ec_softc *sc, const char *msg, EC_EVENT event) { ACPI_STATUS status; EC_STATUS ec_status; status = AE_NO_HARDWARE_RESPONSE; ec_status = EC_GET_CSR(sc); if (sc->ec_burstactive && !(ec_status & EC_FLAG_BURST_MODE)) { CTR1(KTR_ACPI, "ec burst disabled in waitevent (%s)", msg); sc->ec_burstactive = FALSE; } if (EVENT_READY(event, ec_status)) { CTR2(KTR_ACPI, "ec %s wait ready, status %#x", msg, ec_status); status = AE_OK; } return (status); }
static ACPI_STATUS EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 Width, UINT64 *Value, void *Context, void *RegionContext) { struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context; ACPI_PHYSICAL_ADDRESS EcAddr; UINT8 *EcData; ACPI_STATUS Status; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address); if (Function != ACPI_READ && Function != ACPI_WRITE) return_ACPI_STATUS (AE_BAD_PARAMETER); if (Width % 8 != 0 || Value == NULL || Context == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); if (Address + Width / 8 > 256) return_ACPI_STATUS (AE_BAD_ADDRESS); /* * If booting, check if we need to run the query handler. If so, we * we call it directly here since our thread taskq is not active yet. */ if (cold || rebooting || sc->ec_suspending) { if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) { EcGpeQueryHandler(sc); } } /* Serialize with EcGpeQueryHandler() at transaction granularity. */ Status = EcLock(sc); if (ACPI_FAILURE(Status)) return_ACPI_STATUS (Status); /* If we can't start burst mode, continue anyway. */ Status = EcCommand(sc, EC_COMMAND_BURST_ENABLE); if (ACPI_SUCCESS(Status)) { if (EC_GET_DATA(sc) == EC_BURST_ACK) { sc->ec_burstactive = TRUE; } } /* Perform the transaction(s), based on Width. */ EcAddr = Address; EcData = (UINT8 *)Value; if (Function == ACPI_READ) *Value = 0; do { switch (Function) { case ACPI_READ: Status = EcRead(sc, EcAddr, EcData); break; case ACPI_WRITE: Status = EcWrite(sc, EcAddr, *EcData); break; } if (ACPI_FAILURE(Status)) break; EcAddr++; EcData++; } while (EcAddr < Address + Width / 8); if (sc->ec_burstactive) { sc->ec_burstactive = FALSE; if (ACPI_SUCCESS(EcCommand(sc, EC_COMMAND_BURST_DISABLE))) { } } EcUnlock(sc); return_ACPI_STATUS (Status); }
static ACPI_STATUS EcSpaceHandler(UINT32 Function, ACPI_PHYSICAL_ADDRESS Address, UINT32 Width, UINT64 *Value, void *Context, void *RegionContext) { struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context; ACPI_STATUS Status; UINT8 *EcData; UINT8 EcAddr; int bytes, i; ACPI_FUNCTION_TRACE_U32((char *)(uintptr_t)__func__, (UINT32)Address); if (Width % 8 != 0 || Value == NULL || Context == NULL) return_ACPI_STATUS (AE_BAD_PARAMETER); bytes = Width / 8; if (Address + bytes - 1 > 0xFF) return_ACPI_STATUS (AE_BAD_ADDRESS); if (Function == ACPI_READ) *Value = 0; EcAddr = Address; EcData = (UINT8 *)Value; /* * If booting, check if we need to run the query handler. If so, we * we call it directly here since our thread taskq is not active yet. */ if (cold || rebooting || sc->ec_suspending) { if ((EC_GET_CSR(sc) & EC_EVENT_SCI)) { CTR0(KTR_ACPI, "ec running gpe handler directly"); EcGpeQueryHandler(sc); } } /* Serialize with EcGpeQueryHandler() at transaction granularity. */ Status = EcLock(sc); if (ACPI_FAILURE(Status)) return_ACPI_STATUS (Status); /* Perform the transaction(s), based on Width. */ for (i = 0; i < bytes; i++, EcAddr++, EcData++) { switch (Function) { case ACPI_READ: Status = EcRead(sc, EcAddr, EcData); break; case ACPI_WRITE: Status = EcWrite(sc, EcAddr, *EcData); break; default: device_printf(sc->ec_dev, "invalid EcSpaceHandler function %d\n", Function); Status = AE_BAD_PARAMETER; break; } if (ACPI_FAILURE(Status)) break; } EcUnlock(sc); return_ACPI_STATUS (Status); }
static void EcGpeQueryHandler(void *Context) { struct acpi_ec_softc *sc = (struct acpi_ec_softc *)Context; UINT8 Data; ACPI_STATUS Status; char qxx[5]; ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); for (;;) { /* * Check EC_SCI. * * Bail out if the EC_SCI bit of the status register is not set. * Note that this function should only be called when * this bit is set (polling is used to detect IBE/OBF events). * * It is safe to do this without locking the controller, as it's * OK to call EcQuery when there's no data ready; in the worst * case we should just find nothing waiting for us and bail. */ if (!(EC_GET_CSR(sc) & EC_EVENT_SCI)) break; /* * Find out why the EC is signalling us */ Status = EcQuery(sc, &Data); /* * If we failed to get anything from the EC, give up */ if (ACPI_FAILURE(Status)) { ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev), "GPE query failed - %s\n", AcpiFormatException(Status)); break; } /* * Evaluate _Qxx to respond to the controller. */ sprintf(qxx, "_Q%02x", Data); strupr(qxx); Status = AcpiEvaluateObject(sc->ec_handle, qxx, NULL, NULL); /* * Ignore spurious query requests. */ if (ACPI_FAILURE(Status) && (Data != 0 || Status != AE_NOT_FOUND)) { ACPI_VPRINT(sc->ec_dev, acpi_device_get_parent_softc(sc->ec_dev), "evaluation of GPE query method %s failed - %s\n", qxx, AcpiFormatException(Status)); } } /* I know I request Level trigger cleanup */ if (ACPI_FAILURE(AcpiClearEvent(sc->ec_gpebit, ACPI_EVENT_GPE))) printf("EcGpeQueryHandler:ClearEvent Failed\n"); if (ACPI_FAILURE(AcpiEnableEvent(sc->ec_gpebit, ACPI_EVENT_GPE, 0))) printf("EcGpeQueryHandler:EnableEvent Failed\n"); return_VOID; }