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$");
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