xref: /netbsd-src/sys/arch/arm/apple/apple_intc.c (revision 2718af68c3efc72c9769069b5c7f9ed36f6b9def)
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