Home | History | Annotate | Line # | Download | only in apple
apple_intc.c revision 1.5
      1 /* $NetBSD: apple_intc.c,v 1.5 2021/11/26 19:36:17 skrll Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2021 Jared McNeill <jmcneill (at) 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.5 2021/11/26 19:36:17 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