xref: /freebsd/sys/dev/acpica/acpi_ged.c (revision f374ba41f55c1a127303d92d830dd58eef2f5243)
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 __FBSDID("$FreeBSD$");
28 
29 #include "opt_acpi.h"
30 
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/module.h>
36 #include <sys/rman.h>
37 
38 #include <contrib/dev/acpica/include/acpi.h>
39 #include <contrib/dev/acpica/include/accommon.h>
40 #include <dev/acpica/acpivar.h>
41 
42 /* Hooks for the ACPI CA debugging infrastructure */
43 #define _COMPONENT ACPI_BUS
44 ACPI_MODULE_NAME("GED")
45 
46 static MALLOC_DEFINE(M_ACPIGED, "acpiged", "ACPI Generic event data");
47 
48 struct acpi_ged_event {
49 	device_t dev;
50 	struct resource *r;
51 	int rid;
52 	void *cookie;
53 	ACPI_HANDLE ah;
54 	ACPI_OBJECT_LIST args;
55 	ACPI_OBJECT arg1;
56 };
57 
58 struct acpi_ged_softc {
59 	int numevts;
60 	struct acpi_ged_event *evts;
61 };
62 
63 static int acpi_ged_probe(device_t dev);
64 static int acpi_ged_attach(device_t dev);
65 static int acpi_ged_detach(device_t dev);
66 
67 static char *ged_ids[] = { "ACPI0013", NULL };
68 
69 static device_method_t acpi_ged_methods[] = {
70 	/* Device interface */
71 	DEVMETHOD(device_probe, acpi_ged_probe),
72 	DEVMETHOD(device_attach, acpi_ged_attach),
73 	DEVMETHOD(device_detach, acpi_ged_detach),
74 	DEVMETHOD_END
75 };
76 
77 static driver_t acpi_ged_driver = {
78 	"acpi_ged",
79 	acpi_ged_methods,
80 	sizeof(struct acpi_ged_softc),
81 };
82 
83 DRIVER_MODULE(acpi_ged, acpi, acpi_ged_driver, 0, 0);
84 MODULE_DEPEND(acpi_ged, acpi, 1, 1, 1);
85 
86 static void
87 acpi_ged_evt(void *arg)
88 {
89 	struct acpi_ged_event *evt = arg;
90 
91 	AcpiEvaluateObject(evt->ah, NULL, &evt->args, NULL);
92 }
93 
94 static void
95 acpi_ged_intr(void *arg)
96 {
97 	AcpiOsExecute(OSL_GPE_HANDLER, acpi_ged_evt, arg);
98 }
99 static int
100 acpi_ged_probe(device_t dev)
101 {
102 	int rv;
103 
104 	if (acpi_disabled("ged"))
105 		return (ENXIO);
106 	rv = ACPI_ID_PROBE(device_get_parent(dev), dev, ged_ids, NULL);
107 	if (rv > 0)
108 		return (ENXIO);
109 
110 	device_set_desc(dev, "Generic Event Device");
111 	return (rv);
112 }
113 
114 /*this should be in acpi_resource.*/
115 static int
116 acpi_get_trigger(ACPI_RESOURCE *res)
117 {
118 	int trig;
119 
120 	switch (res->Type) {
121 	case ACPI_RESOURCE_TYPE_IRQ:
122 		KASSERT(res->Data.Irq.InterruptCount == 1,
123 			("%s: multiple interrupts", __func__));
124 		trig = res->Data.Irq.Triggering;
125 		break;
126 	case ACPI_RESOURCE_TYPE_EXTENDED_IRQ:
127 		KASSERT(res->Data.ExtendedIrq.InterruptCount == 1,
128 			("%s: multiple interrupts", __func__));
129 		trig = res->Data.ExtendedIrq.Triggering;
130 		break;
131 	default:
132 		panic("%s: bad resource type %u", __func__, res->Type);
133 	}
134 
135 	return (trig == ACPI_EDGE_SENSITIVE)
136 		? INTR_TRIGGER_EDGE : INTR_TRIGGER_LEVEL;
137 }
138 
139 static int
140 acpi_ged_attach(device_t dev)
141 {
142 	struct acpi_ged_softc *sc = device_get_softc(dev);
143 	struct resource_list *rl;
144 	struct resource_list_entry *rle;
145 	ACPI_RESOURCE ares;
146 	ACPI_HANDLE evt_method;
147 	int i;
148 	int rawirq, trig;
149 	char name[] = "_Xnn";
150 
151 	ACPI_FUNCTION_TRACE((char *)(uintptr_t) __func__);
152 
153 	if (ACPI_FAILURE(AcpiGetHandle(acpi_get_handle(dev), "_EVT",
154 				       &evt_method))) {
155 		device_printf(dev, "_EVT not found\n");
156 		evt_method = NULL;
157 	}
158 
159 	rl = BUS_GET_RESOURCE_LIST(device_get_parent(dev), dev);
160 	STAILQ_FOREACH(rle, rl, link) {
161 		if (rle->type == SYS_RES_IRQ) {
162 			sc->numevts++;
163 		}
164 	}
165 	sc->evts = mallocarray(sc->numevts, sizeof(*sc->evts), M_ACPIGED,
166 	    M_WAITOK | M_ZERO);
167 	for (i = 0; i < sc->numevts; i++) {
168 		sc->evts[i].dev = dev;
169 		sc->evts[i].rid = i;
170 		sc->evts[i].r = bus_alloc_resource_any(dev, SYS_RES_IRQ,
171 		    &sc->evts[i].rid,  RF_ACTIVE | RF_SHAREABLE);
172 		if (sc->evts[i].r == NULL) {
173 			device_printf(dev, "Cannot alloc %dth irq\n", i);
174 			continue;
175 		}
176 #ifdef INTRNG
177 		{
178 			struct intr_map_data_acpi *ima;
179 			ima = rman_get_virtual(sc->evts[i].r);
180 			if (ima == NULL) {
181 				device_printf(dev, "map not found"
182 					      " non-intrng?\n");
183 				rawirq = rman_get_start(sc->evts[i].r);
184 				trig = INTR_TRIGGER_LEVEL;
185 				if (ACPI_SUCCESS(acpi_lookup_irq_resource
186 					(dev, sc->evts[i].rid,
187 					 sc->evts[i].r, &ares))) {
188 					trig = acpi_get_trigger(&ares);
189 				}
190 			} else if (ima->hdr.type == INTR_MAP_DATA_ACPI) {
191 				device_printf(dev, "Raw IRQ %d\n", ima->irq);
192 				rawirq = ima->irq;
193 				trig = ima->trig;
194 			} else {
195 				device_printf(dev, "Not supported intr"
196 					      " type%d\n", ima->hdr.type);
197 				continue;
198 			}
199 		}
200 #else
201 		rawirq = rman_get_start(sc->evts[i].r);
202 		trig = INTR_TRIGGER_LEVEL;
203 		if (ACPI_SUCCESS(acpi_lookup_irq_resource
204 				(dev, sc->evts[i].rid,
205 				 sc->evts[i].r, &ares))) {
206 			trig = acpi_get_trigger(&ares);
207 		}
208 #endif
209 		if (rawirq < 0x100) {
210 			sprintf(name, "_%c%02X",
211 				((trig == INTR_TRIGGER_EDGE) ? 'E' : 'L'),
212 				rawirq);
213 			if (ACPI_SUCCESS(AcpiGetHandle
214 					(acpi_get_handle(dev),
215 					 name, &sc->evts[i].ah))) {
216 				sc->evts[i].args.Count = 0; /* ensure */
217 			} else {
218 				sc->evts[i].ah = NULL; /* ensure */
219 			}
220 		}
221 
222 		if (sc->evts[i].ah == NULL) {
223 			if (evt_method != NULL) {
224 				sc->evts[i].ah = evt_method;
225 				sc->evts[i].arg1.Type = ACPI_TYPE_INTEGER;
226 				sc->evts[i].arg1.Integer.Value = rawirq;
227 				sc->evts[i].args.Count = 1;
228 				sc->evts[i].args.Pointer = &sc->evts[i].arg1;
229 			} else{
230 				device_printf
231 					(dev,
232 					 "Cannot find handler method %d\n",
233 					 i);
234 				continue;
235 			}
236 		}
237 
238 		if (bus_setup_intr(dev, sc->evts[i].r,
239 			INTR_TYPE_MISC | INTR_MPSAFE, NULL, acpi_ged_intr,
240 			&sc->evts[i], &sc->evts[i].cookie) != 0) {
241 			device_printf(dev, "Failed to setup intr %d\n", i);
242 		}
243 	}
244 
245 	return_VALUE(0);
246 }
247 
248 static int
249 acpi_ged_detach(device_t dev)
250 {
251 	struct acpi_ged_softc *sc = device_get_softc(dev);
252 	int i;
253 
254 	for (i = 0; i < sc->numevts; i++) {
255 		if (sc->evts[i].cookie) {
256 			bus_teardown_intr(dev, sc->evts[i].r,
257 			    sc->evts[i].cookie);
258 		}
259 		if (sc->evts[i].r) {
260 			bus_release_resource(dev, SYS_RES_IRQ, sc->evts[i].rid,
261 			    sc->evts[i].r);
262 		}
263 	}
264 	free(sc->evts, M_ACPIGED);
265 
266 	return (0);
267 }
268