167760Smsmith/*- 267760Smsmith * Copyright (c) 2000 Michael Smith 367760Smsmith * Copyright (c) 2000 BSDi 4217238Sjkim * Copyright (c) 2011 Jung-uk Kim <jkim@FreeBSD.org> 567760Smsmith * All rights reserved. 667760Smsmith * 767760Smsmith * Redistribution and use in source and binary forms, with or without 867760Smsmith * modification, are permitted provided that the following conditions 967760Smsmith * are met: 1067760Smsmith * 1. Redistributions of source code must retain the above copyright 1167760Smsmith * notice, this list of conditions and the following disclaimer. 1267760Smsmith * 2. Redistributions in binary form must reproduce the above copyright 1367760Smsmith * notice, this list of conditions and the following disclaimer in the 1467760Smsmith * documentation and/or other materials provided with the distribution. 1567760Smsmith * 1667760Smsmith * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1767760Smsmith * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1867760Smsmith * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1967760Smsmith * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2067760Smsmith * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2167760Smsmith * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2267760Smsmith * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2367760Smsmith * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2467760Smsmith * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2567760Smsmith * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2667760Smsmith * SUCH DAMAGE. 2767760Smsmith */ 2867760Smsmith 2967760Smsmith/* 3067760Smsmith * 6.5 : Interrupt handling 3167760Smsmith */ 3267760Smsmith 33148318Snjl#include <sys/cdefs.h> 34148318Snjl__FBSDID("$FreeBSD: releng/10.3/sys/dev/acpica/Osd/OsdInterrupt.c 227293 2011-11-07 06:44:47Z ed $"); 35148318Snjl 36128226Snjl#include <sys/param.h> 37128226Snjl#include <sys/kernel.h> 3867760Smsmith#include <sys/bus.h> 39217238Sjkim#include <sys/lock.h> 40217238Sjkim#include <sys/malloc.h> 41217238Sjkim#include <sys/mutex.h> 42128226Snjl#include <machine/bus.h> 4367760Smsmith#include <machine/resource.h> 4467760Smsmith#include <sys/rman.h> 45217238Sjkim 46193530Sjkim#include <contrib/dev/acpica/include/acpi.h> 47193530Sjkim#include <contrib/dev/acpica/include/accommon.h> 48193530Sjkim 4967760Smsmith#include <dev/acpica/acpivar.h> 5067760Smsmith 5177432Smsmith#define _COMPONENT ACPI_OS_SERVICES 5291128SmsmithACPI_MODULE_NAME("INTERRUPT") 5371875Smsmith 54227293Sedstatic MALLOC_DEFINE(M_ACPIINTR, "acpiintr", "ACPI interrupt"); 5579000Smsmith 56217238Sjkimstruct acpi_intr { 57217238Sjkim SLIST_ENTRY(acpi_intr) ai_link; 58217238Sjkim struct resource *ai_irq; 59217238Sjkim int ai_rid; 60217238Sjkim void *ai_handle; 61217238Sjkim int ai_number; 62217238Sjkim ACPI_OSD_HANDLER ai_handler; 63217238Sjkim void *ai_context; 64217238Sjkim}; 65217238Sjkimstatic SLIST_HEAD(, acpi_intr) acpi_intr_list = 66217238Sjkim SLIST_HEAD_INITIALIZER(acpi_intr_list); 67217238Sjkimstatic struct mtx acpi_intr_lock; 68217238Sjkim 69217238Sjkimstatic UINT32 InterruptOverride; 70217238Sjkim 71217238Sjkimstatic void 72217238Sjkimacpi_intr_init(struct mtx *lock) 73217238Sjkim{ 74217238Sjkim 75217238Sjkim mtx_init(lock, "ACPI interrupt lock", NULL, MTX_DEF); 76217238Sjkim} 77217238Sjkim 78217238SjkimSYSINIT(acpi_intr, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_intr_init, 79217238Sjkim &acpi_intr_lock); 80217238Sjkim 81217238Sjkimstatic int 82217238Sjkimacpi_intr_handler(void *arg) 83217238Sjkim{ 84217238Sjkim struct acpi_intr *ai; 85217238Sjkim 86217238Sjkim ai = arg; 87217238Sjkim KASSERT(ai != NULL && ai->ai_handler != NULL, 88217238Sjkim ("invalid ACPI interrupt handler")); 89217238Sjkim if (ai->ai_handler(ai->ai_context) == ACPI_INTERRUPT_HANDLED) 90217238Sjkim return (FILTER_HANDLED); 91217238Sjkim return (FILTER_STRAY); 92217238Sjkim} 93217238Sjkim 94217278Sjkimstatic void 95217278Sjkimacpi_intr_destroy(device_t dev, struct acpi_intr *ai) 96217278Sjkim{ 97217278Sjkim 98217278Sjkim if (ai->ai_handle != NULL) 99217278Sjkim bus_teardown_intr(dev, ai->ai_irq, ai->ai_handle); 100217278Sjkim if (ai->ai_irq != NULL) 101217278Sjkim bus_release_resource(dev, SYS_RES_IRQ, ai->ai_rid, ai->ai_irq); 102217278Sjkim bus_delete_resource(dev, SYS_RES_IRQ, ai->ai_rid); 103217278Sjkim free(ai, M_ACPIINTR); 104217278Sjkim} 105217278Sjkim 10667760SmsmithACPI_STATUS 107128226SnjlAcpiOsInstallInterruptHandler(UINT32 InterruptNumber, 108138300Smarks ACPI_OSD_HANDLER ServiceRoutine, void *Context) 10967760Smsmith{ 110217238Sjkim struct acpi_softc *sc; 111217238Sjkim struct acpi_intr *ai, *ap; 11267760Smsmith 113217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 11471875Smsmith 115217238Sjkim sc = devclass_get_softc(devclass_find("acpi"), 0); 116217238Sjkim KASSERT(sc != NULL && sc->acpi_dev != NULL, 117217238Sjkim ("can't find ACPI device to register interrupt")); 11879386Smsmith 119217241Sjkim if (InterruptNumber > 255 || ServiceRoutine == NULL) 120217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 12167760Smsmith 122217238Sjkim ai = malloc(sizeof(*ai), M_ACPIINTR, M_WAITOK | M_ZERO); 123217238Sjkim mtx_lock(&acpi_intr_lock); 124217238Sjkim SLIST_FOREACH(ap, &acpi_intr_list, ai_link) { 125217238Sjkim if (InterruptNumber == ap->ai_number || 126217238Sjkim (InterruptNumber == InterruptOverride && 127217238Sjkim InterruptNumber != AcpiGbl_FADT.SciInterrupt)) { 128217238Sjkim mtx_unlock(&acpi_intr_lock); 129217240Sjkim free(ai, M_ACPIINTR); 130217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 131217238Sjkim } 132217238Sjkim if (ai->ai_rid <= ap->ai_rid) 133217238Sjkim ai->ai_rid = ap->ai_rid + 1; 134217238Sjkim } 135217238Sjkim ai->ai_number = InterruptNumber; 136217238Sjkim ai->ai_handler = ServiceRoutine; 137217238Sjkim ai->ai_context = Context; 138217278Sjkim SLIST_INSERT_HEAD(&acpi_intr_list, ai, ai_link); 139217278Sjkim mtx_unlock(&acpi_intr_lock); 140128226Snjl 141217238Sjkim /* 142217238Sjkim * If the MADT contained an interrupt override directive for the SCI, 143217238Sjkim * we use that value instead of the one from the FADT. 144217238Sjkim */ 145217238Sjkim if (InterruptOverride != 0 && 146217238Sjkim InterruptNumber == AcpiGbl_FADT.SciInterrupt) { 147217238Sjkim device_printf(sc->acpi_dev, 148217238Sjkim "Overriding SCI from IRQ %u to IRQ %u\n", 149217238Sjkim InterruptNumber, InterruptOverride); 150217238Sjkim InterruptNumber = InterruptOverride; 151217238Sjkim } 152128226Snjl 153217238Sjkim /* Set up the interrupt resource. */ 154217238Sjkim bus_set_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 155217238Sjkim InterruptNumber, 1); 156217238Sjkim ai->ai_irq = bus_alloc_resource_any(sc->acpi_dev, SYS_RES_IRQ, 157217238Sjkim &ai->ai_rid, RF_SHAREABLE | RF_ACTIVE); 158217238Sjkim if (ai->ai_irq == NULL) { 159217238Sjkim device_printf(sc->acpi_dev, "could not allocate interrupt\n"); 160217238Sjkim goto error; 161217238Sjkim } 162217238Sjkim if (bus_setup_intr(sc->acpi_dev, ai->ai_irq, 163217238Sjkim INTR_TYPE_MISC | INTR_MPSAFE, acpi_intr_handler, NULL, ai, 164217238Sjkim &ai->ai_handle) != 0) { 165217238Sjkim device_printf(sc->acpi_dev, "could not set up interrupt\n"); 166217238Sjkim goto error; 167217238Sjkim } 168217238Sjkim return_ACPI_STATUS (AE_OK); 169128226Snjl 170128226Snjlerror: 171217278Sjkim mtx_lock(&acpi_intr_lock); 172217278Sjkim SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 173217238Sjkim mtx_unlock(&acpi_intr_lock); 174217278Sjkim acpi_intr_destroy(sc->acpi_dev, ai); 175217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 17667760Smsmith} 17767760Smsmith 17867760SmsmithACPI_STATUS 179217238SjkimAcpiOsRemoveInterruptHandler(UINT32 InterruptNumber, 180217238Sjkim ACPI_OSD_HANDLER ServiceRoutine) 18167760Smsmith{ 182217238Sjkim struct acpi_softc *sc; 183217238Sjkim struct acpi_intr *ai; 18467760Smsmith 185217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 18671875Smsmith 187217238Sjkim sc = devclass_get_softc(devclass_find("acpi"), 0); 188217238Sjkim KASSERT(sc != NULL && sc->acpi_dev != NULL, 189217238Sjkim ("can't find ACPI device to deregister interrupt")); 19067760Smsmith 191217241Sjkim if (InterruptNumber > 255 || ServiceRoutine == NULL) 192217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 193217238Sjkim mtx_lock(&acpi_intr_lock); 194217238Sjkim SLIST_FOREACH(ai, &acpi_intr_list, ai_link) 195217238Sjkim if (InterruptNumber == ai->ai_number) { 196217238Sjkim if (ServiceRoutine != ai->ai_handler) { 197217238Sjkim mtx_unlock(&acpi_intr_lock); 198217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 199217238Sjkim } 200217238Sjkim SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 201217238Sjkim break; 202217238Sjkim } 203217238Sjkim mtx_unlock(&acpi_intr_lock); 204217238Sjkim if (ai == NULL) 205217238Sjkim return_ACPI_STATUS (AE_NOT_EXIST); 206217278Sjkim acpi_intr_destroy(sc->acpi_dev, ai); 207217238Sjkim return_ACPI_STATUS (AE_OK); 20867760Smsmith} 20967760Smsmith 210122500SjhbACPI_STATUS 211122500Sjhbacpi_OverrideInterruptLevel(UINT32 InterruptNumber) 212122500Sjhb{ 213122500Sjhb 214217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 215122564Sjhb 216217238Sjkim if (InterruptOverride != 0) 217217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 218217238Sjkim InterruptOverride = InterruptNumber; 219217238Sjkim return_ACPI_STATUS (AE_OK); 220122500Sjhb} 221