bcm2835_intr.c revision 1.35 1 /* $NetBSD: bcm2835_intr.c,v 1.35 2021/01/19 00:38:52 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2012, 2015, 2019 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.35 2021/01/19 00:38:52 thorpej Exp $");
34
35 #define _INTR_PRIVATE
36
37 #include "opt_bcm283x.h"
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/cpu.h>
42 #include <sys/device.h>
43 #include <sys/kernel.h>
44 #include <sys/kmem.h>
45 #include <sys/proc.h>
46
47 #include <dev/fdt/fdtvar.h>
48
49 #include <machine/intr.h>
50
51 #include <arm/locore.h>
52
53 #include <arm/pic/picvar.h>
54 #include <arm/cortex/gtmr_var.h>
55
56 #include <arm/broadcom/bcm2835_intr.h>
57 #include <arm/broadcom/bcm2835reg.h>
58 #include <arm/broadcom/bcm2835var.h>
59
60 #include <arm/fdt/arm_fdtvar.h>
61
62 static void bcm2835_irq_handler(void *);
63 static void bcm2836mp_intr_init(void *, struct cpu_info *);
64
65 static void bcm2835_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
66 static void bcm2835_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
67 static int bcm2835_pic_find_pending_irqs(struct pic_softc *);
68 static void bcm2835_pic_establish_irq(struct pic_softc *, struct intrsource *);
69 static void bcm2835_pic_source_name(struct pic_softc *, int, char *,
70 size_t);
71
72 static void bcm2836mp_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
73 static void bcm2836mp_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
74 static int bcm2836mp_pic_find_pending_irqs(struct pic_softc *);
75 static void bcm2836mp_pic_establish_irq(struct pic_softc *, struct intrsource *);
76 static void bcm2836mp_pic_source_name(struct pic_softc *, int, char *,
77 size_t);
78 #ifdef MULTIPROCESSOR
79 int bcm2836mp_ipi_handler(void *);
80 static void bcm2836mp_cpu_init(struct pic_softc *, struct cpu_info *);
81 static void bcm2836mp_send_ipi(struct pic_softc *, const kcpuset_t *, u_long);
82 #endif
83
84 static int bcm2835_icu_fdt_decode_irq(u_int *);
85 static void *bcm2835_icu_fdt_establish(device_t, u_int *, int, int,
86 int (*)(void *), void *, const char *);
87 static void bcm2835_icu_fdt_disestablish(device_t, void *);
88 static bool bcm2835_icu_fdt_intrstr(device_t, u_int *, char *, size_t);
89
90 static int bcm2835_icu_intr(void *);
91
92 static int bcm2836mp_icu_fdt_decode_irq(u_int *);
93 static void *bcm2836mp_icu_fdt_establish(device_t, u_int *, int, int,
94 int (*)(void *), void *, const char *);
95 static void bcm2836mp_icu_fdt_disestablish(device_t, void *);
96 static bool bcm2836mp_icu_fdt_intrstr(device_t, u_int *, char *, size_t);
97
98 static int bcm2835_icu_match(device_t, cfdata_t, void *);
99 static void bcm2835_icu_attach(device_t, device_t, void *);
100
101 static int bcm2835_int_base;
102 static int bcm2836mp_int_base[BCM2836_NCPUS];
103
104 #define BCM2835_INT_BASE bcm2835_int_base
105 #define BCM2836_INT_BASECPUN(n) bcm2836mp_int_base[(n)]
106
107 static void
108 bcm2835_set_priority(struct pic_softc *pic, int ipl)
109 {
110 }
111
112 static struct pic_ops bcm2835_picops = {
113 .pic_unblock_irqs = bcm2835_pic_unblock_irqs,
114 .pic_block_irqs = bcm2835_pic_block_irqs,
115 .pic_find_pending_irqs = bcm2835_pic_find_pending_irqs,
116 .pic_establish_irq = bcm2835_pic_establish_irq,
117 .pic_source_name = bcm2835_pic_source_name,
118 .pic_set_priority = bcm2835_set_priority,
119 };
120
121 static struct pic_softc bcm2835_pic = {
122 .pic_ops = &bcm2835_picops,
123 .pic_maxsources = BCM2835_NIRQ,
124 .pic_name = "bcm2835 pic",
125 };
126
127 static struct pic_ops bcm2836mp_picops = {
128 .pic_unblock_irqs = bcm2836mp_pic_unblock_irqs,
129 .pic_block_irqs = bcm2836mp_pic_block_irqs,
130 .pic_find_pending_irqs = bcm2836mp_pic_find_pending_irqs,
131 .pic_establish_irq = bcm2836mp_pic_establish_irq,
132 .pic_source_name = bcm2836mp_pic_source_name,
133 #if defined(MULTIPROCESSOR)
134 .pic_cpu_init = bcm2836mp_cpu_init,
135 .pic_ipi_send = bcm2836mp_send_ipi,
136 #endif
137 };
138
139 static struct pic_softc bcm2836mp_pic[BCM2836_NCPUS] = {
140 [0 ... BCM2836_NCPUS - 1] = {
141 .pic_ops = &bcm2836mp_picops,
142 .pic_maxsources = BCM2836_NIRQPERCPU,
143 .pic_name = "bcm2836 pic",
144 }
145 };
146
147 static struct fdtbus_interrupt_controller_func bcm2835icu_fdt_funcs = {
148 .establish = bcm2835_icu_fdt_establish,
149 .disestablish = bcm2835_icu_fdt_disestablish,
150 .intrstr = bcm2835_icu_fdt_intrstr
151 };
152
153 static struct fdtbus_interrupt_controller_func bcm2836mpicu_fdt_funcs = {
154 .establish = bcm2836mp_icu_fdt_establish,
155 .disestablish = bcm2836mp_icu_fdt_disestablish,
156 .intrstr = bcm2836mp_icu_fdt_intrstr
157 };
158
159 struct bcm2836mp_interrupt {
160 bool bi_done;
161 TAILQ_ENTRY(bcm2836mp_interrupt) bi_next;
162 int bi_irq;
163 int bi_ipl;
164 int bi_flags;
165 int (*bi_func)(void *);
166 void *bi_arg;
167 void *bi_ihs[BCM2836_NCPUS];
168 };
169
170 static TAILQ_HEAD(, bcm2836mp_interrupt) bcm2836mp_interrupts =
171 TAILQ_HEAD_INITIALIZER(bcm2836mp_interrupts);
172
173 struct bcm2835icu_irqhandler;
174 struct bcm2835icu_irq;
175 struct bcm2835icu_softc;
176
177 struct bcm2835icu_irqhandler {
178 struct bcm2835icu_irq *ih_irq;
179 int (*ih_fn)(void *);
180 void *ih_arg;
181 TAILQ_ENTRY(bcm2835icu_irqhandler) ih_next;
182 };
183
184 struct bcm2835icu_irq {
185 struct bcm2835icu_softc *intr_sc;
186 void *intr_ih;
187 void *intr_arg;
188 int intr_refcnt;
189 int intr_ipl;
190 int intr_irq;
191 int intr_mpsafe;
192 TAILQ_HEAD(, bcm2835icu_irqhandler) intr_handlers;
193 };
194
195 struct bcm2835icu_softc {
196 device_t sc_dev;
197 bus_space_tag_t sc_iot;
198 bus_space_handle_t sc_ioh;
199
200 struct bcm2835icu_irq *sc_irq[BCM2835_NIRQ];
201
202 int sc_phandle;
203 };
204
205 static struct bcm2835icu_softc *bcml1icu_sc;
206 static struct bcm2835icu_softc *bcmicu_sc;
207
208 #define read_bcm2835reg(o) \
209 bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
210
211 #define write_bcm2835reg(o, v) \
212 bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
213
214
215 #define bcm2835_barrier() \
216 bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
217 BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
218
219 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
220 "(unused 0)", "(unused 1)", "(unused 2)", "timer3",
221 "(unused 4)", "(unused 5)", "(unused 6)", "jpeg",
222 "(unused 8)", "usb", "(unused 10)", "(unused 11)",
223 "(unused 12)", "(unused 13)", "(unused 14)", "(unused 15)",
224 "dma0", "dma1", "dma2", "dma3",
225 "dma4", "dma5", "dma6", "dma7",
226 "dma8", "dma9", "dma10", "dma11",
227 "dma12", "aux", "(unused 30)", "(unused 31)",
228 "(unused 32)", "(unused 33)", "(unused 34)", "(unused 35)",
229 "(unused 36)", "(unused 37)", "(unused 38)", "(unused 39)",
230 "(unused 40)", "(unused 41)", "(unused 42)", "i2c spl slv",
231 "(unused 44)", "pwa0", "pwa1", "(unused 47)",
232 "smi", "gpio[0]", "gpio[1]", "gpio[2]",
233 "gpio[3]", "i2c", "spi", "pcm",
234 "sdhost", "uart", "(unused 58)", "(unused 59)",
235 "(unused 60)", "(unused 61)", "emmc", "(unused 63)",
236 "Timer", "Mailbox", "Doorbell0", "Doorbell1",
237 "GPU0 Halted", "GPU1 Halted", "Illegal #1", "Illegal #0"
238 };
239
240 static const char * const bcm2836mp_sources[BCM2836_NIRQPERCPU] = {
241 "cntpsirq", "cntpnsirq", "cnthpirq", "cntvirq",
242 "mailbox0", "mailbox1", "mailbox2", "mailbox3",
243 "gpu", "pmu"
244 };
245
246 #define BCM2836_INTBIT_GPUPENDING __BIT(8)
247
248 #define BCM2835_INTBIT_PENDING1 __BIT(8)
249 #define BCM2835_INTBIT_PENDING2 __BIT(9)
250 #define BCM2835_INTBIT_ARM __BITS(0,7)
251 #define BCM2835_INTBIT_GPU0 __BITS(10,14)
252 #define BCM2835_INTBIT_GPU1 __BITS(15,20)
253
254 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
255 bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
256
257 static const struct device_compatible_entry compat_data[] = {
258 { .compat = "brcm,bcm2708-armctrl-ic", .value = 0 },
259 { .compat = "brcm,bcm2709-armctrl-ic", .value = 0 },
260 { .compat = "brcm,bcm2835-armctrl-ic", .value = 0 },
261 { .compat = "brcm,bcm2836-armctrl-ic", .value = 0 },
262 { .compat = "brcm,bcm2836-l1-intc", .value = 1 },
263 { 0 }
264 };
265
266 static int
267 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
268 {
269 struct fdt_attach_args * const faa = aux;
270
271 return of_match_compat_data(faa->faa_phandle, compat_data);
272 }
273
274 static void
275 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
276 {
277 struct bcm2835icu_softc * const sc = device_private(self);
278 struct fdt_attach_args * const faa = aux;
279 struct fdtbus_interrupt_controller_func *ifuncs;
280 const struct device_compatible_entry *dce;
281 const int phandle = faa->faa_phandle;
282 bus_addr_t addr;
283 bus_size_t size;
284 bus_space_handle_t ioh;
285 int error;
286
287 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
288 aprint_error(": couldn't get registers\n");
289 return;
290 }
291
292 sc->sc_dev = self;
293 sc->sc_iot = faa->faa_bst;
294
295 if (bus_space_map(sc->sc_iot, addr, size, 0, &ioh) != 0) {
296 aprint_error(": couldn't map device\n");
297 return;
298 }
299
300 sc->sc_ioh = ioh;
301 sc->sc_phandle = phandle;
302
303 dce = of_search_compatible(faa->faa_phandle, compat_data);
304 KASSERT(dce != NULL);
305
306 if (dce->value != 0) {
307 #if defined(MULTIPROCESSOR)
308 aprint_normal(": Multiprocessor");
309 #endif
310 bcml1icu_sc = sc;
311
312 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
313 BCM2836_LOCAL_CONTROL, 0);
314 bus_space_write_4(sc->sc_iot, sc->sc_ioh,
315 BCM2836_LOCAL_PRESCALER, 0x80000000);
316
317 ifuncs = &bcm2836mpicu_fdt_funcs;
318
319 bcm2836mp_intr_init(self, curcpu());
320 arm_fdt_cpu_hatch_register(self, bcm2836mp_intr_init);
321 } else {
322 if (bcml1icu_sc == NULL)
323 arm_fdt_irq_set_handler(bcm2835_irq_handler);
324 bcmicu_sc = sc;
325 sc->sc_ioh = ioh;
326 sc->sc_phandle = phandle;
327 bcm2835_int_base = pic_add(&bcm2835_pic, PIC_IRQBASE_ALLOC);
328 ifuncs = &bcm2835icu_fdt_funcs;
329 }
330
331 error = fdtbus_register_interrupt_controller(self, phandle, ifuncs);
332 if (error != 0) {
333 aprint_error(": couldn't register with fdtbus: %d\n", error);
334 return;
335 }
336 aprint_normal("\n");
337 }
338
339 static void
340 bcm2835_irq_handler(void *frame)
341 {
342 struct cpu_info * const ci = curcpu();
343 const int oldipl = ci->ci_cpl;
344 const cpuid_t cpuid = ci->ci_core_id;
345 const uint32_t oldipl_mask = __BIT(oldipl);
346 int ipl_mask = 0;
347
348 KASSERT(cpuid < BCM2836_NCPUS);
349
350 ci->ci_data.cpu_nintr++;
351
352 bcm2835_barrier();
353 if (cpuid == 0) {
354 ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
355 }
356 #if defined(SOC_BCM2836)
357 ipl_mask |= bcm2836mp_pic_find_pending_irqs(&bcm2836mp_pic[cpuid]);
358 #endif
359
360 /*
361 * Record the pending_ipls and deliver them if we can.
362 */
363 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
364 pic_do_pending_ints(I32_bit, oldipl, frame);
365 }
366
367 static void
368 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
369 uint32_t irq_mask)
370 {
371
372 write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
373 bcm2835_barrier();
374 }
375
376 static void
377 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
378 uint32_t irq_mask)
379 {
380
381 write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
382 bcm2835_barrier();
383 }
384
385 /*
386 * Called with interrupts disabled
387 */
388 static int
389 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
390 {
391 int ipl = 0;
392 uint32_t bpending, gpu0irq, gpu1irq, armirq;
393
394 bcm2835_barrier();
395 bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
396 if (bpending == 0)
397 return 0;
398
399 armirq = bpending & BCM2835_INTBIT_ARM;
400 gpu0irq = bpending & BCM2835_INTBIT_GPU0;
401 gpu1irq = bpending & BCM2835_INTBIT_GPU1;
402
403 if (armirq) {
404 ipl |= pic_mark_pending_sources(pic,
405 BCM2835_INT_BASICBASE - BCM2835_INT_BASE, armirq);
406 }
407
408 if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
409 uint32_t pending1;
410
411 pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
412 ipl |= pic_mark_pending_sources(pic,
413 BCM2835_INT_GPU0BASE - BCM2835_INT_BASE, pending1);
414 }
415 if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
416 uint32_t pending2;
417
418 pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
419 ipl |= pic_mark_pending_sources(pic,
420 BCM2835_INT_GPU1BASE - BCM2835_INT_BASE, pending2);
421 }
422
423 return ipl;
424 }
425
426 static void
427 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
428 {
429
430 /* Nothing really*/
431 KASSERT(is->is_irq < BCM2835_NIRQ);
432 KASSERT(is->is_type == IST_LEVEL);
433 }
434
435 static void
436 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
437 {
438
439 strlcpy(buf, bcm2835_sources[irq], len);
440 }
441
442 static int
443 bcm2835_icu_fdt_decode_irq(u_int *specifier)
444 {
445 u_int base;
446
447 if (!specifier)
448 return -1;
449
450 /* 1st cell is the bank number. 0 = ARM, 1 = GPU0, 2 = GPU1 */
451 /* 2nd cell is the irq relative to that bank */
452
453 const u_int bank = be32toh(specifier[0]);
454 switch (bank) {
455 case 0:
456 base = BCM2835_INT_BASICBASE;
457 break;
458 case 1:
459 base = BCM2835_INT_GPU0BASE;
460 break;
461 case 2:
462 base = BCM2835_INT_GPU1BASE;
463 break;
464 default:
465 return -1;
466 }
467 const u_int off = be32toh(specifier[1]);
468
469 return base + off;
470 }
471
472 static void *
473 bcm2835_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
474 int (*func)(void *), void *arg, const char *xname)
475 {
476 struct bcm2835icu_softc * const sc = device_private(dev);
477 struct bcm2835icu_irq *firq;
478 struct bcm2835icu_irqhandler *firqh;
479 int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
480 int irq, irqidx;
481
482 irq = bcm2835_icu_fdt_decode_irq(specifier);
483 if (irq == -1)
484 return NULL;
485 irqidx = irq - BCM2835_INT_BASE;
486
487 KASSERT(irqidx < BCM2835_NIRQ);
488
489 firq = sc->sc_irq[irqidx];
490 if (firq == NULL) {
491 firq = kmem_alloc(sizeof(*firq), KM_SLEEP);
492 firq->intr_sc = sc;
493 firq->intr_refcnt = 0;
494 firq->intr_arg = arg;
495 firq->intr_ipl = ipl;
496 firq->intr_mpsafe = iflags;
497 firq->intr_irq = irq;
498 TAILQ_INIT(&firq->intr_handlers);
499 if (arg == NULL) {
500 firq->intr_ih = intr_establish_xname(irq, ipl,
501 IST_LEVEL | iflags, func, NULL, xname);
502 } else {
503 firq->intr_ih = intr_establish_xname(irq, ipl,
504 IST_LEVEL | iflags, bcm2835_icu_intr, firq, xname);
505 }
506 if (firq->intr_ih == NULL) {
507 kmem_free(firq, sizeof(*firq));
508 return NULL;
509 }
510 sc->sc_irq[irqidx] = firq;
511 } else {
512 if (firq->intr_arg == NULL || arg == NULL) {
513 device_printf(dev,
514 "cannot share irq with NULL-arg handler\n");
515 return NULL;
516 }
517 if (firq->intr_ipl != ipl) {
518 device_printf(dev,
519 "cannot share irq with different ipl\n");
520 return NULL;
521 }
522 if (firq->intr_mpsafe != iflags) {
523 device_printf(dev,
524 "cannot share irq between mpsafe/non-mpsafe\n");
525 return NULL;
526 }
527 }
528
529 firqh = kmem_alloc(sizeof(*firqh), KM_SLEEP);
530 firqh->ih_irq = firq;
531 firqh->ih_fn = func;
532 firqh->ih_arg = arg;
533
534 firq->intr_refcnt++;
535 TAILQ_INSERT_TAIL(&firq->intr_handlers, firqh, ih_next);
536
537 /*
538 * XXX interrupt_distribute(9) assumes that any interrupt
539 * handle can be used as an input to the MD interrupt_distribute
540 * implementationm, so we are forced to return the handle
541 * we got back from intr_establish(). Upshot is that the
542 * input to bcm2835_icu_fdt_disestablish() is ambiguous for
543 * shared IRQs, rendering them un-disestablishable.
544 */
545
546 return firq->intr_ih;
547 }
548
549 static void
550 bcm2835_icu_fdt_disestablish(device_t dev, void *ih)
551 {
552 struct bcm2835icu_softc * const sc = device_private(dev);
553 struct bcm2835icu_irqhandler *firqh;
554 struct bcm2835icu_irq *firq;
555 u_int n;
556
557 for (n = 0; n < BCM2835_NIRQ; n++) {
558 firq = sc->sc_irq[n];
559 if (firq == NULL || firq->intr_ih != ih)
560 continue;
561
562 KASSERT(firq->intr_refcnt > 0);
563 KASSERT(n == (firq->intr_irq - BCM2835_INT_BASE));
564
565 /* XXX see above */
566 if (firq->intr_refcnt > 1)
567 panic("%s: cannot disestablish shared irq", __func__);
568
569 intr_disestablish(firq->intr_ih);
570
571 firqh = TAILQ_FIRST(&firq->intr_handlers);
572 TAILQ_REMOVE(&firq->intr_handlers, firqh, ih_next);
573 kmem_free(firqh, sizeof(*firqh));
574
575 sc->sc_irq[n] = NULL;
576 kmem_free(firq, sizeof(*firq));
577
578 return;
579 }
580
581 panic("%s: interrupt not established", __func__);
582 }
583
584 static int
585 bcm2835_icu_intr(void *priv)
586 {
587 struct bcm2835icu_irq *firq = priv;
588 struct bcm2835icu_irqhandler *firqh;
589 int handled = 0;
590
591 TAILQ_FOREACH(firqh, &firq->intr_handlers, ih_next) {
592 handled |= firqh->ih_fn(firqh->ih_arg);
593 }
594
595 return handled;
596 }
597
598 static bool
599 bcm2835_icu_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
600 {
601 int irq;
602
603 irq = bcm2835_icu_fdt_decode_irq(specifier);
604 if (irq == -1)
605 return false;
606
607 snprintf(buf, buflen, "icu irq %d", irq);
608
609 return true;
610 }
611
612 #define BCM2836MP_TIMER_IRQS __BITS(3,0)
613 #define BCM2836MP_MAILBOX_IRQS __BITS(4,7)
614 #define BCM2836MP_GPU_IRQ __BIT(8)
615 #define BCM2836MP_PMU_IRQ __BIT(9)
616 #define BCM2836MP_ALL_IRQS (BCM2836MP_TIMER_IRQS | BCM2836MP_MAILBOX_IRQS | BCM2836MP_GPU_IRQ | BCM2836MP_PMU_IRQ)
617
618 static void
619 bcm2836mp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
620 uint32_t irq_mask)
621 {
622 const bus_space_tag_t iot = bcml1icu_sc->sc_iot;
623 const bus_space_handle_t ioh = bcml1icu_sc->sc_ioh;
624 const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
625
626 KASSERT(cpuid < BCM2836_NCPUS);
627 KASSERT(irqbase == 0);
628
629 if (irq_mask & BCM2836MP_TIMER_IRQS) {
630 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
631 uint32_t val = bus_space_read_4(iot, ioh,
632 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
633 val |= mask;
634 bus_space_write_4(iot, ioh,
635 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
636 val);
637 bus_space_barrier(iot, ioh,
638 BCM2836_LOCAL_TIMER_IRQ_CONTROL_BASE,
639 BCM2836_LOCAL_TIMER_IRQ_CONTROL_SIZE,
640 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
641 }
642 if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
643 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
644 uint32_t val = bus_space_read_4(iot, ioh,
645 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
646 val |= mask;
647 bus_space_write_4(iot, ioh,
648 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
649 val);
650 bus_space_barrier(iot, ioh,
651 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_BASE,
652 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_SIZE,
653 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
654 }
655 if (irq_mask & BCM2836MP_PMU_IRQ) {
656 bus_space_write_4(iot, ioh, BCM2836_LOCAL_PM_ROUTING_SET,
657 __BIT(cpuid));
658 bus_space_barrier(iot, ioh, BCM2836_LOCAL_PM_ROUTING_SET, 4,
659 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
660 }
661
662 return;
663 }
664
665 static void
666 bcm2836mp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
667 uint32_t irq_mask)
668 {
669 const bus_space_tag_t iot = bcml1icu_sc->sc_iot;
670 const bus_space_handle_t ioh = bcml1icu_sc->sc_ioh;
671 const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
672
673 KASSERT(cpuid < BCM2836_NCPUS);
674 KASSERT(irqbase == 0);
675
676 if (irq_mask & BCM2836MP_TIMER_IRQS) {
677 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
678 uint32_t val = bus_space_read_4(iot, ioh,
679 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
680 val &= ~mask;
681 bus_space_write_4(iot, ioh,
682 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
683 val);
684 }
685 if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
686 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
687 uint32_t val = bus_space_read_4(iot, ioh,
688 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
689 val &= ~mask;
690 bus_space_write_4(iot, ioh,
691 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
692 val);
693 }
694 if (irq_mask & BCM2836MP_PMU_IRQ) {
695 bus_space_write_4(iot, ioh, BCM2836_LOCAL_PM_ROUTING_CLR,
696 __BIT(cpuid));
697 }
698
699 bcm2835_barrier();
700 return;
701 }
702
703 static int
704 bcm2836mp_pic_find_pending_irqs(struct pic_softc *pic)
705 {
706 struct cpu_info * const ci = curcpu();
707 const cpuid_t cpuid = ci->ci_core_id;
708 uint32_t lpending;
709 int ipl = 0;
710
711 KASSERT(cpuid < BCM2836_NCPUS);
712 KASSERT(pic == &bcm2836mp_pic[cpuid]);
713
714 bcm2835_barrier();
715
716 lpending = bus_space_read_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
717 BCM2836_LOCAL_INTC_IRQPENDINGN(cpuid));
718
719 lpending &= ~BCM2836_INTBIT_GPUPENDING;
720 const uint32_t allirqs = lpending & BCM2836MP_ALL_IRQS;
721 if (allirqs) {
722 ipl |= pic_mark_pending_sources(pic, 0, allirqs);
723 }
724
725 return ipl;
726 }
727
728 static void
729 bcm2836mp_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
730 {
731 /* Nothing really*/
732 KASSERT(is->is_irq >= 0);
733 KASSERT(is->is_irq < BCM2836_NIRQPERCPU);
734 }
735
736 static void
737 bcm2836mp_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
738 {
739
740 irq %= BCM2836_NIRQPERCPU;
741 strlcpy(buf, bcm2836mp_sources[irq], len);
742 }
743
744
745 #if defined(MULTIPROCESSOR)
746 static void bcm2836mp_cpu_init(struct pic_softc *pic, struct cpu_info *ci)
747 {
748 const cpuid_t cpuid = ci->ci_core_id;
749
750 KASSERT(cpuid < BCM2836_NCPUS);
751
752 /* Enable IRQ and not FIQ */
753 bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
754 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid), 1);
755 }
756
757 static void
758 bcm2836mp_send_ipi(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
759 {
760 KASSERT(pic != NULL);
761 KASSERT(pic != &bcm2835_pic);
762 KASSERT(pic->pic_cpus != NULL);
763
764 const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
765 KASSERT(cpuid < BCM2836_NCPUS);
766
767 bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
768 BCM2836_LOCAL_MAILBOX0_SETN(cpuid), __BIT(ipi));
769 }
770
771 int
772 bcm2836mp_ipi_handler(void *priv)
773 {
774 const struct cpu_info *ci = curcpu();
775 const cpuid_t cpuid = ci->ci_core_id;
776 uint32_t ipimask, bit;
777
778 KASSERT(cpuid < BCM2836_NCPUS);
779
780 ipimask = bus_space_read_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
781 BCM2836_LOCAL_MAILBOX0_CLRN(cpuid));
782 bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
783 BCM2836_LOCAL_MAILBOX0_CLRN(cpuid), ipimask);
784
785 while ((bit = ffs(ipimask)) > 0) {
786 const u_int ipi = bit - 1;
787 switch (ipi) {
788 case IPI_AST:
789 pic_ipi_ast(priv);
790 break;
791 case IPI_NOP:
792 pic_ipi_nop(priv);
793 break;
794 #ifdef __HAVE_PREEMPTION
795 case IPI_KPREEMPT:
796 pic_ipi_kpreempt(priv);
797 break;
798 #endif
799 case IPI_XCALL:
800 pic_ipi_xcall(priv);
801 break;
802 case IPI_GENERIC:
803 pic_ipi_generic(priv);
804 break;
805 case IPI_SHOOTDOWN:
806 pic_ipi_shootdown(priv);
807 break;
808 #ifdef DDB
809 case IPI_DDB:
810 pic_ipi_ddb(priv);
811 break;
812 #endif
813 }
814 ipimask &= ~__BIT(ipi);
815 }
816
817 return 1;
818 }
819 #endif
820
821 static void
822 bcm2836mp_intr_init(void *priv, struct cpu_info *ci)
823 {
824 const cpuid_t cpuid = ci->ci_core_id;
825 struct pic_softc * const pic = &bcm2836mp_pic[cpuid];
826
827 KASSERT(cpuid < BCM2836_NCPUS);
828
829 #if defined(MULTIPROCESSOR)
830 pic->pic_cpus = ci->ci_kcpuset;
831
832 /*
833 * Append "#n" to avoid duplication of .pic_name[]
834 * It should be a unique id for intr_get_source()
835 */
836 char suffix[sizeof("#00000")];
837 snprintf(suffix, sizeof(suffix), "#%lu", cpuid);
838 strlcat(pic->pic_name, suffix, sizeof(pic->pic_name));
839 #endif
840 bcm2836mp_int_base[cpuid] = pic_add(pic, PIC_IRQBASE_ALLOC);
841
842 #if defined(MULTIPROCESSOR)
843 intr_establish(BCM2836_INT_MAILBOX0_CPUN(cpuid), IPL_HIGH,
844 IST_LEVEL | IST_MPSAFE, bcm2836mp_ipi_handler, NULL);
845
846 struct bcm2836mp_interrupt *bip;
847 TAILQ_FOREACH(bip, &bcm2836mp_interrupts, bi_next) {
848 if (bip->bi_done)
849 continue;
850
851 const int irq = BCM2836_INT_BASECPUN(cpuid) + bip->bi_irq;
852 void *ih = intr_establish(irq, bip->bi_ipl,
853 IST_LEVEL | bip->bi_flags, bip->bi_func, bip->bi_arg);
854
855 bip->bi_ihs[cpuid] = ih;
856 }
857 #endif
858 }
859
860 static int
861 bcm2836mp_icu_fdt_decode_irq(u_int *specifier)
862 {
863
864 if (!specifier)
865 return -1;
866 return be32toh(specifier[0]);
867 }
868
869 static void *
870 bcm2836mp_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
871 int (*func)(void *), void *arg, const char *xname)
872 {
873 int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
874 struct bcm2836mp_interrupt *bip;
875 void *ih;
876
877 int irq = bcm2836mp_icu_fdt_decode_irq(specifier);
878 if (irq == -1)
879 return NULL;
880
881 TAILQ_FOREACH(bip, &bcm2836mp_interrupts, bi_next) {
882 if (irq == bip->bi_irq)
883 return NULL;
884 }
885
886 bip = kmem_alloc(sizeof(*bip), KM_SLEEP);
887 if (bip == NULL)
888 return NULL;
889
890 bip->bi_done = false;
891 bip->bi_irq = irq;
892 bip->bi_ipl = ipl;
893 bip->bi_flags = IST_LEVEL | iflags;
894 bip->bi_func = func;
895 bip->bi_arg = arg;
896
897 /*
898 * If we're not cold and the BPs have been started then we can
899 * register the interrupt for all CPUs now, e.g. PMU
900 */
901 if (!cold) {
902 for (cpuid_t cpuid = 0; cpuid < BCM2836_NCPUS; cpuid++) {
903 ih = intr_establish_xname(
904 BCM2836_INT_BASECPUN(cpuid) + irq, ipl,
905 IST_LEVEL | iflags, func, arg, xname);
906 if (!ih) {
907 kmem_free(bip, sizeof(*bip));
908 return NULL;
909 }
910 bip->bi_ihs[cpuid] = ih;
911
912 }
913 bip->bi_done = true;
914 ih = bip->bi_ihs[0];
915 goto done;
916 }
917
918 /*
919 * Otherwise we can only establish the interrupt for the BP and
920 * delay until bcm2836mp_intr_init is called for each AP, e.g.
921 * gtmr
922 */
923 ih = intr_establish_xname(BCM2836_INT_BASECPUN(0) + irq, ipl,
924 IST_LEVEL | iflags, func, arg, xname);
925 if (!ih) {
926 kmem_free(bip, sizeof(*bip));
927 return NULL;
928 }
929
930 bip->bi_ihs[0] = ih;
931 for (cpuid_t cpuid = 1; cpuid < BCM2836_NCPUS; cpuid++)
932 bip->bi_ihs[cpuid] = NULL;
933
934 done:
935 TAILQ_INSERT_TAIL(&bcm2836mp_interrupts, bip, bi_next);
936
937 /*
938 * Return the intr_establish handle for cpu 0 for API compatibility.
939 * Any cpu would do here as these sources don't support set_affinity
940 * when the handle is used in interrupt_distribute(9)
941 */
942 return ih;
943 }
944
945 static void
946 bcm2836mp_icu_fdt_disestablish(device_t dev, void *ih)
947 {
948 struct bcm2836mp_interrupt *bip;
949
950 TAILQ_FOREACH(bip, &bcm2836mp_interrupts, bi_next) {
951 if (bip->bi_ihs[0] == ih)
952 break;
953 }
954
955 if (bip == NULL)
956 return;
957
958 for (cpuid_t cpuid = 0; cpuid < BCM2836_NCPUS; cpuid++)
959 intr_disestablish(bip->bi_ihs[cpuid]);
960
961 TAILQ_REMOVE(&bcm2836mp_interrupts, bip, bi_next);
962
963 kmem_free(bip, sizeof(*bip));
964 }
965
966 static bool
967 bcm2836mp_icu_fdt_intrstr(device_t dev, u_int *specifier, char *buf,
968 size_t buflen)
969 {
970 int irq;
971
972 irq = bcm2836mp_icu_fdt_decode_irq(specifier);
973 if (irq == -1)
974 return false;
975
976 snprintf(buf, buflen, "local_intc irq %d", irq);
977
978 return true;
979 }
980