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