OsdInterrupt.c revision 217241
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: head/sys/dev/acpica/Osd/OsdInterrupt.c 217241 2011-01-10 21:09:38Z jkim $"); 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 54217238SjkimMALLOC_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 9467760SmsmithACPI_STATUS 95128226SnjlAcpiOsInstallInterruptHandler(UINT32 InterruptNumber, 96138300Smarks ACPI_OSD_HANDLER ServiceRoutine, void *Context) 9767760Smsmith{ 98217238Sjkim struct acpi_softc *sc; 99217238Sjkim struct acpi_intr *ai, *ap; 10067760Smsmith 101217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 10271875Smsmith 103217238Sjkim sc = devclass_get_softc(devclass_find("acpi"), 0); 104217238Sjkim KASSERT(sc != NULL && sc->acpi_dev != NULL, 105217238Sjkim ("can't find ACPI device to register interrupt")); 10679386Smsmith 107217241Sjkim if (InterruptNumber > 255 || ServiceRoutine == NULL) 108217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 10967760Smsmith 110217238Sjkim ai = malloc(sizeof(*ai), M_ACPIINTR, M_WAITOK | M_ZERO); 111217238Sjkim mtx_lock(&acpi_intr_lock); 112217238Sjkim SLIST_FOREACH(ap, &acpi_intr_list, ai_link) { 113217238Sjkim if (InterruptNumber == ap->ai_number || 114217238Sjkim (InterruptNumber == InterruptOverride && 115217238Sjkim InterruptNumber != AcpiGbl_FADT.SciInterrupt)) { 116217238Sjkim mtx_unlock(&acpi_intr_lock); 117217240Sjkim free(ai, M_ACPIINTR); 118217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 119217238Sjkim } 120217238Sjkim if (ai->ai_rid <= ap->ai_rid) 121217238Sjkim ai->ai_rid = ap->ai_rid + 1; 122217238Sjkim } 123217238Sjkim ai->ai_number = InterruptNumber; 124217238Sjkim ai->ai_handler = ServiceRoutine; 125217238Sjkim ai->ai_context = Context; 126128226Snjl 127217238Sjkim /* 128217238Sjkim * If the MADT contained an interrupt override directive for the SCI, 129217238Sjkim * we use that value instead of the one from the FADT. 130217238Sjkim */ 131217238Sjkim if (InterruptOverride != 0 && 132217238Sjkim InterruptNumber == AcpiGbl_FADT.SciInterrupt) { 133217238Sjkim device_printf(sc->acpi_dev, 134217238Sjkim "Overriding SCI from IRQ %u to IRQ %u\n", 135217238Sjkim InterruptNumber, InterruptOverride); 136217238Sjkim InterruptNumber = InterruptOverride; 137217238Sjkim } 138128226Snjl 139217238Sjkim /* Set up the interrupt resource. */ 140217238Sjkim bus_set_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 141217238Sjkim InterruptNumber, 1); 142217238Sjkim ai->ai_irq = bus_alloc_resource_any(sc->acpi_dev, SYS_RES_IRQ, 143217238Sjkim &ai->ai_rid, RF_SHAREABLE | RF_ACTIVE); 144217238Sjkim if (ai->ai_irq == NULL) { 145217238Sjkim device_printf(sc->acpi_dev, "could not allocate interrupt\n"); 146217238Sjkim goto error; 147217238Sjkim } 148217238Sjkim if (bus_setup_intr(sc->acpi_dev, ai->ai_irq, 149217238Sjkim INTR_TYPE_MISC | INTR_MPSAFE, acpi_intr_handler, NULL, ai, 150217238Sjkim &ai->ai_handle) != 0) { 151217238Sjkim device_printf(sc->acpi_dev, "could not set up interrupt\n"); 152217238Sjkim goto error; 153217238Sjkim } 154217238Sjkim SLIST_INSERT_HEAD(&acpi_intr_list, ai, ai_link); 155217238Sjkim mtx_unlock(&acpi_intr_lock); 156217238Sjkim return_ACPI_STATUS (AE_OK); 157128226Snjl 158128226Snjlerror: 159217238Sjkim mtx_unlock(&acpi_intr_lock); 160217238Sjkim if (ai->ai_handle != NULL) 161217238Sjkim bus_teardown_intr(sc->acpi_dev, ai->ai_irq, ai->ai_handle); 162217238Sjkim if (ai->ai_irq != NULL) 163217238Sjkim bus_release_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 164217238Sjkim ai->ai_irq); 165217238Sjkim bus_delete_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid); 166217238Sjkim free(ai, M_ACPIINTR); 167217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 16867760Smsmith} 16967760Smsmith 17067760SmsmithACPI_STATUS 171217238SjkimAcpiOsRemoveInterruptHandler(UINT32 InterruptNumber, 172217238Sjkim ACPI_OSD_HANDLER ServiceRoutine) 17367760Smsmith{ 174217238Sjkim struct acpi_softc *sc; 175217238Sjkim struct acpi_intr *ai; 17667760Smsmith 177217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 17871875Smsmith 179217238Sjkim sc = devclass_get_softc(devclass_find("acpi"), 0); 180217238Sjkim KASSERT(sc != NULL && sc->acpi_dev != NULL, 181217238Sjkim ("can't find ACPI device to deregister interrupt")); 18267760Smsmith 183217241Sjkim if (InterruptNumber > 255 || ServiceRoutine == NULL) 184217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 185217238Sjkim mtx_lock(&acpi_intr_lock); 186217238Sjkim SLIST_FOREACH(ai, &acpi_intr_list, ai_link) 187217238Sjkim if (InterruptNumber == ai->ai_number) { 188217238Sjkim if (ServiceRoutine != ai->ai_handler) { 189217238Sjkim mtx_unlock(&acpi_intr_lock); 190217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 191217238Sjkim } 192217238Sjkim SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 193217238Sjkim break; 194217238Sjkim } 195217238Sjkim mtx_unlock(&acpi_intr_lock); 196217238Sjkim if (ai == NULL) 197217238Sjkim return_ACPI_STATUS (AE_NOT_EXIST); 198217238Sjkim bus_teardown_intr(sc->acpi_dev, ai->ai_irq, ai->ai_handle); 199217238Sjkim bus_release_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, ai->ai_irq); 200217238Sjkim bus_delete_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid); 201217238Sjkim free(ai, M_ACPIINTR); 202217238Sjkim return_ACPI_STATUS (AE_OK); 20367760Smsmith} 20467760Smsmith 205122500SjhbACPI_STATUS 206122500Sjhbacpi_OverrideInterruptLevel(UINT32 InterruptNumber) 207122500Sjhb{ 208122500Sjhb 209217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 210122564Sjhb 211217238Sjkim if (InterruptOverride != 0) 212217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 213217238Sjkim InterruptOverride = InterruptNumber; 214217238Sjkim return_ACPI_STATUS (AE_OK); 215122500Sjhb} 216