1 /* $NetBSD: apple_intc.c,v 1.7 2022/03/28 19:59:26 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2021 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 "opt_ddb.h" 30 #include "opt_multiprocessor.h" 31 32 #define _INTR_PRIVATE 33 34 #include <sys/cdefs.h> 35 __KERNEL_RCSID(0, "$NetBSD: apple_intc.c,v 1.7 2022/03/28 19:59:26 riastradh Exp $"); 36 37 #include <sys/param.h> 38 #include <sys/bus.h> 39 #include <sys/device.h> 40 #include <sys/intr.h> 41 #include <sys/kernel.h> 42 #include <sys/lwp.h> 43 #include <sys/systm.h> 44 #include <sys/cpu.h> 45 #include <sys/kmem.h> 46 #include <sys/atomic.h> 47 48 #include <dev/fdt/fdtvar.h> 49 50 #include <dev/pci/pcireg.h> 51 #include <dev/pci/pcivar.h> 52 53 #include <arm/cpu.h> 54 #include <arm/cpufunc.h> 55 #include <arm/armreg.h> 56 #include <arm/locore.h> 57 #include <arm/pic/picvar.h> 58 #include <arm/fdt/arm_fdtvar.h> 59 60 /* 61 * AIC registers 62 */ 63 #define AIC_INFO 0x0004 64 #define AIC_INFO_NIRQ __BITS(15,0) 65 #define AIC_WHOAMI 0x2000 66 #define AIC_EVENT 0x2004 67 #define AIC_EVENT_TYPE __BITS(31,16) 68 #define AIC_EVENT_TYPE_NONE 0 69 #define AIC_EVENT_TYPE_IRQ 1 70 #define AIC_EVENT_TYPE_IPI 4 71 #define AIC_EVENT_DATA __BITS(15,0) 72 #define AIC_EVENT_IPI_OTHER 1 73 #define AIC_IPI_SEND 0x2008 74 #define AIC_IPI_ACK 0x200c 75 #define AIC_IPI_MASK_CLR 0x2028 76 #define AIC_IPI_OTHER __BIT(0) 77 #define AIC_AFFINITY(irqno) (0x3000 + (irqno) * 4) 78 #define AIC_SW_SET(irqno) (0x4000 + (irqno) / 32 * 4) 79 #define AIC_SW_CLR(irqno) (0x4080 + (irqno) / 32 * 4) 80 #define AIC_MASK_SET(irqno) (0x4100 + (irqno) / 32 * 4) 81 #define AIC_MASK_CLR(irqno) (0x4180 + (irqno) / 32 * 4) 82 #define AIC_MASK_BIT(irqno) __BIT((irqno) & 0x1f) 83 84 static const struct device_compatible_entry compat_data[] = { 85 { .compat = "apple,aic" }, 86 DEVICE_COMPAT_EOL 87 }; 88 89 struct apple_intc_softc; 90 91 struct apple_intc_percpu { 92 struct apple_intc_softc *pc_sc; 93 u_int pc_cpuid; 94 u_int pc_ipimask; 95 96 struct pic_softc pc_pic; 97 }; 98 99 #define LOCALPIC_SOURCE_TIMER 0 100 #define LOCALPIC_SOURCE_IPI 1 101 102 struct apple_intc_softc { 103 device_t sc_dev; /* device handle */ 104 bus_space_tag_t sc_bst; /* mmio tag */ 105 bus_space_handle_t sc_bsh; /* mmio handle */ 106 u_int sc_nirq; /* number of supported IRQs */ 107 u_int *sc_cpuid; /* map of cpu index to AIC CPU ID */ 108 struct apple_intc_percpu *sc_pc; /* per-CPU data for timer and IPIs */ 109 110 struct pic_softc sc_pic; 111 }; 112 113 static struct apple_intc_softc *intc_softc; 114 115 #define PICTOSOFTC(pic) container_of(pic, struct apple_intc_softc, sc_pic) 116 #define PICTOPERCPU(pic) container_of(pic, struct apple_intc_percpu, pc_pic) 117 118 #define AIC_READ(sc, reg) \ 119 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) 120 #define AIC_WRITE(sc, reg, val) \ 121 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 122 123 static void 124 apple_intc_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t mask) 125 { 126 struct apple_intc_softc * const sc = PICTOSOFTC(pic); 127 128 AIC_WRITE(sc, AIC_SW_SET(irqbase), mask); 129 AIC_WRITE(sc, AIC_MASK_CLR(irqbase), mask); 130 } 131 132 static void 133 apple_intc_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t mask) 134 { 135 } 136 137 static void 138 apple_intc_establish_irq(struct pic_softc *pic, struct intrsource *is) 139 { 140 struct apple_intc_softc * const sc = PICTOSOFTC(pic); 141 142 KASSERT(is->is_type == IST_LEVEL); 143 144 /* Route to primary PE by default */ 145 AIC_WRITE(sc, AIC_AFFINITY(is->is_irq), __BIT(0)); 146 AIC_WRITE(sc, AIC_MASK_CLR(is->is_irq), 147 AIC_MASK_BIT(is->is_irq)); 148 } 149 150 static void 151 apple_intc_set_priority(struct pic_softc *pic, int ipl) 152 { 153 } 154 155 static void 156 apple_intc_cpu_init(struct pic_softc *pic, struct cpu_info *ci) 157 { 158 struct apple_intc_softc * const sc = PICTOSOFTC(pic); 159 const u_int cpuno = cpu_index(ci); 160 161 sc->sc_cpuid[cpuno] = AIC_READ(sc, AIC_WHOAMI); 162 } 163 164 static const struct pic_ops apple_intc_picops = { 165 .pic_unblock_irqs = apple_intc_unblock_irqs, 166 .pic_block_irqs = apple_intc_block_irqs, 167 .pic_establish_irq = apple_intc_establish_irq, 168 .pic_set_priority = apple_intc_set_priority, 169 #ifdef MULTIPROCESSOR 170 .pic_cpu_init = apple_intc_cpu_init, 171 #endif 172 }; 173 174 static void 175 apple_intc_local_unblock_irqs(struct pic_softc *pic, size_t irqbase, 176 uint32_t mask) 177 { 178 KASSERT(irqbase == 0); 179 180 if ((mask & __BIT(LOCALPIC_SOURCE_TIMER)) != 0) { 181 gtmr_cntv_ctl_write(gtmr_cntv_ctl_read() & ~CNTCTL_IMASK); 182 isb(); 183 } 184 } 185 186 static void 187 apple_intc_local_block_irqs(struct pic_softc *pic, size_t irqbase, 188 uint32_t mask) 189 { 190 KASSERT(irqbase == 0); 191 192 if ((mask & __BIT(LOCALPIC_SOURCE_TIMER)) != 0) { 193 gtmr_cntv_ctl_write(gtmr_cntv_ctl_read() | CNTCTL_IMASK); 194 isb(); 195 } 196 } 197 198 static void 199 apple_intc_local_establish_irq(struct pic_softc *pic, struct intrsource *is) 200 { 201 } 202 203 #ifdef MULTIPROCESSOR 204 static void 205 apple_intc_local_ipi_send(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi) 206 { 207 struct apple_intc_percpu * const pc = PICTOPERCPU(pic); 208 struct apple_intc_softc * const sc = pc->pc_sc; 209 const u_int target = sc->sc_cpuid[pc->pc_cpuid]; 210 211 atomic_or_32(&pc->pc_ipimask, __BIT(ipi)); 212 AIC_WRITE(sc, AIC_IPI_SEND, __BIT(target)); 213 } 214 #endif /* MULTIPROCESSOR */ 215 216 static const struct pic_ops apple_intc_localpicops = { 217 .pic_unblock_irqs = apple_intc_local_unblock_irqs, 218 .pic_block_irqs = apple_intc_local_block_irqs, 219 .pic_establish_irq = apple_intc_local_establish_irq, 220 #ifdef MULTIPROCESSOR 221 .pic_ipi_send = apple_intc_local_ipi_send, 222 #endif 223 }; 224 225 static void * 226 apple_intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, 227 int (*func)(void *), void *arg, const char *xname) 228 { 229 struct apple_intc_softc * const sc = device_private(dev); 230 231 /* 1st cell is the interrupt type (0=IRQ, 1=FIQ) */ 232 const u_int type = be32toh(specifier[0]); 233 /* 2nd cell is the interrupt number */ 234 const u_int intno = be32toh(specifier[1]); 235 /* 3rd cell is the interrupt flags */ 236 237 const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; 238 239 if (type == 0) 240 return intr_establish_xname(intno, ipl, IST_LEVEL | mpsafe, 241 func, arg, xname); 242 243 /* interate over CPUs for LOCALPIC_SOURCE_TIMER */ 244 CPU_INFO_ITERATOR cii; 245 struct cpu_info *ci; 246 void *ih = NULL; 247 for (CPU_INFO_FOREACH(cii, ci)) { 248 const cpuid_t cpuno = cpu_index(ci); 249 struct apple_intc_percpu * const pc = &sc->sc_pc[cpuno]; 250 struct pic_softc * const pic = &pc->pc_pic; 251 const int irq = pic->pic_irqbase + LOCALPIC_SOURCE_TIMER; 252 253 void *ihn = intr_establish_xname(irq, ipl, IST_LEVEL | mpsafe, 254 func, arg, xname); 255 if (cpuno == 0) 256 ih = ihn; 257 } 258 return ih; 259 } 260 261 static void 262 apple_intc_fdt_disestablish(device_t dev, void *ih) 263 { 264 intr_disestablish(ih); 265 } 266 267 static bool 268 apple_intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) 269 { 270 if (!specifier) 271 return false; 272 273 /* 1st cell is the interrupt type (0=IRQ, 1=FIQ) */ 274 const u_int type = be32toh(specifier[0]); 275 /* 2nd cell is the interrupt number */ 276 const u_int intno = be32toh(specifier[1]); 277 278 snprintf(buf, buflen, "%s %u", type == 0 ? "irq" : "fiq", intno); 279 280 return true; 281 } 282 283 static const struct fdtbus_interrupt_controller_func apple_intc_fdt_funcs = { 284 .establish = apple_intc_fdt_establish, 285 .disestablish = apple_intc_fdt_disestablish, 286 .intrstr = apple_intc_fdt_intrstr, 287 }; 288 289 static void 290 apple_intc_mark_pending(struct pic_softc *pic, u_int intno) 291 { 292 const int base = intno & ~0x1f; 293 const uint32_t pending = __BIT(intno & 0x1f); 294 pic_mark_pending_sources(pic, base, pending); 295 } 296 297 static void 298 apple_intc_irq_handler(void *frame) 299 { 300 struct cpu_info * const ci = curcpu(); 301 struct apple_intc_softc * const sc = intc_softc; 302 struct pic_softc *pic; 303 struct intrsource *is; 304 const int oldipl = ci->ci_cpl; 305 uint16_t evtype, evdata; 306 bus_size_t clr_reg; 307 uint32_t clr_val; 308 309 ci->ci_data.cpu_nintr++; 310 311 for (;;) { 312 const uint32_t ev = AIC_READ(sc, AIC_EVENT); 313 evtype = __SHIFTOUT(ev, AIC_EVENT_TYPE); 314 evdata = __SHIFTOUT(ev, AIC_EVENT_DATA); 315 316 dsb(sy); 317 isb(); 318 319 if (evtype == AIC_EVENT_TYPE_IRQ) { 320 KASSERT(evdata < sc->sc_nirq); 321 pic = &sc->sc_pic; 322 is = pic->pic_sources[evdata]; 323 KASSERT(is != NULL); 324 325 AIC_WRITE(sc, AIC_SW_CLR(evdata), 326 __BIT(evdata & 0x1f)); 327 328 clr_reg = AIC_MASK_CLR(evdata); 329 clr_val = AIC_MASK_BIT(evdata); 330 } else if (evtype == AIC_EVENT_TYPE_IPI) { 331 KASSERT(evdata == AIC_EVENT_IPI_OTHER); 332 pic = &sc->sc_pc[cpu_index(ci)].pc_pic; 333 is = pic->pic_sources[LOCALPIC_SOURCE_IPI]; 334 KASSERT(is != NULL); 335 336 AIC_WRITE(sc, AIC_IPI_ACK, AIC_IPI_OTHER); 337 338 clr_reg = 0; 339 clr_val = 0; 340 } else { 341 break; 342 } 343 344 if (ci->ci_cpl >= is->is_ipl) { 345 apple_intc_mark_pending(pic, is->is_irq); 346 } else { 347 pic_set_priority(ci, is->is_ipl); 348 ENABLE_INTERRUPT(); 349 pic_dispatch(is, frame); 350 DISABLE_INTERRUPT(); 351 352 if (clr_val != 0) { 353 AIC_WRITE(sc, clr_reg, clr_val); 354 } 355 } 356 } 357 358 if (oldipl != IPL_HIGH) { 359 pic_do_pending_ints(DAIF_I|DAIF_F, oldipl, frame); 360 } 361 } 362 363 static void 364 apple_intc_fiq_handler(void *frame) 365 { 366 struct cpu_info * const ci = curcpu(); 367 struct apple_intc_softc * const sc = intc_softc; 368 struct pic_softc * const pic = &sc->sc_pc[cpu_index(ci)].pc_pic; 369 const int oldipl = ci->ci_cpl; 370 371 ci->ci_data.cpu_nintr++; 372 373 struct intrsource * const is = pic->pic_sources[LOCALPIC_SOURCE_TIMER]; 374 375 dsb(sy); 376 isb(); 377 378 if (oldipl >= is->is_ipl) { 379 apple_intc_mark_pending(pic, LOCALPIC_SOURCE_TIMER); 380 } else { 381 pic_set_priority(ci, is->is_ipl); 382 pic_dispatch(is, frame); 383 } 384 385 if (oldipl != IPL_HIGH) { 386 pic_do_pending_ints(DAIF_I|DAIF_F, oldipl, frame); 387 } 388 } 389 390 #ifdef MULTIPROCESSOR 391 static int 392 apple_intc_ipi_handler(void *priv) 393 { 394 struct apple_intc_percpu * const pc = priv; 395 struct apple_intc_softc * const sc = pc->pc_sc; 396 uint32_t ipimask, bit; 397 398 AIC_WRITE(sc, AIC_IPI_MASK_CLR, AIC_IPI_OTHER); 399 ipimask = atomic_swap_32(&pc->pc_ipimask, 0); 400 401 while ((bit = ffs(ipimask)) > 0) { 402 const u_int ipi = bit - 1; 403 404 switch (ipi) { 405 case IPI_AST: 406 pic_ipi_ast(priv); 407 break; 408 case IPI_NOP: 409 pic_ipi_nop(priv); 410 break; 411 #ifdef __HAVE_PREEMPTION 412 case IPI_KPREEMPT: 413 pic_ipi_kpreempt(priv); 414 break; 415 #endif 416 case IPI_XCALL: 417 pic_ipi_xcall(priv); 418 break; 419 case IPI_GENERIC: 420 pic_ipi_generic(priv); 421 break; 422 case IPI_SHOOTDOWN: 423 pic_ipi_shootdown(priv); 424 break; 425 #ifdef DDB 426 case IPI_DDB: 427 pic_ipi_ddb(priv); 428 break; 429 #endif 430 } 431 ipimask &= ~__BIT(ipi); 432 } 433 434 return 1; 435 } 436 #endif /* MULTIPROCESSOR */ 437 438 static int 439 apple_intc_match(device_t parent, cfdata_t cf, void *aux) 440 { 441 struct fdt_attach_args * const faa = aux; 442 443 return of_compatible_match(faa->faa_phandle, compat_data); 444 } 445 446 static void 447 apple_intc_attach(device_t parent, device_t self, void *aux) 448 { 449 struct apple_intc_softc * const sc = device_private(self); 450 struct fdt_attach_args * const faa = aux; 451 const int phandle = faa->faa_phandle; 452 bus_addr_t addr; 453 bus_size_t size; 454 int error; 455 456 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 457 aprint_error(": couldn't get registers\n"); 458 return; 459 } 460 461 sc->sc_dev = self; 462 sc->sc_bst = faa->faa_bst; 463 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { 464 aprint_error(": couldn't map registers\n"); 465 return; 466 } 467 468 sc->sc_nirq = AIC_READ(sc, AIC_INFO) & AIC_INFO_NIRQ; 469 470 aprint_naive("\n"); 471 aprint_normal(": Apple AIC (%u IRQs, 1 FIQ)\n", sc->sc_nirq); 472 KASSERT(sc->sc_nirq % 32 == 0); 473 474 sc->sc_pic.pic_ops = &apple_intc_picops; 475 sc->sc_pic.pic_maxsources = sc->sc_nirq; 476 snprintf(sc->sc_pic.pic_name, sizeof(sc->sc_pic.pic_name), "AIC"); 477 pic_add(&sc->sc_pic, 0); 478 479 error = fdtbus_register_interrupt_controller(self, phandle, 480 &apple_intc_fdt_funcs); 481 if (error) { 482 aprint_error_dev(self, "couldn't register with fdtbus: %d\n", 483 error); 484 return; 485 } 486 487 KASSERT(intc_softc == NULL); 488 intc_softc = sc; 489 arm_fdt_irq_set_handler(apple_intc_irq_handler); 490 arm_fdt_fiq_set_handler(apple_intc_fiq_handler); 491 492 KASSERT(ncpu != 0); 493 sc->sc_cpuid = kmem_zalloc(sizeof(*sc->sc_cpuid) * ncpu, KM_SLEEP); 494 sc->sc_pc = kmem_zalloc(sizeof(*sc->sc_pc) * ncpu, KM_SLEEP); 495 496 CPU_INFO_ITERATOR cii; 497 struct cpu_info *ci; 498 for (CPU_INFO_FOREACH(cii, ci)) { 499 const cpuid_t cpuno = cpu_index(ci); 500 struct apple_intc_percpu * const pc = &sc->sc_pc[cpuno]; 501 struct pic_softc * const pic = &pc->pc_pic; 502 503 pc->pc_sc = sc; 504 pc->pc_cpuid = cpuno; 505 506 pic->pic_cpus = ci->ci_kcpuset; 507 pic->pic_ops = &apple_intc_localpicops; 508 pic->pic_maxsources = 2; 509 snprintf(pic->pic_name, sizeof(pic->pic_name), "AIC/%lu", cpuno); 510 511 pic_add(pic, PIC_IRQBASE_ALLOC); 512 513 intr_establish_xname(pic->pic_irqbase + LOCALPIC_SOURCE_IPI, 514 IPL_HIGH, IST_LEVEL | IST_MPSAFE, apple_intc_ipi_handler, 515 pc, "ipi"); 516 } 517 518 apple_intc_cpu_init(&sc->sc_pic, curcpu()); 519 } 520 521 CFATTACH_DECL_NEW(apple_intc, sizeof(struct apple_intc_softc), 522 apple_intc_match, apple_intc_attach, NULL, NULL); 523