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