1/*-
2 * Copyright (c) 2022 Takanori Watanabe
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27#include "opt_acpi.h"
28
29#include <sys/param.h>
30#include <sys/bus.h>
31#include <sys/kernel.h>
32#include <sys/malloc.h>
33#include <sys/module.h>
34#include <sys/rman.h>
35
36#include <contrib/dev/acpica/include/acpi.h>
37#include <contrib/dev/acpica/include/accommon.h>
38#include <dev/acpica/acpivar.h>
39
40/* Hooks for the ACPI CA debugging infrastructure */
41#define _COMPONENT ACPI_BUS
42ACPI_MODULE_NAME("GED")
43
44static MALLOC_DEFINE(M_ACPIGED, "acpiged", "ACPI Generic event data");
45
46struct acpi_ged_event {
47	device_t dev;
48	struct resource *r;
49	int rid;
50	void *cookie;
51	ACPI_HANDLE ah;
52	ACPI_OBJECT_LIST args;
53	ACPI_OBJECT arg1;
54};
55
56struct acpi_ged_softc {
57	int numevts;
58	struct acpi_ged_event *evts;
59};
60
61static int acpi_ged_probe(device_t dev);
62static int acpi_ged_attach(device_t dev);
63static int acpi_ged_detach(device_t dev);
64
65static char *ged_ids[] = { "ACPI0013", NULL };
66
67static device_method_t acpi_ged_methods[] = {
68	/* Device interface */
69	DEVMETHOD(device_probe, acpi_ged_probe),
70	DEVMETHOD(device_attach, acpi_ged_attach),
71	DEVMETHOD(device_detach, acpi_ged_detach),
72	DEVMETHOD_END
73};
74
75static driver_t acpi_ged_driver = {
76	"acpi_ged",
77	acpi_ged_methods,
78	sizeof(struct acpi_ged_softc),
79};
80
81DRIVER_MODULE(acpi_ged, acpi, acpi_ged_driver, 0, 0);
82MODULE_DEPEND(acpi_ged, acpi, 1, 1, 1);
83
84static int acpi_ged_defer;
85SYSCTL_INT(_debug_acpi, OID_AUTO, ged_defer, CTLFLAG_RWTUN,
86    &acpi_ged_defer, 0,
87    "Handle ACPI GED via a task, rather than in the ISR");
88
89static void
90acpi_ged_evt(void *arg)
91{
92	struct acpi_ged_event *evt = arg;
93
94	AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
95}
96
97static void
98acpi_ged_intr(void *arg)
99{
100	struct acpi_ged_event *evt = arg;
101
102	if (acpi_ged_defer)
103		AcpiOsExecute(OSL_GPE_HANDLER, acpi_ged_evt, arg);
104	else
105		AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
106}
107static int
108acpi_ged_probe(device_t dev)
109{
110	int rv;
111
112	if (acpi_disabled("ged"))
113		return (ENXIO);
114	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ged_ids, NULL);
115	if (rv > 0)
116		return (ENXIO);
117
118	device_set_desc(dev, "Generic Event Device");
119	return (rv);
120}
121
122/*this should be in acpi_resource.*/
123static int
124acpi_get_trigger(ACPI_RESOURCE *res)
125{
126	int trig;
127
128	switch (res->Type) {
129	case ACPI_RESOURCE_TYPE_IRQ:
130		KASSERT(res->Data.Irq.InterruptCount == 1,
131			("%s: multiple interrupts", __func__));
132		trig = res->Data.Irq.Triggering;
133		break;
134	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
135		KASSERT(res->Data.ExtendedIrq.InterruptCount == 1,
136			("%s: multiple interrupts", __func__));
137		trig = res->Data.ExtendedIrq.Triggering;
138		break;
139	default:
140		panic("%s: bad resource type %u", __func__, res->Type);
141	}
142
143	return (trig == ACPI_EDGE_SENSITIVE)
144		? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
145}
146
147static int
148acpi_ged_attach(device_t dev)
149{
150	struct acpi_ged_softc *sc = device_get_softc(dev);
151	struct resource_list *rl;
152	struct resource_list_entry *rle;
153	ACPI_RESOURCE ares;
154	ACPI_HANDLE evt_method;
155	int i;
156	int rawirq, trig;
157	char name[] = "_Xnn";
158
159	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
160
161	if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(dev), "_EVT",
162				       &evt_method))) {
163		device_printf(dev, "_EVT not found\n");
164		evt_method = NULL;
165	}
166
167	rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
168	STAILQ_FOREACH(rle, rl, link) {
169		if (rle->type == SYS_RES_IRQ) {
170			sc->numevts++;
171		}
172	}
173	sc->evts = mallocarray(sc->numevts, sizeof(*sc->evts), M_ACPIGED,
174	    M_WAITOK | M_ZERO);
175	for (i = 0; i < sc->numevts; i++) {
176		sc->evts[i].dev = dev;
177		sc->evts[i].rid = i;
178		sc->evts[i].r = bus_alloc_resource_any(dev, SYS_RES_IRQ,
179		    &sc->evts[i].rid,  RF_ACTIVE | RF_SHAREABLE);
180		if (sc->evts[i].r == NULL) {
181			device_printf(dev, "Cannot alloc %dth irq\n", i);
182			continue;
183		}
184#ifdef INTRNG
185		{
186			struct intr_map_data_acpi *ima;
187			ima = rman_get_virtual(sc->evts[i].r);
188			if (ima == NULL) {
189				device_printf(dev, "map not found"
190					      " non-intrng?\n");
191				rawirq = rman_get_start(sc->evts[i].r);
192				trig = INTR_TRIGGER_LEVEL;
193				if (ACPI_SUCCESS(acpi_lookup_irq_resource
194					(dev, sc->evts[i].rid,
195					 sc->evts[i].r, &ares))) {
196					trig = acpi_get_trigger(&ares);
197				}
198			} else if (ima->hdr.type == INTR_MAP_DATA_ACPI) {
199				device_printf(dev, "Raw IRQ %d\n", ima->irq);
200				rawirq = ima->irq;
201				trig = ima->trig;
202			} else {
203				device_printf(dev, "Not supported intr"
204					      " type%d\n", ima->hdr.type);
205				continue;
206			}
207		}
208#else
209		rawirq = rman_get_start(sc->evts[i].r);
210		trig = INTR_TRIGGER_LEVEL;
211		if (ACPI_SUCCESS(acpi_lookup_irq_resource
212				(dev, sc->evts[i].rid,
213				 sc->evts[i].r, &ares))) {
214			trig = acpi_get_trigger(&ares);
215		}
216#endif
217		if (rawirq < 0x100) {
218			sprintf(name, "_%c%02X",
219				((trig == INTR_TRIGGER_EDGE) ? 'E' : 'L'),
220				rawirq);
221			if (ACPI_SUCCESS(AcpiGetHandle
222					(acpi_get_handle(dev),
223					 name, &sc->evts[i].ah))) {
224				sc->evts[i].args.Count = 0; /* ensure */
225			} else {
226				sc->evts[i].ah = NULL; /* ensure */
227			}
228		}
229
230		if (sc->evts[i].ah == NULL) {
231			if (evt_method != NULL) {
232				sc->evts[i].ah = evt_method;
233				sc->evts[i].arg1.Type = ACPI_TYPE_INTEGER;
234				sc->evts[i].arg1.Integer.Value = rawirq;
235				sc->evts[i].args.Count = 1;
236				sc->evts[i].args.Pointer = &sc->evts[i].arg1;
237			} else{
238				device_printf
239					(dev,
240					 "Cannot find handler method %d\n",
241					 i);
242				continue;
243			}
244		}
245
246		if (bus_setup_intr(dev, sc->evts[i].r,
247			INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr,
248			&sc->evts[i], &sc->evts[i].cookie) != 0) {
249			device_printf(dev, "Failed to setup intr %d\n", i);
250		}
251	}
252
253	return_VALUE(0);
254}
255
256static int
257acpi_ged_detach(device_t dev)
258{
259	struct acpi_ged_softc *sc = device_get_softc(dev);
260	int i;
261
262	for (i = 0; i < sc->numevts; i++) {
263		if (sc->evts[i].cookie) {
264			bus_teardown_intr(dev, sc->evts[i].r,
265			    sc->evts[i].cookie);
266		}
267		if (sc->evts[i].r) {
268			bus_release_resource(dev, SYS_RES_IRQ, sc->evts[i].rid,
269			    sc->evts[i].r);
270		}
271	}
272	free(sc->evts, M_ACPIGED);
273
274	return (0);
275}
276