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