OsdInterrupt.c revision 217278
1210006Srdivacky/*- 2210006Srdivacky * Copyright (c) 2000 Michael Smith 3210006Srdivacky * Copyright (c) 2000 BSDi 4210006Srdivacky * Copyright (c) 2011 Jung-uk Kim <jkim@FreeBSD.org> 5210006Srdivacky * All rights reserved. 6210006Srdivacky * 7210006Srdivacky * Redistribution and use in source and binary forms, with or without 8210006Srdivacky * modification, are permitted provided that the following conditions 9210006Srdivacky * are met: 10210006Srdivacky * 1. Redistributions of source code must retain the above copyright 11210006Srdivacky * notice, this list of conditions and the following disclaimer. 12210006Srdivacky * 2. Redistributions in binary form must reproduce the above copyright 13210006Srdivacky * notice, this list of conditions and the following disclaimer in the 14210006Srdivacky * documentation and/or other materials provided with the distribution. 15210006Srdivacky * 16249423Sdim * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17249423Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18249423Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19249423Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20249423Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21249423Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22249423Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23210006Srdivacky * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24210006Srdivacky * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25210006Srdivacky * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26210006Srdivacky * SUCH DAMAGE. 27210006Srdivacky */ 28210006Srdivacky 29210006Srdivacky/* 30210006Srdivacky * 6.5 : Interrupt handling 31210006Srdivacky */ 32210006Srdivacky 33210006Srdivacky#include <sys/cdefs.h> 34210006Srdivacky__FBSDID("$FreeBSD: head/sys/dev/acpica/Osd/OsdInterrupt.c 217278 2011-01-11 19:20:01Z jkim $"); 35210006Srdivacky 36223017Sdim#include <sys/param.h> 37210006Srdivacky#include <sys/kernel.h> 38210006Srdivacky#include <sys/bus.h> 39210006Srdivacky#include <sys/lock.h> 40210006Srdivacky#include <sys/malloc.h> 41210006Srdivacky#include <sys/mutex.h> 42210006Srdivacky#include <machine/bus.h> 43210006Srdivacky#include <machine/resource.h> 44210006Srdivacky#include <sys/rman.h> 45210006Srdivacky 46210006Srdivacky#include <contrib/dev/acpica/include/acpi.h> 47223017Sdim#include <contrib/dev/acpica/include/accommon.h> 48210006Srdivacky 49210006Srdivacky#include <dev/acpica/acpivar.h> 50210006Srdivacky 51210006Srdivacky#define _COMPONENT ACPI_OS_SERVICES 52210006SrdivackyACPI_MODULE_NAME("INTERRUPT") 53210006Srdivacky 54210006SrdivackyMALLOC_DEFINE(M_ACPIINTR, "acpiintr", "ACPI interrupt"); 55210006Srdivacky 56210006Srdivackystruct acpi_intr { 57243830Sdim SLIST_ENTRY(acpi_intr) ai_link; 58249423Sdim struct resource *ai_irq; 59210006Srdivacky int ai_rid; 60249423Sdim void *ai_handle; 61210006Srdivacky int ai_number; 62249423Sdim ACPI_OSD_HANDLER ai_handler; 63249423Sdim void *ai_context; 64249423Sdim}; 65226633Sdimstatic SLIST_HEAD(, acpi_intr) acpi_intr_list = 66210006Srdivacky SLIST_HEAD_INITIALIZER(acpi_intr_list); 67210006Srdivackystatic struct mtx acpi_intr_lock; 68210006Srdivacky 69210006Srdivackystatic UINT32 InterruptOverride; 70210006Srdivacky 71249423Sdimstatic void 72210006Srdivackyacpi_intr_init(struct mtx *lock) 73210006Srdivacky{ 74249423Sdim 75210006Srdivacky mtx_init(lock, "ACPI interrupt lock", NULL, MTX_DEF); 76210006Srdivacky} 77210006Srdivacky 78210006SrdivackySYSINIT(acpi_intr, SI_SUB_DRIVERS, SI_ORDER_FIRST, acpi_intr_init, 79210006Srdivacky &acpi_intr_lock); 80210006Srdivacky 81210006Srdivackystatic int 82210006Srdivackyacpi_intr_handler(void *arg) 83210006Srdivacky{ 84210006Srdivacky struct acpi_intr *ai; 85210006Srdivacky 86210006Srdivacky ai = arg; 87210006Srdivacky KASSERT(ai != NULL && ai->ai_handler != NULL, 88210006Srdivacky ("invalid ACPI interrupt handler")); 89226633Sdim if (ai->ai_handler(ai->ai_context) == ACPI_INTERRUPT_HANDLED) 90210006Srdivacky return (FILTER_HANDLED); 91210006Srdivacky return (FILTER_STRAY); 92210006Srdivacky} 93210006Srdivacky 94210006Srdivackystatic void 95210006Srdivackyacpi_intr_destroy(device_t dev, struct acpi_intr *ai) 96210006Srdivacky{ 97210006Srdivacky 98210006Srdivacky if (ai->ai_handle != NULL) 99210006Srdivacky bus_teardown_intr(dev, ai->ai_irq, ai->ai_handle); 100210006Srdivacky if (ai->ai_irq != NULL) 101210006Srdivacky bus_release_resource(dev, SYS_RES_IRQ, ai->ai_rid, ai->ai_irq); 102210006Srdivacky bus_delete_resource(dev, SYS_RES_IRQ, ai->ai_rid); 103210006Srdivacky free(ai, M_ACPIINTR); 104210006Srdivacky} 105210006Srdivacky 106210006SrdivackyACPI_STATUS 107210006SrdivackyAcpiOsInstallInterruptHandler(UINT32 InterruptNumber, 108210006Srdivacky ACPI_OSD_HANDLER ServiceRoutine, void *Context) 109210006Srdivacky{ 110210006Srdivacky struct acpi_softc *sc; 111210006Srdivacky struct acpi_intr *ai, *ap; 112210006Srdivacky 113210006Srdivacky ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 114210006Srdivacky 115210006Srdivacky sc = devclass_get_softc(devclass_find("acpi"), 0); 116210006Srdivacky KASSERT(sc != NULL && sc->acpi_dev != NULL, 117210006Srdivacky ("can't find ACPI device to register interrupt")); 118210006Srdivacky 119210006Srdivacky if (InterruptNumber > 255 || ServiceRoutine == NULL) 120210006Srdivacky return_ACPI_STATUS (AE_BAD_PARAMETER); 121210006Srdivacky 122210006Srdivacky ai = malloc(sizeof(*ai), M_ACPIINTR, M_WAITOK | M_ZERO); 123210006Srdivacky mtx_lock(&acpi_intr_lock); 124210006Srdivacky SLIST_FOREACH(ap, &acpi_intr_list, ai_link) { 125210006Srdivacky if (InterruptNumber == ap->ai_number || 126210006Srdivacky (InterruptNumber == InterruptOverride && 127210006Srdivacky InterruptNumber != AcpiGbl_FADT.SciInterrupt)) { 128210006Srdivacky mtx_unlock(&acpi_intr_lock); 129210006Srdivacky free(ai, M_ACPIINTR); 130210006Srdivacky return_ACPI_STATUS (AE_ALREADY_EXISTS); 131210006Srdivacky } 132210006Srdivacky if (ai->ai_rid <= ap->ai_rid) 133210006Srdivacky ai->ai_rid = ap->ai_rid + 1; 134210006Srdivacky } 135234353Sdim ai->ai_number = InterruptNumber; 136234353Sdim ai->ai_handler = ServiceRoutine; 137234353Sdim ai->ai_context = Context; 138234353Sdim SLIST_INSERT_HEAD(&acpi_intr_list, ai, ai_link); 139210006Srdivacky mtx_unlock(&acpi_intr_lock); 140210006Srdivacky 141210006Srdivacky /* 142234353Sdim * If the MADT contained an interrupt override directive for the SCI, 143234353Sdim * we use that value instead of the one from the FADT. 144210006Srdivacky */ 145210006Srdivacky if (InterruptOverride != 0 && 146210006Srdivacky InterruptNumber == AcpiGbl_FADT.SciInterrupt) { 147218893Sdim device_printf(sc->acpi_dev, 148210006Srdivacky "Overriding SCI from IRQ %u to IRQ %u\n", 149226633Sdim InterruptNumber, InterruptOverride); 150210006Srdivacky InterruptNumber = InterruptOverride; 151210006Srdivacky } 152210006Srdivacky 153210006Srdivacky /* Set up the interrupt resource. */ 154210006Srdivacky bus_set_resource(sc->acpi_dev, SYS_RES_IRQ, ai->ai_rid, 155210006Srdivacky InterruptNumber, 1); 156210006Srdivacky ai->ai_irq = bus_alloc_resource_any(sc->acpi_dev, SYS_RES_IRQ, 157210006Srdivacky &ai->ai_rid, RF_SHAREABLE | RF_ACTIVE); 158210006Srdivacky if (ai->ai_irq == NULL) { 159210006Srdivacky device_printf(sc->acpi_dev, "could not allocate interrupt\n"); 160210006Srdivacky goto error; 161210006Srdivacky } 162210006Srdivacky if (bus_setup_intr(sc->acpi_dev, ai->ai_irq, 163210006Srdivacky INTR_TYPE_MISC | INTR_MPSAFE, acpi_intr_handler, NULL, ai, 164210006Srdivacky &ai->ai_handle) != 0) { 165210006Srdivacky device_printf(sc->acpi_dev, "could not set up interrupt\n"); 166210006Srdivacky goto error; 167210006Srdivacky } 168226633Sdim return_ACPI_STATUS (AE_OK); 169226633Sdim 170210006Srdivackyerror: 171234353Sdim mtx_lock(&acpi_intr_lock); 172234353Sdim SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 173210006Srdivacky mtx_unlock(&acpi_intr_lock); 174234353Sdim acpi_intr_destroy(sc->acpi_dev, ai); 175210006Srdivacky return_ACPI_STATUS (AE_ALREADY_EXISTS); 176210006Srdivacky} 177210006Srdivacky 178226633SdimACPI_STATUS 179226633SdimAcpiOsRemoveInterruptHandler(UINT32 InterruptNumber, 180234353Sdim ACPI_OSD_HANDLER ServiceRoutine) 181234353Sdim{ 182210006Srdivacky struct acpi_softc *sc; 183234353Sdim struct acpi_intr *ai; 184210006Srdivacky 185210006Srdivacky ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 186210006Srdivacky 187210006Srdivacky sc = devclass_get_softc(devclass_find("acpi"), 0); 188210006Srdivacky KASSERT(sc != NULL && sc->acpi_dev != NULL, 189210006Srdivacky ("can't find ACPI device to deregister interrupt")); 190210006Srdivacky 191210006Srdivacky if (InterruptNumber > 255 || ServiceRoutine == NULL) 192210006Srdivacky return_ACPI_STATUS (AE_BAD_PARAMETER); 193210006Srdivacky mtx_lock(&acpi_intr_lock); 194210006Srdivacky SLIST_FOREACH(ai, &acpi_intr_list, ai_link) 195210006Srdivacky if (InterruptNumber == ai->ai_number) { 196210006Srdivacky if (ServiceRoutine != ai->ai_handler) { 197210006Srdivacky mtx_unlock(&acpi_intr_lock); 198210006Srdivacky return_ACPI_STATUS (AE_BAD_PARAMETER); 199210006Srdivacky } 200210006Srdivacky SLIST_REMOVE(&acpi_intr_list, ai, acpi_intr, ai_link); 201210006Srdivacky break; 202210006Srdivacky } 203210006Srdivacky mtx_unlock(&acpi_intr_lock); 204210006Srdivacky if (ai == NULL) 205210006Srdivacky return_ACPI_STATUS (AE_NOT_EXIST); 206210006Srdivacky acpi_intr_destroy(sc->acpi_dev, ai); 207210006Srdivacky return_ACPI_STATUS (AE_OK); 208210006Srdivacky} 209210006Srdivacky 210210006SrdivackyACPI_STATUS 211210006Srdivackyacpi_OverrideInterruptLevel(UINT32 InterruptNumber) 212210006Srdivacky{ 213210006Srdivacky 214210006Srdivacky ACPI_FUNCTION_TRACE((char *)(uintptr_t)__func__); 215210006Srdivacky 216210006Srdivacky if (InterruptOverride != 0) 217210006Srdivacky return_ACPI_STATUS (AE_ALREADY_EXISTS); 218210006Srdivacky InterruptOverride = InterruptNumber; 219210006Srdivacky return_ACPI_STATUS (AE_OK); 220210006Srdivacky} 221210006Srdivacky