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