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