1 /* $NetBSD: gic.c,v 1.4 2013/06/20 05:30:21 matt Exp $ */ 2 /*- 3 * Copyright (c) 2012 The NetBSD Foundation, Inc. 4 * All rights reserved. 5 * 6 * This code is derived from software contributed to The NetBSD Foundation 7 * by Matt Thomas of 3am Software Foundry. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 19 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 20 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 21 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 22 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #define _INTR_PRIVATE 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: gic.c,v 1.4 2013/06/20 05:30:21 matt Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/bus.h> 38 #include <sys/device.h> 39 #include <sys/evcnt.h> 40 #include <sys/intr.h> 41 #include <sys/proc.h> 42 #include <sys/xcall.h> /* for xc_ipi_handler */ 43 44 #include <arm/armreg.h> 45 #include <arm/cpufunc.h> 46 #include <arm/atomic.h> 47 48 #include <arm/cortex/gic_reg.h> 49 #include <arm/cortex/mpcore_var.h> 50 51 #define ARMGIC_SGI_IPIBASE (16 - NIPI) 52 53 static int armgic_match(device_t, cfdata_t, void *); 54 static void armgic_attach(device_t, device_t, void *); 55 56 static void armgic_set_priority(struct pic_softc *, int); 57 static void armgic_unblock_irqs(struct pic_softc *, size_t, uint32_t); 58 static void armgic_block_irqs(struct pic_softc *, size_t, uint32_t); 59 static void armgic_establish_irq(struct pic_softc *, struct intrsource *); 60 #if 0 61 static void armgic_source_name(struct pic_softc *, int, char *, size_t); 62 #endif 63 64 #ifdef MULTIPROCESSOR 65 static void armgic_cpu_init(struct pic_softc *, struct cpu_info *); 66 static void armgic_ipi_send(struct pic_softc *, const kcpuset_t *, u_long); 67 #endif 68 69 static const struct pic_ops armgic_picops = { 70 .pic_unblock_irqs = armgic_unblock_irqs, 71 .pic_block_irqs = armgic_block_irqs, 72 .pic_establish_irq = armgic_establish_irq, 73 #if 0 74 .pic_source_name = armgic_source_name, 75 #endif 76 .pic_set_priority = armgic_set_priority, 77 #ifdef MULTIPROCESSOR 78 .pic_cpu_init = armgic_cpu_init, 79 .pic_ipi_send = armgic_ipi_send, 80 #endif 81 }; 82 83 #define PICTOSOFTC(pic) ((struct armgic_softc *)(pic)) 84 85 static struct armgic_softc { 86 struct pic_softc sc_pic; 87 device_t sc_dev; 88 bus_space_tag_t sc_memt; 89 bus_space_handle_t sc_gicch; 90 bus_space_handle_t sc_gicdh; 91 size_t sc_gic_lines; 92 uint32_t sc_gic_type; 93 uint32_t sc_gic_valid_lines[1024/32]; 94 uint32_t sc_enabled_local; 95 } armgic_softc = { 96 .sc_pic = { 97 .pic_ops = &armgic_picops, 98 .pic_name = "armgic", 99 }, 100 }; 101 102 static struct intrsource armgic_dummy_source; 103 104 __CTASSERT(NIPL == 8); 105 106 /* 107 * GIC register are always in little-endian. 108 */ 109 static inline uint32_t 110 gicc_read(struct armgic_softc *sc, bus_size_t o) 111 { 112 uint32_t v = bus_space_read_4(sc->sc_memt, sc->sc_gicch, o); 113 return le32toh(v); 114 } 115 116 static inline void 117 gicc_write(struct armgic_softc *sc, bus_size_t o, uint32_t v) 118 { 119 v = htole32(v); 120 bus_space_write_4(sc->sc_memt, sc->sc_gicch, o, v); 121 } 122 123 static inline uint32_t 124 gicd_read(struct armgic_softc *sc, bus_size_t o) 125 { 126 uint32_t v = bus_space_read_4(sc->sc_memt, sc->sc_gicdh, o); 127 return le32toh(v); 128 } 129 130 static inline void 131 gicd_write(struct armgic_softc *sc, bus_size_t o, uint32_t v) 132 { 133 v = htole32(v); 134 bus_space_write_4(sc->sc_memt, sc->sc_gicdh, o, v); 135 } 136 137 /* 138 * In the GIC prioritization scheme, lower numbers have higher priority. 139 */ 140 static inline uint32_t 141 armgic_ipl_to_priority(int ipl) 142 { 143 return (IPL_HIGH - ipl) * GICC_PMR_PRIORITIES / NIPL; 144 } 145 146 static inline int 147 armgic_priority_to_ipl(uint32_t priority) 148 { 149 return IPL_HIGH - priority * NIPL / GICC_PMR_PRIORITIES; 150 } 151 152 static void 153 armgic_unblock_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask) 154 { 155 struct armgic_softc * const sc = PICTOSOFTC(pic); 156 const size_t group = irq_base / 32; 157 158 if (group == 0) 159 sc->sc_enabled_local |= irq_mask; 160 161 gicd_write(sc, GICD_ISENABLERn(group), irq_mask); 162 } 163 164 static void 165 armgic_block_irqs(struct pic_softc *pic, size_t irq_base, uint32_t irq_mask) 166 { 167 struct armgic_softc * const sc = PICTOSOFTC(pic); 168 const size_t group = irq_base / 32; 169 170 if (group == 0) 171 sc->sc_enabled_local &= ~irq_mask; 172 173 gicd_write(sc, GICD_ICENABLERn(group), irq_mask); 174 } 175 176 static uint32_t armgic_last_priority; 177 178 static void 179 armgic_set_priority(struct pic_softc *pic, int ipl) 180 { 181 struct armgic_softc * const sc = PICTOSOFTC(pic); 182 183 const uint32_t priority = armgic_ipl_to_priority(ipl); 184 gicc_write(sc, GICC_PMR, priority); 185 armgic_last_priority = priority; 186 } 187 188 #ifdef __HAVE_PIC_FAST_SOFTINTS 189 void 190 softint_init_md(lwp_t *l, u_int level, uintptr_t *machdep_p) 191 { 192 lwp_t **lp = &l->l_cpu->ci_softlwps[level]; 193 KASSERT(*lp == NULL || *lp == l); 194 *lp = l; 195 /* 196 * Really easy. Just tell it to trigger the local CPU. 197 */ 198 *machdep_p = GICD_SGIR_TargetListFilter_Me 199 | __SHIFTIN(level, GICD_SGIR_SGIINTID); 200 } 201 202 void 203 softint_trigger(uintptr_t machdep) 204 { 205 206 gicd_write(&armgic_softc, GICD_SGIR, machdep); 207 } 208 #endif 209 210 void 211 armgic_irq_handler(void *tf) 212 { 213 struct cpu_info * const ci = curcpu(); 214 struct armgic_softc * const sc = &armgic_softc; 215 const int old_ipl = ci->ci_cpl; 216 #ifdef DIAGNOSTIC 217 const int old_mtx_count = ci->ci_mtx_count; 218 const int old_l_biglocks = ci->ci_curlwp->l_biglocks; 219 #endif 220 #ifdef DEBUG 221 size_t n = 0; 222 #endif 223 224 ci->ci_data.cpu_nintr++; 225 226 KASSERTMSG(old_ipl != IPL_HIGH, "old_ipl %d pmr %#x hppir %#x", 227 old_ipl, gicc_read(sc, GICC_PMR), gicc_read(sc, GICC_HPPIR)); 228 #if 0 229 printf("%s(enter): %s: pmr=%u hppir=%u\n", 230 __func__, ci->ci_data.cpu_name, 231 gicc_read(sc, GICC_PMR), 232 gicc_read(sc, GICC_HPPIR)); 233 #elif 0 234 printf("(%u:%d", ci->ci_index, old_ipl); 235 #endif 236 237 for (;;) { 238 uint32_t iar = gicc_read(sc, GICC_IAR); 239 uint32_t irq = __SHIFTOUT(iar, GICC_IAR_IRQ); 240 //printf(".%u", irq); 241 if (irq == GICC_IAR_IRQ_SPURIOUS) { 242 iar = gicc_read(sc, GICC_IAR); 243 irq = __SHIFTOUT(iar, GICC_IAR_IRQ); 244 if (irq == GICC_IAR_IRQ_SPURIOUS) 245 break; 246 //printf(".%u", irq); 247 } 248 249 //const uint32_t cpuid = __SHIFTOUT(iar, GICC_IAR_CPUID_MASK); 250 struct intrsource * const is = sc->sc_pic.pic_sources[irq]; 251 KASSERT(is != &armgic_dummy_source); 252 253 /* 254 * GIC has asserted IPL for us so we can just update ci_cpl. 255 * 256 * But it's not that simple. We may have already bumped ci_cpl 257 * due to a high priority interrupt and now we are about to 258 * dispatch one lower than the previous. It's possible for 259 * that previous interrupt to have deferred some interrupts 260 * so we need deal with those when lowering to the current 261 * interrupt's ipl. 262 * 263 * However, if are just raising ipl, we can just update ci_cpl. 264 */ 265 #if 0 266 const int ipl = armgic_priority_to_ipl(gicc_read(sc, GICC_RPR)); 267 KASSERTMSG(panicstr != NULL || ipl == is->is_ipl, 268 "%s: irq %d: running ipl %d != source ipl %u", 269 ci->ci_data.cpu_name, irq, ipl, is->is_ipl); 270 #else 271 const int ipl = is->is_ipl; 272 #endif 273 if (__predict_false(ipl < ci->ci_cpl)) { 274 //printf("<"); 275 pic_do_pending_ints(I32_bit, ipl, tf); 276 KASSERT(ci->ci_cpl == ipl); 277 } else { 278 KASSERTMSG(ipl > ci->ci_cpl, "ipl %d cpl %d hw-ipl %#x", 279 ipl, ci->ci_cpl, 280 gicc_read(sc, GICC_PMR)); 281 //printf(">"); 282 gicc_write(sc, GICC_PMR, armgic_ipl_to_priority(ipl)); 283 ci->ci_cpl = ipl; 284 } 285 //printf("$"); 286 cpsie(I32_bit); 287 pic_dispatch(is, tf); 288 cpsid(I32_bit); 289 gicc_write(sc, GICC_EOIR, iar); 290 #ifdef DEBUG 291 n++; 292 KDASSERTMSG(n < 5, "%s: processed too many (%zu)", 293 ci->ci_data.cpu_name, n); 294 #endif 295 } 296 297 // printf("%s(%p): exit (%zu dispatched)\n", __func__, tf, n); 298 /* 299 * Now handle any pending ints. 300 */ 301 //printf("!"); 302 KASSERT(old_ipl != IPL_HIGH); 303 pic_do_pending_ints(I32_bit, old_ipl, tf); 304 KASSERTMSG(ci->ci_cpl == old_ipl, "ci_cpl %d old_ipl %d", ci->ci_cpl, old_ipl); 305 KASSERT(old_mtx_count == ci->ci_mtx_count); 306 KASSERT(old_l_biglocks == ci->ci_curlwp->l_biglocks); 307 #if 0 308 printf("%s(exit): %s(%d): pmr=%u hppir=%u\n", 309 __func__, ci->ci_data.cpu_name, ci->ci_cpl, 310 gicc_read(sc, GICC_PMR), 311 gicc_read(sc, GICC_HPPIR)); 312 #elif 0 313 printf("->%#x)", ((struct trapframe *)tf)->tf_pc); 314 #endif 315 } 316 317 void 318 armgic_establish_irq(struct pic_softc *pic, struct intrsource *is) 319 { 320 struct armgic_softc * const sc = PICTOSOFTC(pic); 321 const size_t group = is->is_irq / 32; 322 const u_int irq = is->is_irq & 31; 323 const u_int byte_shift = 8 * (irq & 3); 324 const u_int twopair_shift = 2 * (irq & 15); 325 326 KASSERTMSG(sc->sc_gic_valid_lines[group] & __BIT(irq), 327 "irq %u: not valid (group[%zu]=0x%08x [0x%08x])", 328 is->is_irq, group, sc->sc_gic_valid_lines[group], 329 (uint32_t)__BIT(irq)); 330 331 KASSERTMSG(is->is_type == IST_LEVEL || is->is_type == IST_EDGE, 332 "irq %u: type %u unsupported", is->is_irq, is->is_type); 333 334 const bus_size_t targets_reg = GICD_ITARGETSRn(is->is_irq / 4); 335 const bus_size_t cfg_reg = GICD_ICFGRn(is->is_irq / 16); 336 uint32_t targets = gicd_read(sc, targets_reg); 337 uint32_t cfg = gicd_read(sc, cfg_reg); 338 339 if (group > 0) { 340 /* 341 * There are 4 irqs per TARGETS register. For now bind 342 * to the primary cpu. 343 */ 344 targets &= ~(0xff << byte_shift); 345 targets |= 1 << byte_shift; 346 gicd_write(sc, targets_reg, targets); 347 348 /* 349 * There are 16 irqs per CFG register. 10=EDGE 00=LEVEL 350 */ 351 uint32_t new_cfg = cfg; 352 uint32_t old_cfg = (cfg >> twopair_shift) & 3; 353 if (is->is_type == IST_LEVEL && (old_cfg & 2) != 0) { 354 new_cfg &= ~(3 << twopair_shift); 355 } else if (is->is_type == IST_EDGE && (old_cfg & 2) == 0) { 356 new_cfg |= 2 << twopair_shift; 357 } 358 if (new_cfg != cfg) { 359 gicd_write(sc, cfg_reg, cfg); 360 #if 0 361 printf("%s: irq %u: cfg changed from %#x to %#x\n", 362 pic->pic_name, is->is_irq, cfg, new_cfg); 363 #endif 364 } 365 } 366 367 /* 368 * There are 4 irqs per PRIORITY register. Map the IPL 369 * to GIC priority. 370 */ 371 const bus_size_t priority_reg = GICD_IPRIORITYRn(is->is_irq / 4); 372 uint32_t priority = gicd_read(sc, priority_reg); 373 priority &= ~(0xff << byte_shift); 374 priority |= armgic_ipl_to_priority(is->is_ipl) << byte_shift; 375 gicd_write(sc, priority_reg, priority); 376 377 #if 0 378 printf("%s: irq %u: target %#x cfg %u priority %#x (%u)\n", 379 pic->pic_name, is->is_irq, (targets >> byte_shift) & 0xff, 380 (cfg >> twopair_shift) & 3, (priority >> byte_shift) & 0xff, 381 is->is_ipl); 382 #endif 383 } 384 385 #ifdef MULTIPROCESSOR 386 static void 387 armgic_cpu_init_priorities(struct armgic_softc *sc) 388 { 389 uint32_t enabled = sc->sc_enabled_local; 390 for (size_t i = 0; i < 32; i += 4, enabled >>= 4) { 391 /* 392 * If there are no enabled interrupts for the priority register, 393 * don't bother changing it. 394 */ 395 if ((enabled & 0x0f) == 0) 396 continue; 397 /* 398 * Since priorities are in 3210 order, it' 399 */ 400 const bus_size_t priority_reg = GICD_IPRIORITYRn(i / 4); 401 uint32_t priority = gicd_read(sc, priority_reg); 402 uint32_t byte_mask = 0xff; 403 size_t byte_shift = 0; 404 for (size_t j = 0; j < 4; j++, byte_mask <<= 8, byte_shift += 8) { 405 struct intrsource * const is = sc->sc_pic.pic_sources[i+j]; 406 if (is == NULL || is == &armgic_dummy_source) 407 continue; 408 priority &= ~byte_mask; 409 priority |= armgic_ipl_to_priority(is->is_ipl) << byte_shift; 410 } 411 gicd_write(sc, priority_reg, priority); 412 } 413 } 414 415 void 416 armgic_cpu_init(struct pic_softc *pic, struct cpu_info *ci) 417 { 418 struct armgic_softc * const sc = PICTOSOFTC(pic); 419 if (!CPU_IS_PRIMARY(ci) && sc->sc_enabled_local) { 420 armgic_cpu_init_priorities(sc); 421 } 422 KASSERTMSG(ci->ci_cpl == IPL_HIGH, "ipl %d not IPL_HIGH", ci->ci_cpl); 423 gicc_write(sc, GICC_PMR, armgic_ipl_to_priority(ci->ci_cpl)); // set PMR 424 gicc_write(sc, GICC_CTRL, GICC_CTRL_V1_Enable); // enable interrupt 425 if (!CPU_IS_PRIMARY(ci) && sc->sc_enabled_local) 426 gicd_write(sc, GICD_ISENABLERn(0), sc->sc_enabled_local); 427 cpsie(I32_bit); // allow IRQ exceptions 428 } 429 430 void 431 armgic_ipi_send(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi) 432 { 433 struct armgic_softc * const sc = PICTOSOFTC(pic); 434 435 if (ipi == IPI_NOP) { 436 __asm __volatile("sev"); 437 return; 438 } 439 440 uint32_t targets; 441 kcpuset_export_u32(kcp, &targets, sizeof(targets)); 442 uint32_t sgir = __SHIFTOUT(ARMGIC_SGI_IPIBASE + ipi, GICD_SGIR_SGIINTID); 443 sgir |= __SHIFTOUT(targets, GICD_SGIR_TargetList); 444 445 printf("%s: %s: %#x", __func__, curcpu()->ci_data.cpu_name, sgir); 446 gicd_write(sc, GICD_SGIR, sgir); 447 printf("\n"); 448 } 449 #endif 450 451 int 452 armgic_match(device_t parent, cfdata_t cf, void *aux) 453 { 454 struct mpcore_attach_args * const mpcaa = aux; 455 456 if (strcmp(cf->cf_name, mpcaa->mpcaa_name) != 0) 457 return 0; 458 if (!CPU_ID_CORTEX_P(cputype) || CPU_ID_CORTEX_A8_P(cputype)) 459 return 0; 460 461 return 1; 462 } 463 464 void 465 armgic_attach(device_t parent, device_t self, void *aux) 466 { 467 struct armgic_softc * const sc = &armgic_softc; 468 struct mpcore_attach_args * const mpcaa = aux; 469 470 sc->sc_dev = self; 471 self->dv_private = sc; 472 473 sc->sc_memt = mpcaa->mpcaa_memt; /* provided for us */ 474 bus_space_subregion(sc->sc_memt, mpcaa->mpcaa_memh, mpcaa->mpcaa_off1, 475 4096, &sc->sc_gicdh); 476 bus_space_subregion(sc->sc_memt, mpcaa->mpcaa_memh, mpcaa->mpcaa_off2, 477 4096, &sc->sc_gicch); 478 479 sc->sc_gic_type = gicd_read(sc, GICD_TYPER); 480 sc->sc_pic.pic_maxsources = GICD_TYPER_LINES(sc->sc_gic_type); 481 482 gicc_write(sc, GICC_CTRL, 0); /* disable all interrupts */ 483 gicd_write(sc, GICD_CTRL, 0); /* disable all interrupts */ 484 485 gicc_write(sc, GICC_PMR, 0xff); 486 uint32_t pmr = gicc_read(sc, GICC_PMR); 487 u_int priorities = 1 << popcount32(pmr); 488 489 /* 490 * Let's find out how many real sources we have. 491 */ 492 for (size_t i = 0, group = 0; 493 i < sc->sc_pic.pic_maxsources; 494 i += 32, group++) { 495 /* 496 * To figure what sources are real, one enables all interrupts 497 * and then reads back the enable mask so which ones really 498 * got enabled. 499 */ 500 gicd_write(sc, GICD_ISENABLERn(group), 0xffffffff); 501 uint32_t valid = gicd_read(sc, GICD_ISENABLERn(group)); 502 503 /* 504 * Now disable (clear enable) them again. 505 */ 506 gicd_write(sc, GICD_ICENABLERn(group), valid); 507 508 /* 509 * Count how many are valid. 510 */ 511 sc->sc_gic_lines += popcount32(valid); 512 sc->sc_gic_valid_lines[group] = valid; 513 } 514 515 pic_add(&sc->sc_pic, 0); 516 517 /* 518 * Force the GICD to IPL_HIGH and then enable interrupts. 519 */ 520 struct cpu_info * const ci = curcpu(); 521 KASSERTMSG(ci->ci_cpl == IPL_HIGH, "ipl %d not IPL_HIGH", ci->ci_cpl); 522 armgic_set_priority(&sc->sc_pic, ci->ci_cpl); // set PMR 523 gicd_write(sc, GICD_CTRL, GICD_CTRL_Enable); // enable Distributer 524 gicc_write(sc, GICC_CTRL, GICC_CTRL_V1_Enable); // enable CPU interrupts 525 cpsie(I32_bit); // allow interrupt exceptions 526 527 /* 528 * For each line that isn't valid, we set the intrsource for it to 529 * point at a dummy source so that pic_intr_establish will fail for it. 530 */ 531 for (size_t i = 0, group = 0; 532 i < sc->sc_pic.pic_maxsources; 533 i += 32, group++) { 534 uint32_t invalid = ~sc->sc_gic_valid_lines[group]; 535 for (size_t j = 0; invalid && j < 32; j++, invalid >>= 1) { 536 if (invalid & 1) { 537 sc->sc_pic.pic_sources[i + j] = 538 &armgic_dummy_source; 539 } 540 } 541 } 542 #ifdef __HAVE_PIC_FAST_SOFTINTS 543 intr_establish(SOFTINT_BIO, IPL_SOFTBIO, IST_EDGE, 544 pic_handle_softint, (void *)SOFTINT_BIO); 545 intr_establish(SOFTINT_CLOCK, IPL_SOFTCLOCK, IST_EDGE, 546 pic_handle_softint, (void *)SOFTINT_CLOCK); 547 intr_establish(SOFTINT_NET, IPL_SOFTNET, IST_EDGE, 548 pic_handle_softint, (void *)SOFTINT_NET); 549 intr_establish(SOFTINT_SERIAL, IPL_SOFTSERIAL, IST_EDGE, 550 pic_handle_softint, (void *)SOFTINT_SERIAL); 551 #endif 552 #ifdef MULTIPROCESSOR 553 intr_establish(ARMGIC_SGI_IPIBASE + IPI_AST, IPL_VM, IST_EDGE, 554 pic_ipi_nop, (void *)-1); 555 intr_establish(ARMGIC_SGI_IPIBASE + IPI_XCALL, IPL_VM, IST_EDGE, 556 pic_ipi_xcall, (void *)-1); 557 #if 0 /* Not needed */ 558 intr_establish(ARMGIC_SGI_IPIBASE + IPI_NOP, IPL_VM, IST_EDGE, 559 pic_ipi_nop, (void *)-1); 560 #endif 561 #ifdef __HAVE_PREEMPTION 562 intr_establish(ARMGIC_SGI_IPIBASE + IPI_KPREEMPT, IPL_VM, IST_EDGE, 563 pic_ipi_nop, (void *)-1); 564 #endif 565 armgic_cpu_init(&sc->sc_pic, curcpu()); 566 #endif 567 568 aprint_normal(": Generic Interrupt Controller, " 569 "%zu sources (%zu valid)\n", 570 sc->sc_pic.pic_maxsources, sc->sc_gic_lines); 571 572 const u_int ppis = popcount32(sc->sc_gic_valid_lines[0] >> 16); 573 const u_int sgis = popcount32(sc->sc_gic_valid_lines[0] & 0xffff); 574 aprint_normal_dev(sc->sc_dev, "%u Priorities, %zu SPIs, %u PPIs, %u SGIs\n", 575 priorities, sc->sc_gic_lines - ppis - sgis, ppis, sgis); 576 } 577 578 CFATTACH_DECL_NEW(armgic, 0, 579 armgic_match, armgic_attach, NULL, NULL); 580