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