1 /* $NetBSD: gic_fdt.c,v 1.18 2019/11/24 11:10:12 skrll Exp $ */ 2 3 /*- 4 * Copyright (c) 2015-2017 Jared McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "pci.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.18 2019/11/24 11:10:12 skrll Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/device.h> 37 #include <sys/intr.h> 38 #include <sys/systm.h> 39 #include <sys/kernel.h> 40 #include <sys/lwp.h> 41 #include <sys/kmem.h> 42 #include <sys/queue.h> 43 44 #include <dev/pci/pcivar.h> 45 46 #include <arm/cortex/gic_intr.h> 47 #include <arm/cortex/gic_reg.h> 48 #include <arm/cortex/gic_v2m.h> 49 #include <arm/cortex/mpcore_var.h> 50 51 #include <dev/fdt/fdtvar.h> 52 53 #define GIC_MAXIRQ 1020 54 55 extern struct pic_softc *pic_list[]; 56 57 struct gic_fdt_softc; 58 struct gic_fdt_irq; 59 60 static int gic_fdt_match(device_t, cfdata_t, void *); 61 static void gic_fdt_attach(device_t, device_t, void *); 62 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX) 63 static void gic_fdt_attach_v2m(struct gic_fdt_softc *, bus_space_tag_t, int); 64 #endif 65 66 static int gic_fdt_intr(void *); 67 68 static void * gic_fdt_establish(device_t, u_int *, int, int, 69 int (*)(void *), void *); 70 static void gic_fdt_disestablish(device_t, void *); 71 static bool gic_fdt_intrstr(device_t, u_int *, char *, size_t); 72 73 struct fdtbus_interrupt_controller_func gic_fdt_funcs = { 74 .establish = gic_fdt_establish, 75 .disestablish = gic_fdt_disestablish, 76 .intrstr = gic_fdt_intrstr 77 }; 78 79 struct gic_fdt_irqhandler { 80 struct gic_fdt_irq *ih_irq; 81 int (*ih_fn)(void *); 82 void *ih_arg; 83 bool ih_mpsafe; 84 TAILQ_ENTRY(gic_fdt_irqhandler) ih_next; 85 }; 86 87 struct gic_fdt_irq { 88 struct gic_fdt_softc *intr_sc; 89 void *intr_ih; 90 void *intr_arg; 91 int intr_refcnt; 92 int intr_ipl; 93 int intr_level; 94 int intr_mpsafe; 95 TAILQ_HEAD(, gic_fdt_irqhandler) intr_handlers; 96 int intr_irq; 97 }; 98 99 struct gic_fdt_softc { 100 device_t sc_dev; 101 device_t sc_gicdev; 102 int sc_phandle; 103 104 int sc_v2m_count; 105 106 struct gic_fdt_irq *sc_irq[GIC_MAXIRQ]; 107 }; 108 109 CFATTACH_DECL_NEW(gic_fdt, sizeof(struct gic_fdt_softc), 110 gic_fdt_match, gic_fdt_attach, NULL, NULL); 111 112 static int 113 gic_fdt_match(device_t parent, cfdata_t cf, void *aux) 114 { 115 const char * const compatible[] = { 116 "arm,gic-400", 117 "arm,cortex-a15-gic", 118 "arm,cortex-a9-gic", 119 "arm,cortex-a7-gic", 120 NULL 121 }; 122 struct fdt_attach_args * const faa = aux; 123 124 return of_match_compatible(faa->faa_phandle, compatible); 125 } 126 127 static void 128 gic_fdt_attach(device_t parent, device_t self, void *aux) 129 { 130 struct gic_fdt_softc * const sc = device_private(self); 131 struct fdt_attach_args * const faa = aux; 132 const int phandle = faa->faa_phandle; 133 bus_addr_t addr_d, addr_c; 134 bus_size_t size_d, size_c; 135 bus_space_handle_t bsh; 136 int error; 137 138 sc->sc_dev = self; 139 sc->sc_phandle = phandle; 140 141 error = fdtbus_register_interrupt_controller(self, phandle, 142 &gic_fdt_funcs); 143 if (error) { 144 aprint_error(": couldn't register with fdtbus: %d\n", error); 145 return; 146 } 147 148 aprint_naive("\n"); 149 aprint_normal(": GIC\n"); 150 151 if (fdtbus_get_reg(sc->sc_phandle, 0, &addr_d, &size_d) != 0) { 152 aprint_error(": couldn't get distributor address\n"); 153 return; 154 } 155 if (fdtbus_get_reg(sc->sc_phandle, 1, &addr_c, &size_c) != 0) { 156 aprint_error(": couldn't get cpu interface address\n"); 157 return; 158 } 159 160 const bus_addr_t addr = uimin(addr_d, addr_c); 161 const bus_size_t end = uimax(addr_d + size_d, addr_c + size_c); 162 const bus_size_t size = end - addr; 163 164 error = bus_space_map(faa->faa_bst, addr, size, 0, &bsh); 165 if (error) { 166 aprint_error(": couldn't map registers: %d\n", error); 167 return; 168 } 169 170 struct mpcore_attach_args mpcaa = { 171 .mpcaa_name = "armgic", 172 .mpcaa_memt = faa->faa_bst, 173 .mpcaa_memh = bsh, 174 .mpcaa_off1 = addr_d - addr, 175 .mpcaa_off2 = addr_c - addr, 176 }; 177 178 sc->sc_gicdev = config_found(self, &mpcaa, NULL); 179 180 arm_fdt_irq_set_handler(armgic_irq_handler); 181 182 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX) 183 for (int child = OF_child(phandle); child; child = OF_peer(child)) { 184 if (!fdtbus_status_okay(child)) 185 continue; 186 const char * const v2m_compat[] = { "arm,gic-v2m-frame", NULL }; 187 if (of_match_compatible(child, v2m_compat)) 188 gic_fdt_attach_v2m(sc, faa->faa_bst, child); 189 } 190 #endif 191 } 192 193 #if NPCI > 0 && defined(__HAVE_PCI_MSI_MSIX) 194 static void 195 gic_fdt_attach_v2m(struct gic_fdt_softc *sc, bus_space_tag_t bst, int phandle) 196 { 197 struct gic_v2m_frame *frame; 198 u_int base_spi, num_spis; 199 bus_space_handle_t bsh; 200 bus_addr_t addr; 201 bus_size_t size; 202 203 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 204 aprint_error_dev(sc->sc_gicdev, "couldn't get V2M address\n"); 205 return; 206 } 207 208 if (bus_space_map(bst, addr, size, 0, &bsh) != 0) { 209 aprint_error_dev(sc->sc_gicdev, "couldn't map V2M frame\n"); 210 return; 211 } 212 const uint32_t typer = bus_space_read_4(bst, bsh, GIC_MSI_TYPER); 213 bus_space_unmap(bst, bsh, size); 214 215 if (of_getprop_uint32(phandle, "arm,msi-base-spi", &base_spi)) 216 base_spi = __SHIFTOUT(typer, GIC_MSI_TYPER_BASE); 217 if (of_getprop_uint32(phandle, "arm,msi-num-spis", &num_spis)) 218 num_spis = __SHIFTOUT(typer, GIC_MSI_TYPER_NUMBER); 219 220 frame = kmem_zalloc(sizeof(*frame), KM_SLEEP); 221 frame->frame_reg = addr; 222 frame->frame_pic = pic_list[0]; 223 frame->frame_base = base_spi; 224 frame->frame_count = num_spis; 225 226 if (gic_v2m_init(frame, sc->sc_gicdev, sc->sc_v2m_count++) != 0) { 227 aprint_error_dev(sc->sc_gicdev, "failed to initialize GICv2m\n"); 228 } else { 229 aprint_normal_dev(sc->sc_gicdev, "GICv2m @ %#" PRIx64 230 ", SPIs %u-%u\n", frame->frame_reg, 231 frame->frame_base, frame->frame_base + frame->frame_count); 232 } 233 } 234 #endif 235 236 static void * 237 gic_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, 238 int (*func)(void *), void *arg) 239 { 240 struct gic_fdt_softc * const sc = device_private(dev); 241 struct gic_fdt_irq *firq; 242 struct gic_fdt_irqhandler *firqh; 243 244 /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ 245 /* 2nd cell is the interrupt number */ 246 /* 3rd cell is flags */ 247 248 const u_int type = be32toh(specifier[0]); 249 const u_int intr = be32toh(specifier[1]); 250 const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); 251 const u_int trig = be32toh(specifier[2]) & 0xf; 252 const u_int level = (trig & FDT_INTR_TYPE_DOUBLE_EDGE) 253 ? IST_EDGE : IST_LEVEL; 254 255 const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; 256 257 firq = sc->sc_irq[irq]; 258 if (firq == NULL) { 259 firq = kmem_alloc(sizeof(*firq), KM_SLEEP); 260 firq->intr_sc = sc; 261 firq->intr_refcnt = 0; 262 firq->intr_arg = arg; 263 firq->intr_ipl = ipl; 264 firq->intr_level = level; 265 firq->intr_mpsafe = mpsafe; 266 TAILQ_INIT(&firq->intr_handlers); 267 firq->intr_irq = irq; 268 if (arg == NULL) { 269 firq->intr_ih = intr_establish(irq, ipl, level | mpsafe, 270 func, NULL); 271 } else { 272 firq->intr_ih = intr_establish(irq, ipl, level | mpsafe, 273 gic_fdt_intr, firq); 274 } 275 if (firq->intr_ih == NULL) { 276 kmem_free(firq, sizeof(*firq)); 277 return NULL; 278 } 279 sc->sc_irq[irq] = firq; 280 } else { 281 if (firq->intr_arg == NULL && arg != NULL) { 282 device_printf(dev, "cannot share irq with NULL arg\n"); 283 return NULL; 284 } 285 if (firq->intr_ipl != ipl) { 286 device_printf(dev, "cannot share irq with different " 287 "ipl\n"); 288 return NULL; 289 } 290 if (firq->intr_level != level) { 291 device_printf(dev, "cannot share edge and level " 292 "interrupts\n"); 293 return NULL; 294 } 295 if (firq->intr_mpsafe != mpsafe) { 296 device_printf(dev, "cannot share between " 297 "mpsafe/non-mpsafe\n"); 298 return NULL; 299 } 300 } 301 302 firq->intr_refcnt++; 303 304 firqh = kmem_alloc(sizeof(*firqh), KM_SLEEP); 305 firqh->ih_mpsafe = (flags & FDT_INTR_MPSAFE) != 0; 306 firqh->ih_irq = firq; 307 firqh->ih_fn = func; 308 firqh->ih_arg = arg; 309 TAILQ_INSERT_TAIL(&firq->intr_handlers, firqh, ih_next); 310 311 return firq->intr_ih; 312 } 313 314 static void 315 gic_fdt_disestablish(device_t dev, void *ih) 316 { 317 struct gic_fdt_softc * const sc = device_private(dev); 318 struct gic_fdt_irqhandler *firqh; 319 struct gic_fdt_irq *firq; 320 u_int n; 321 322 for (n = 0; n < GIC_MAXIRQ; n++) { 323 firq = sc->sc_irq[n]; 324 if (firq->intr_ih != ih) 325 continue; 326 327 KASSERT(firq->intr_refcnt > 0); 328 329 if (firq->intr_refcnt > 1) 330 panic("%s: cannot disestablish shared irq", __func__); 331 332 firqh = TAILQ_FIRST(&firq->intr_handlers); 333 kmem_free(firqh, sizeof(*firqh)); 334 intr_disestablish(firq->intr_ih); 335 kmem_free(firq, sizeof(*firq)); 336 sc->sc_irq[n] = NULL; 337 return; 338 } 339 340 panic("%s: interrupt not established", __func__); 341 } 342 343 static int 344 gic_fdt_intr(void *priv) 345 { 346 struct gic_fdt_irq *firq = priv; 347 struct gic_fdt_irqhandler *firqh; 348 int handled = 0; 349 350 TAILQ_FOREACH(firqh, &firq->intr_handlers, ih_next) 351 handled += firqh->ih_fn(firqh->ih_arg); 352 353 return handled; 354 } 355 356 static bool 357 gic_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) 358 { 359 /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */ 360 /* 2nd cell is the interrupt number */ 361 /* 3rd cell is flags */ 362 363 if (!specifier) 364 return false; 365 const u_int type = be32toh(specifier[0]); 366 const u_int intr = be32toh(specifier[1]); 367 const u_int irq = type == 0 ? IRQ_SPI(intr) : IRQ_PPI(intr); 368 369 snprintf(buf, buflen, "GIC irq %d", irq); 370 371 return true; 372 } 373