OsdInterrupt.c revision 217240
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 217240 2011-01-10 21:01:41Z 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 107217238Sjkim if (InterruptNumber < 0 || InterruptNumber > 255 || 108217238Sjkim ServiceRoutine == NULL) 109217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 11067760Smsmith 111217238Sjkim ai = malloc(sizeof(*ai), M_ACPIINTR, M_WAITOK | M_ZERO); 112217238Sjkim mtx_lock(&acpi_intr_lock); 113217238Sjkim SLIST_FOREACH(ap, &acpi_intr_list, ai_link) { 114217238Sjkim if (InterruptNumber == ap->ai_number || 115217238Sjkim (InterruptNumber == InterruptOverride && 116217238Sjkim InterruptNumber != AcpiGbl_FADT.SciInterrupt)) { 117217238Sjkim mtx_unlock(&acpi_intr_lock); 118217240Sjkim free(ai, M_ACPIINTR); 119217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 120217238Sjkim } 121217238Sjkim if (ai->ai_rid <= ap->ai_rid) 122217238Sjkim ai->ai_rid = ap->ai_rid + 1; 123217238Sjkim } 124217238Sjkim ai->ai_number = InterruptNumber; 125217238Sjkim ai->ai_handler = ServiceRoutine; 126217238Sjkim ai->ai_context = Context; 127128226Snjl 128217238Sjkim /* 129217238Sjkim * If the MADT contained an interrupt override directive for the SCI, 130217238Sjkim * we use that value instead of the one from the FADT. 131217238Sjkim */ 132217238Sjkim if (InterruptOverride != 0 && 133217238Sjkim InterruptNumber == AcpiGbl_FADT.SciInterrupt) { 134217238Sjkim device_printf(sc->acpi_dev, 135217238Sjkim "Overriding SCI from IRQ %u to IRQ %u\n", 136217238Sjkim InterruptNumber, InterruptOverride); 137217238Sjkim InterruptNumber = InterruptOverride; 138217238Sjkim } 139128226Snjl 140217238Sjkim /* Set up the interrupt resource. */ 141217238Sjkim bus_set_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 142217238Sjkim InterruptNumber, 1); 143217238Sjkim ai->ai_irq = bus_alloc_resource_any(sc->acpi_dev, SYS_RES_IRQ, 144217238Sjkim &ai->ai_rid, RF_SHAREABLE | RF_ACTIVE); 145217238Sjkim if (ai->ai_irq == NULL) { 146217238Sjkim device_printf(sc->acpi_dev, "could not allocate interrupt\n"); 147217238Sjkim goto error; 148217238Sjkim } 149217238Sjkim if (bus_setup_intr(sc->acpi_dev, ai->ai_irq, 150217238Sjkim INTR_TYPE_MISC | INTR_MPSAFE, acpi_intr_handler, NULL, ai, 151217238Sjkim &ai->ai_handle) != 0) { 152217238Sjkim device_printf(sc->acpi_dev, "could not set up interrupt\n"); 153217238Sjkim goto error; 154217238Sjkim } 155217238Sjkim SLIST_INSERT_HEAD(&acpi_intr_list, ai, ai_link); 156217238Sjkim mtx_unlock(&acpi_intr_lock); 157217238Sjkim return_ACPI_STATUS (AE_OK); 158128226Snjl 159128226Snjlerror: 160217238Sjkim mtx_unlock(&acpi_intr_lock); 161217238Sjkim if (ai->ai_handle != NULL) 162217238Sjkim bus_teardown_intr(sc->acpi_dev, ai->ai_irq, ai->ai_handle); 163217238Sjkim if (ai->ai_irq != NULL) 164217238Sjkim bus_release_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 165217238Sjkim ai->ai_irq); 166217238Sjkim bus_delete_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid); 167217238Sjkim free(ai, M_ACPIINTR); 168217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 16967760Smsmith} 17067760Smsmith 17167760SmsmithACPI_STATUS 172217238SjkimAcpiOsRemoveInterruptHandler(UINT32 InterruptNumber, 173217238Sjkim ACPI_OSD_HANDLER ServiceRoutine) 17467760Smsmith{ 175217238Sjkim struct acpi_softc *sc; 176217238Sjkim struct acpi_intr *ai; 17767760Smsmith 178217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 17971875Smsmith 180217238Sjkim sc = devclass_get_softc(devclass_find("acpi"), 0); 181217238Sjkim KASSERT(sc != NULL && sc->acpi_dev != NULL, 182217238Sjkim ("can't find ACPI device to deregister interrupt")); 18367760Smsmith 184217238Sjkim if (InterruptNumber < 0 || InterruptNumber > 255 || 185217238Sjkim ServiceRoutine == NULL) 186217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 187217238Sjkim mtx_lock(&acpi_intr_lock); 188217238Sjkim SLIST_FOREACH(ai, &acpi_intr_list, ai_link) 189217238Sjkim if (InterruptNumber == ai->ai_number) { 190217238Sjkim if (ServiceRoutine != ai->ai_handler) { 191217238Sjkim mtx_unlock(&acpi_intr_lock); 192217238Sjkim return_ACPI_STATUS (AE_BAD_PARAMETER); 193217238Sjkim } 194217238Sjkim SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 195217238Sjkim break; 196217238Sjkim } 197217238Sjkim mtx_unlock(&acpi_intr_lock); 198217238Sjkim if (ai == NULL) 199217238Sjkim return_ACPI_STATUS (AE_NOT_EXIST); 200217238Sjkim bus_teardown_intr(sc->acpi_dev, ai->ai_irq, ai->ai_handle); 201217238Sjkim bus_release_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, ai->ai_irq); 202217238Sjkim bus_delete_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid); 203217238Sjkim free(ai, M_ACPIINTR); 204217238Sjkim return_ACPI_STATUS (AE_OK); 20567760Smsmith} 20667760Smsmith 207122500SjhbACPI_STATUS 208122500Sjhbacpi_OverrideInterruptLevel(UINT32 InterruptNumber) 209122500Sjhb{ 210122500Sjhb 211217238Sjkim ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 212122564Sjhb 213217238Sjkim if (InterruptOverride != 0) 214217238Sjkim return_ACPI_STATUS (AE_ALREADY_EXISTS); 215217238Sjkim InterruptOverride = InterruptNumber; 216217238Sjkim return_ACPI_STATUS (AE_OK); 217122500Sjhb} 218