1 /*- 2 * Copyright (c) 2011,2016 The FreeBSD Foundation 3 * All rights reserved. 4 * 5 * This software was developed by Andrew Turner under 6 * sponsorship from the FreeBSD Foundation. 7 * 8 * Developed by Damjan Marion <damjan.marion@gmail.com> 9 * 10 * Based on OMAP4 GIC code by Ben Gray 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 3. The name of the company nor the name of the author may be used to 21 * endorse or promote products derived from this software without specific 22 * prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 */ 36 37 #include "opt_acpi.h" 38 #include "opt_platform.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/bus.h> 43 #include <sys/kernel.h> 44 #include <sys/malloc.h> 45 #include <sys/module.h> 46 47 #include <machine/intr.h> 48 49 #include <contrib/dev/acpica/include/acpi.h> 50 #include <dev/acpica/acpivar.h> 51 52 #include <arm/arm/gic.h> 53 #include <arm/arm/gic_common.h> 54 55 struct gic_acpi_devinfo { 56 struct resource_list rl; 57 }; 58 59 static device_identify_t gic_acpi_identify; 60 static device_probe_t gic_acpi_probe; 61 static device_attach_t gic_acpi_attach; 62 static bus_get_resource_list_t gic_acpi_get_resource_list; 63 static bool arm_gic_add_children(device_t); 64 65 static device_method_t gic_acpi_methods[] = { 66 /* Device interface */ 67 DEVMETHOD(device_identify, gic_acpi_identify), 68 DEVMETHOD(device_probe, gic_acpi_probe), 69 DEVMETHOD(device_attach, gic_acpi_attach), 70 71 /* Bus interface */ 72 DEVMETHOD(bus_get_resource_list, gic_acpi_get_resource_list), 73 74 DEVMETHOD_END, 75 }; 76 77 DEFINE_CLASS_1(gic, gic_acpi_driver, gic_acpi_methods, 78 sizeof(struct arm_gic_softc), arm_gic_driver); 79 80 EARLY_DRIVER_MODULE(gic, acpi, gic_acpi_driver, 0, 0, 81 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 82 83 struct madt_table_data { 84 device_t parent; 85 ACPI_MADT_GENERIC_DISTRIBUTOR *dist; 86 ACPI_MADT_GENERIC_INTERRUPT *intr[GIC_MAXCPU]; 87 }; 88 89 static void 90 madt_handler(ACPI_SUBTABLE_HEADER *entry, void *arg) 91 { 92 struct madt_table_data *madt_data; 93 ACPI_MADT_GENERIC_INTERRUPT *intr; 94 95 madt_data = (struct madt_table_data *)arg; 96 97 switch(entry->Type) { 98 case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR: 99 if (madt_data->dist != NULL) { 100 if (bootverbose) 101 device_printf(madt_data->parent, 102 "gic: Already have a distributor table"); 103 } else 104 madt_data->dist = 105 (ACPI_MADT_GENERIC_DISTRIBUTOR *)entry; 106 break; 107 case ACPI_MADT_TYPE_GENERIC_INTERRUPT: 108 intr = (ACPI_MADT_GENERIC_INTERRUPT *)entry; 109 if (intr->CpuInterfaceNumber < GIC_MAXCPU) 110 madt_data->intr[intr->CpuInterfaceNumber] = intr; 111 break; 112 } 113 } 114 115 static void 116 gic_acpi_identify(driver_t *driver, device_t parent) 117 { 118 struct madt_table_data madt_data; 119 ACPI_MADT_GENERIC_INTERRUPT *intr; 120 ACPI_TABLE_MADT *madt; 121 vm_paddr_t physaddr; 122 device_t dev; 123 int i; 124 125 physaddr = acpi_find_table(ACPI_SIG_MADT); 126 if (physaddr == 0) 127 return; 128 129 madt = acpi_map_table(physaddr, ACPI_SIG_MADT); 130 if (madt == NULL) { 131 device_printf(parent, "gic: Unable to map the MADT\n"); 132 return; 133 } 134 135 bzero(&madt_data, sizeof(madt_data)); 136 madt_data.parent = parent; 137 madt_data.dist = NULL; 138 139 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, 140 madt_handler, &madt_data); 141 142 /* Check the version of the GIC we have */ 143 switch (madt_data.dist->Version) { 144 case ACPI_MADT_GIC_VERSION_NONE: 145 case ACPI_MADT_GIC_VERSION_V1: 146 case ACPI_MADT_GIC_VERSION_V2: 147 break; 148 default: 149 goto out; 150 } 151 152 intr = NULL; 153 for (i = 0; i < GIC_MAXCPU; i++) { 154 if (madt_data.intr[i] != NULL) { 155 if (intr == NULL) { 156 intr = madt_data.intr[i]; 157 } else if (intr->BaseAddress != 158 madt_data.intr[i]->BaseAddress) { 159 device_printf(parent, 160 "gic: Not all CPU interfaces at the same address, this may fail\n"); 161 } 162 } 163 } 164 if (intr == NULL) { 165 device_printf(parent, "gic: No CPU interfaces found\n"); 166 goto out; 167 } 168 169 dev = BUS_ADD_CHILD(parent, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE, 170 "gic", -1); 171 if (dev == NULL) { 172 device_printf(parent, "add gic child failed\n"); 173 goto out; 174 } 175 176 BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 0, 177 madt_data.dist->BaseAddress, 4 * 1024); 178 BUS_SET_RESOURCE(parent, dev, SYS_RES_MEMORY, 1, 179 intr->BaseAddress, 4 * 1024); 180 181 acpi_set_private(dev, (void *)(uintptr_t)madt_data.dist->Version); 182 out: 183 acpi_unmap_table(madt); 184 } 185 186 static int 187 gic_acpi_probe(device_t dev) 188 { 189 190 switch((uintptr_t)acpi_get_private(dev)) { 191 case ACPI_MADT_GIC_VERSION_NONE: 192 case ACPI_MADT_GIC_VERSION_V1: 193 case ACPI_MADT_GIC_VERSION_V2: 194 break; 195 default: 196 return (ENXIO); 197 } 198 199 device_set_desc(dev, "ARM Generic Interrupt Controller"); 200 return (BUS_PROBE_NOWILDCARD); 201 } 202 203 static int 204 gic_acpi_attach(device_t dev) 205 { 206 struct arm_gic_softc *sc = device_get_softc(dev); 207 intptr_t xref; 208 int err; 209 210 sc->gic_bus = GIC_BUS_ACPI; 211 212 err = arm_gic_attach(dev); 213 if (err != 0) 214 return (err); 215 216 xref = ACPI_INTR_XREF; 217 218 /* 219 * Now, when everything is initialized, it's right time to 220 * register interrupt controller to interrupt framefork. 221 */ 222 if (intr_pic_register(dev, xref) == NULL) { 223 device_printf(dev, "could not register PIC\n"); 224 goto cleanup; 225 } 226 227 /* 228 * Controller is root: 229 */ 230 if (intr_pic_claim_root(dev, xref, arm_gic_intr, sc, 231 GIC_LAST_SGI - GIC_FIRST_SGI + 1) != 0) { 232 device_printf(dev, "could not set PIC as a root\n"); 233 intr_pic_deregister(dev, xref); 234 goto cleanup; 235 } 236 /* If we have children probe and attach them */ 237 if (arm_gic_add_children(dev)) { 238 bus_generic_probe(dev); 239 return (bus_generic_attach(dev)); 240 } 241 242 return (0); 243 244 cleanup: 245 arm_gic_detach(dev); 246 return(ENXIO); 247 } 248 249 static struct resource_list * 250 gic_acpi_get_resource_list(device_t bus, device_t child) 251 { 252 struct gic_acpi_devinfo *di; 253 254 di = device_get_ivars(child); 255 KASSERT(di != NULL, ("gic_acpi_get_resource_list: No devinfo")); 256 257 return (&di->rl); 258 } 259 260 static void 261 madt_gicv2m_handler(ACPI_SUBTABLE_HEADER *entry, void *arg) 262 { 263 struct arm_gic_softc *sc; 264 ACPI_MADT_GENERIC_MSI_FRAME *msi; 265 struct gic_acpi_devinfo *dinfo; 266 device_t dev, cdev; 267 268 if (entry->Type == ACPI_MADT_TYPE_GENERIC_MSI_FRAME) { 269 sc = arg; 270 dev = sc->gic_dev; 271 msi = (ACPI_MADT_GENERIC_MSI_FRAME *)entry; 272 273 device_printf(dev, "frame: %x %lx %x %u %u\n", msi->MsiFrameId, 274 msi->BaseAddress, msi->Flags, msi->SpiCount, msi->SpiBase); 275 276 cdev = device_add_child(dev, NULL, -1); 277 if (cdev == NULL) 278 return; 279 280 dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO); 281 resource_list_init(&dinfo->rl); 282 resource_list_add(&dinfo->rl, SYS_RES_MEMORY, 0, 283 msi->BaseAddress, msi->BaseAddress + PAGE_SIZE - 1, 284 PAGE_SIZE); 285 device_set_ivars(cdev, dinfo); 286 } 287 } 288 289 static bool 290 arm_gic_add_children(device_t dev) 291 { 292 struct arm_gic_softc *sc = device_get_softc(dev); 293 ACPI_TABLE_MADT *madt; 294 vm_paddr_t physaddr; 295 296 /* This should return a valid address as it did in gic_acpi_identify */ 297 physaddr = acpi_find_table(ACPI_SIG_MADT); 298 if (physaddr == 0) 299 return (false); 300 301 madt = acpi_map_table(physaddr, ACPI_SIG_MADT); 302 if (madt == NULL) { 303 device_printf(dev, "gic: Unable to map the MADT\n"); 304 return (false); 305 } 306 307 acpi_walk_subtables(madt + 1, (char *)madt + madt->Header.Length, 308 madt_gicv2m_handler, sc); 309 310 acpi_unmap_table(madt); 311 312 return (true); 313 } 314 315 static int 316 arm_gicv2m_acpi_probe(device_t dev) 317 { 318 319 if (gic_get_bus(dev) != GIC_BUS_ACPI) 320 return (EINVAL); 321 322 if (gic_get_hw_rev(dev) > 2) 323 return (EINVAL); 324 325 device_set_desc(dev, "ARM Generic Interrupt Controller MSI/MSIX"); 326 return (BUS_PROBE_DEFAULT); 327 } 328 329 static int 330 arm_gicv2m_acpi_attach(device_t dev) 331 { 332 struct arm_gicv2m_softc *sc; 333 334 sc = device_get_softc(dev); 335 sc->sc_xref = ACPI_MSI_XREF; 336 337 return (arm_gicv2m_attach(dev)); 338 } 339 340 static device_method_t arm_gicv2m_acpi_methods[] = { 341 /* Device interface */ 342 DEVMETHOD(device_probe, arm_gicv2m_acpi_probe), 343 DEVMETHOD(device_attach, arm_gicv2m_acpi_attach), 344 345 /* End */ 346 DEVMETHOD_END 347 }; 348 349 DEFINE_CLASS_1(gicv2m, arm_gicv2m_acpi_driver, arm_gicv2m_acpi_methods, 350 sizeof(struct arm_gicv2m_softc), arm_gicv2m_driver); 351 352 EARLY_DRIVER_MODULE(gicv2m_acpi, gic, arm_gicv2m_acpi_driver, 0, 0, 353 BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); 354