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