xref: /freebsd/sys/dev/acpica/acpi_ged.c (revision be91b4797e2c8f3440f6fe3aac7e246886f9ebca)
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
42 ACPI_MODULE_NAME("GED")
43 
44 static MALLOC_DEFINE(M_ACPIGED, "acpiged", "ACPI Generic event data");
45 
46 struct 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 
56 struct acpi_ged_softc {
57 	int numevts;
58 	struct acpi_ged_event *evts;
59 };
60 
61 static int acpi_ged_probe(device_t dev);
62 static int acpi_ged_attach(device_t dev);
63 static int acpi_ged_detach(device_t dev);
64 
65 static char *ged_ids[] = { "ACPI0013", NULL };
66 
67 static 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 
75 static driver_t acpi_ged_driver = {
76 	"acpi_ged",
77 	acpi_ged_methods,
78 	sizeof(struct acpi_ged_softc),
79 };
80 
81 DRIVER_MODULE(acpi_ged, acpi, acpi_ged_driver, 0, 0);
82 MODULE_DEPEND(acpi_ged, acpi, 1, 1, 1);
83 
84 static int acpi_ged_defer;
85 SYSCTL_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 
89 static void
acpi_ged_evt(void * arg)90 acpi_ged_evt(void *arg)
91 {
92 	struct acpi_ged_event *evt = arg;
93 
94 	AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
95 }
96 
97 static void
acpi_ged_intr(void * arg)98 acpi_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 }
107 static int
acpi_ged_probe(device_t dev)108 acpi_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.*/
123 static int
acpi_get_trigger(ACPI_RESOURCE * res)124 acpi_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 
147 static int
acpi_ged_attach(device_t dev)148 acpi_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 
256 static int
acpi_ged_detach(device_t dev)257 acpi_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