xref: /freebsd/sys/dev/acpica/acpi_ged.c (revision 22cf89c938886d14f5796fc49f9f020c23ea8eaf)
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 void
85 acpi_ged_evt(void *arg)
86 {
87 	struct acpi_ged_event *evt = arg;
88 
89 	AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
90 }
91 
92 static void
93 acpi_ged_intr(void *arg)
94 {
95 	AcpiOsExecute(OSL_GPE_HANDLER, acpi_ged_evt, arg);
96 }
97 static int
98 acpi_ged_probe(device_t dev)
99 {
100 	int rv;
101 
102 	if (acpi_disabled("ged"))
103 		return (ENXIO);
104 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ged_ids, NULL);
105 	if (rv > 0)
106 		return (ENXIO);
107 
108 	device_set_desc(dev, "Generic Event Device");
109 	return (rv);
110 }
111 
112 /*this should be in acpi_resource.*/
113 static int
114 acpi_get_trigger(ACPI_RESOURCE *res)
115 {
116 	int trig;
117 
118 	switch (res->Type) {
119 	case ACPI_RESOURCE_TYPE_IRQ:
120 		KASSERT(res->Data.Irq.InterruptCount == 1,
121 			("%s: multiple interrupts", __func__));
122 		trig = res->Data.Irq.Triggering;
123 		break;
124 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
125 		KASSERT(res->Data.ExtendedIrq.InterruptCount == 1,
126 			("%s: multiple interrupts", __func__));
127 		trig = res->Data.ExtendedIrq.Triggering;
128 		break;
129 	default:
130 		panic("%s: bad resource type %u", __func__, res->Type);
131 	}
132 
133 	return (trig == ACPI_EDGE_SENSITIVE)
134 		? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
135 }
136 
137 static int
138 acpi_ged_attach(device_t dev)
139 {
140 	struct acpi_ged_softc *sc = device_get_softc(dev);
141 	struct resource_list *rl;
142 	struct resource_list_entry *rle;
143 	ACPI_RESOURCE ares;
144 	ACPI_HANDLE evt_method;
145 	int i;
146 	int rawirq, trig;
147 	char name[] = "_Xnn";
148 
149 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
150 
151 	if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(dev), "_EVT",
152 				       &evt_method))) {
153 		device_printf(dev, "_EVT not found\n");
154 		evt_method = NULL;
155 	}
156 
157 	rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
158 	STAILQ_FOREACH(rle, rl, link) {
159 		if (rle->type == SYS_RES_IRQ) {
160 			sc->numevts++;
161 		}
162 	}
163 	sc->evts = mallocarray(sc->numevts, sizeof(*sc->evts), M_ACPIGED,
164 	    M_WAITOK | M_ZERO);
165 	for (i = 0; i < sc->numevts; i++) {
166 		sc->evts[i].dev = dev;
167 		sc->evts[i].rid = i;
168 		sc->evts[i].r = bus_alloc_resource_any(dev, SYS_RES_IRQ,
169 		    &sc->evts[i].rid,  RF_ACTIVE | RF_SHAREABLE);
170 		if (sc->evts[i].r == NULL) {
171 			device_printf(dev, "Cannot alloc %dth irq\n", i);
172 			continue;
173 		}
174 #ifdef INTRNG
175 		{
176 			struct intr_map_data_acpi *ima;
177 			ima = rman_get_virtual(sc->evts[i].r);
178 			if (ima == NULL) {
179 				device_printf(dev, "map not found"
180 					      " non-intrng?\n");
181 				rawirq = rman_get_start(sc->evts[i].r);
182 				trig = INTR_TRIGGER_LEVEL;
183 				if (ACPI_SUCCESS(acpi_lookup_irq_resource
184 					(dev, sc->evts[i].rid,
185 					 sc->evts[i].r, &ares))) {
186 					trig = acpi_get_trigger(&ares);
187 				}
188 			} else if (ima->hdr.type == INTR_MAP_DATA_ACPI) {
189 				device_printf(dev, "Raw IRQ %d\n", ima->irq);
190 				rawirq = ima->irq;
191 				trig = ima->trig;
192 			} else {
193 				device_printf(dev, "Not supported intr"
194 					      " type%d\n", ima->hdr.type);
195 				continue;
196 			}
197 		}
198 #else
199 		rawirq = rman_get_start(sc->evts[i].r);
200 		trig = INTR_TRIGGER_LEVEL;
201 		if (ACPI_SUCCESS(acpi_lookup_irq_resource
202 				(dev, sc->evts[i].rid,
203 				 sc->evts[i].r, &ares))) {
204 			trig = acpi_get_trigger(&ares);
205 		}
206 #endif
207 		if (rawirq < 0x100) {
208 			sprintf(name, "_%c%02X",
209 				((trig == INTR_TRIGGER_EDGE) ? 'E' : 'L'),
210 				rawirq);
211 			if (ACPI_SUCCESS(AcpiGetHandle
212 					(acpi_get_handle(dev),
213 					 name, &sc->evts[i].ah))) {
214 				sc->evts[i].args.Count = 0; /* ensure */
215 			} else {
216 				sc->evts[i].ah = NULL; /* ensure */
217 			}
218 		}
219 
220 		if (sc->evts[i].ah == NULL) {
221 			if (evt_method != NULL) {
222 				sc->evts[i].ah = evt_method;
223 				sc->evts[i].arg1.Type = ACPI_TYPE_INTEGER;
224 				sc->evts[i].arg1.Integer.Value = rawirq;
225 				sc->evts[i].args.Count = 1;
226 				sc->evts[i].args.Pointer = &sc->evts[i].arg1;
227 			} else{
228 				device_printf
229 					(dev,
230 					 "Cannot find handler method %d\n",
231 					 i);
232 				continue;
233 			}
234 		}
235 
236 		if (bus_setup_intr(dev, sc->evts[i].r,
237 			INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr,
238 			&sc->evts[i], &sc->evts[i].cookie) != 0) {
239 			device_printf(dev, "Failed to setup intr %d\n", i);
240 		}
241 	}
242 
243 	return_VALUE(0);
244 }
245 
246 static int
247 acpi_ged_detach(device_t dev)
248 {
249 	struct acpi_ged_softc *sc = device_get_softc(dev);
250 	int i;
251 
252 	for (i = 0; i < sc->numevts; i++) {
253 		if (sc->evts[i].cookie) {
254 			bus_teardown_intr(dev, sc->evts[i].r,
255 			    sc->evts[i].cookie);
256 		}
257 		if (sc->evts[i].r) {
258 			bus_release_resource(dev, SYS_RES_IRQ, sc->evts[i].rid,
259 			    sc->evts[i].r);
260 		}
261 	}
262 	free(sc->evts, M_ACPIGED);
263 
264 	return (0);
265 }
266