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