xref: /netbsd-src/sys/arch/arm/broadcom/bcm2835_intr.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: bcm2835_intr.c,v 1.15 2017/12/10 21:38:26 skrll Exp $	*/
2 
3 /*-
4  * Copyright (c) 2012, 2015 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.15 2017/12/10 21:38:26 skrll 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/proc.h>
44 
45 #include <dev/fdt/fdtvar.h>
46 
47 #include <machine/intr.h>
48 
49 #include <arm/locore.h>
50 
51 #include <arm/pic/picvar.h>
52 #include <arm/cortex/gtmr_var.h>
53 
54 #include <arm/broadcom/bcm2835_intr.h>
55 #include <arm/broadcom/bcm2835reg.h>
56 #include <arm/broadcom/bcm2835var.h>
57 
58 #include <arm/fdt/arm_fdtvar.h>
59 
60 static void bcm2835_irq_handler(void *);
61 static void bcm2836mp_intr_init(void *, struct cpu_info *);
62 
63 static void bcm2835_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
64 static void bcm2835_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
65 static int bcm2835_pic_find_pending_irqs(struct pic_softc *);
66 static void bcm2835_pic_establish_irq(struct pic_softc *, struct intrsource *);
67 static void bcm2835_pic_source_name(struct pic_softc *, int, char *,
68     size_t);
69 
70 static void bcm2836mp_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
71 static void bcm2836mp_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
72 static int bcm2836mp_pic_find_pending_irqs(struct pic_softc *);
73 static void bcm2836mp_pic_establish_irq(struct pic_softc *, struct intrsource *);
74 static void bcm2836mp_pic_source_name(struct pic_softc *, int, char *,
75     size_t);
76 #ifdef MULTIPROCESSOR
77 int bcm2836mp_ipi_handler(void *);
78 static void bcm2836mp_cpu_init(struct pic_softc *, struct cpu_info *);
79 static void bcm2836mp_send_ipi(struct pic_softc *, const kcpuset_t *, u_long);
80 #endif
81 
82 static int bcm2835_icu_fdt_decode_irq(u_int *);
83 static void *bcm2835_icu_fdt_establish(device_t, u_int *, int, int,
84     int (*)(void *), void *);
85 static void bcm2835_icu_fdt_disestablish(device_t, void *);
86 static bool bcm2835_icu_fdt_intrstr(device_t, u_int *, char *, size_t);
87 
88 static int bcm2836mp_icu_fdt_decode_irq(u_int *);
89 static void *bcm2836mp_icu_fdt_establish(device_t, u_int *, int, int,
90     int (*)(void *), void *);
91 static void bcm2836mp_icu_fdt_disestablish(device_t, void *);
92 static bool bcm2836mp_icu_fdt_intrstr(device_t, u_int *, char *, size_t);
93 
94 static int  bcm2835_icu_match(device_t, cfdata_t, void *);
95 static void bcm2835_icu_attach(device_t, device_t, void *);
96 
97 static void
98 bcm2835_set_priority(struct pic_softc *pic, int ipl)
99 {
100 }
101 
102 static struct pic_ops bcm2835_picops = {
103 	.pic_unblock_irqs = bcm2835_pic_unblock_irqs,
104 	.pic_block_irqs = bcm2835_pic_block_irqs,
105 	.pic_find_pending_irqs = bcm2835_pic_find_pending_irqs,
106 	.pic_establish_irq = bcm2835_pic_establish_irq,
107 	.pic_source_name = bcm2835_pic_source_name,
108 	.pic_set_priority = bcm2835_set_priority,
109 };
110 
111 struct pic_softc bcm2835_pic = {
112 	.pic_ops = &bcm2835_picops,
113 	.pic_maxsources = BCM2835_NIRQ,
114 	.pic_name = "bcm2835 pic",
115 };
116 
117 static struct pic_ops bcm2836mp_picops = {
118 	.pic_unblock_irqs = bcm2836mp_pic_unblock_irqs,
119 	.pic_block_irqs = bcm2836mp_pic_block_irqs,
120 	.pic_find_pending_irqs = bcm2836mp_pic_find_pending_irqs,
121 	.pic_establish_irq = bcm2836mp_pic_establish_irq,
122 	.pic_source_name = bcm2836mp_pic_source_name,
123 #if defined(MULTIPROCESSOR)
124 	.pic_cpu_init = bcm2836mp_cpu_init,
125 	.pic_ipi_send = bcm2836mp_send_ipi,
126 #endif
127 };
128 
129 struct pic_softc bcm2836mp_pic[BCM2836_NCPUS] = {
130 	[0 ... BCM2836_NCPUS - 1] = {
131 		.pic_ops = &bcm2836mp_picops,
132 		.pic_maxsources = BCM2836_NIRQPERCPU,
133 		.pic_name = "bcm2836 pic",
134 	}
135 };
136 
137 static struct fdtbus_interrupt_controller_func bcm2835icu_fdt_funcs = {
138 	.establish = bcm2835_icu_fdt_establish,
139 	.disestablish = bcm2835_icu_fdt_disestablish,
140 	.intrstr = bcm2835_icu_fdt_intrstr
141 };
142 
143 static struct fdtbus_interrupt_controller_func bcm2836mpicu_fdt_funcs = {
144 	.establish = bcm2836mp_icu_fdt_establish,
145 	.disestablish = bcm2836mp_icu_fdt_disestablish,
146 	.intrstr = bcm2836mp_icu_fdt_intrstr
147 };
148 
149 struct bcm2835icu_softc {
150 	device_t		sc_dev;
151 	bus_space_tag_t		sc_iot;
152 	bus_space_handle_t	sc_ioh;
153 
154 	int sc_phandle;
155 };
156 
157 struct bcm2835icu_softc *bcml1icu_sc;
158 struct bcm2835icu_softc *bcmicu_sc;
159 
160 #define read_bcm2835reg(o)	\
161 	bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
162 
163 #define write_bcm2835reg(o, v)	\
164 	bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
165 
166 
167 #define bcm2835_barrier() \
168 	bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
169 	    BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
170 
171 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
172 	"(unused  0)",	"(unused  1)",	"(unused  2)",	"timer3",
173 	"(unused  4)",	"(unused  5)",	"(unused  6)",	"jpeg",
174 	"(unused  8)",	"usb",		"(unused 10)",	"(unused 11)",
175 	"(unused 12)",	"(unused 13)",	"(unused 14)",	"(unused 15)",
176 	"dma0",		"dma1",		"dma2",		"dma3",
177 	"dma4",		"dma5",		"dma6",		"dma7",
178 	"dma8",		"dma9",		"dma10",	"dma11",
179 	"dma12",	"aux",		"(unused 30)",	"(unused 31)",
180 	"(unused 32)",	"(unused 33)",	"(unused 34)",	"(unused 35)",
181 	"(unused 36)",	"(unused 37)",	"(unused 38)",	"(unused 39)",
182 	"(unused 40)",	"(unused 41)",	"(unused 42)",	"i2c spl slv",
183 	"(unused 44)",	"pwa0",		"pwa1",		"(unused 47)",
184 	"smi",		"gpio[0]",	"gpio[1]",	"gpio[2]",
185 	"gpio[3]",	"i2c",		"spi",		"pcm",
186 	"sdhost",	"uart",		"(unused 58)",	"(unused 59)",
187 	"(unused 60)",	"(unused 61)",	"emmc",		"(unused 63)",
188 	"Timer",	"Mailbox",	"Doorbell0",	"Doorbell1",
189 	"GPU0 Halted",	"GPU1 Halted",	"Illegal #1",	"Illegal #0"
190 };
191 
192 static const char * const bcm2836mp_sources[BCM2836_NIRQPERCPU] = {
193 	"cntpsirq",	"cntpnsirq",	"cnthpirq",	"cntvirq",
194 	"mailbox0",	"mailbox1",	"mailbox2",	"mailbox3",
195 };
196 
197 #define	BCM2836_INTBIT_GPUPENDING	__BIT(8)
198 
199 #define	BCM2835_INTBIT_PENDING1		__BIT(8)
200 #define	BCM2835_INTBIT_PENDING2		__BIT(9)
201 #define	BCM2835_INTBIT_ARM		__BITS(0,7)
202 #define	BCM2835_INTBIT_GPU0		__BITS(10,14)
203 #define	BCM2835_INTBIT_GPU1		__BITS(15,20)
204 
205 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
206     bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
207 
208 static int
209 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
210 {
211 	const char * const compatible[] = {
212 	    "brcm,bcm2708-armctrl-ic",
213 	    "brcm,bcm2709-armctrl-ic",
214 	    "brcm,bcm2835-armctrl-ic",
215 	    "brcm,bcm2836-armctrl-ic",
216 	    "brcm,bcm2836-l1-intc",
217 	    NULL
218 	};
219 	struct fdt_attach_args * const faa = aux;
220 
221 	return of_match_compatible(faa->faa_phandle, compatible);
222 }
223 
224 static void
225 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
226 {
227 	struct bcm2835icu_softc * const sc = device_private(self);
228 	struct fdt_attach_args * const faa = aux;
229 	struct fdtbus_interrupt_controller_func *ifuncs;
230 	const int phandle = faa->faa_phandle;
231 	bus_addr_t addr;
232 	bus_size_t size;
233 	bus_space_handle_t ioh;
234 	int error;
235 
236 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
237 		aprint_error(": couldn't get registers\n");
238 		return;
239 	}
240 
241 	sc->sc_dev = self;
242 	sc->sc_iot = faa->faa_bst;
243 
244 	if (bus_space_map(sc->sc_iot, addr, size, 0, &ioh) != 0) {
245 		aprint_error(": couldn't map device\n");
246 		return;
247 	}
248 
249 	sc->sc_ioh = ioh;
250 	sc->sc_phandle = phandle;
251 
252 	const char * const local_intc[] = { "brcm,bcm2836-l1-intc", NULL };
253 	if (of_match_compatible(faa->faa_phandle, local_intc)) {
254 #if defined(MULTIPROCESSOR)
255 		aprint_normal(": Multiprocessor");
256 #endif
257 		bcml1icu_sc = sc;
258 
259 		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
260 		    BCM2836_LOCAL_CONTROL, 0);
261 		bus_space_write_4(sc->sc_iot, sc->sc_ioh,
262 		    BCM2836_LOCAL_PRESCALER, 0x80000000);
263 
264 		ifuncs = &bcm2836mpicu_fdt_funcs;
265 
266 		bcm2836mp_intr_init(self, curcpu());
267 		arm_fdt_cpu_hatch_register(self, bcm2836mp_intr_init);
268 	} else {
269 		if (bcml1icu_sc == NULL)
270 			arm_fdt_irq_set_handler(bcm2835_irq_handler);
271 		bcmicu_sc = sc;
272 		sc->sc_ioh = ioh;
273 		sc->sc_phandle = phandle;
274 		pic_add(&bcm2835_pic, BCM2835_INT_BASE);
275 		ifuncs = &bcm2835icu_fdt_funcs;
276 	}
277 
278 	error = fdtbus_register_interrupt_controller(self, phandle, ifuncs);
279 	if (error != 0) {
280 		aprint_error(": couldn't register with fdtbus: %d\n", error);
281 		return;
282 	}
283 	aprint_normal("\n");
284 }
285 
286 static void
287 bcm2835_irq_handler(void *frame)
288 {
289 	struct cpu_info * const ci = curcpu();
290 	const int oldipl = ci->ci_cpl;
291 	const cpuid_t cpuid = ci->ci_cpuid;
292 	const uint32_t oldipl_mask = __BIT(oldipl);
293 	int ipl_mask = 0;
294 
295 	ci->ci_data.cpu_nintr++;
296 
297 	bcm2835_barrier();
298 	if (cpuid == 0) {
299 		ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
300 	}
301 #if defined(SOC_BCM2836)
302 	ipl_mask |= bcm2836mp_pic_find_pending_irqs(&bcm2836mp_pic[cpuid]);
303 #endif
304 
305 	/*
306 	 * Record the pending_ipls and deliver them if we can.
307 	 */
308 	if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
309 		pic_do_pending_ints(I32_bit, oldipl, frame);
310 }
311 
312 static void
313 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
314     uint32_t irq_mask)
315 {
316 
317 	write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
318 	bcm2835_barrier();
319 }
320 
321 static void
322 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
323     uint32_t irq_mask)
324 {
325 
326 	write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
327 	bcm2835_barrier();
328 }
329 
330 /*
331  * Called with interrupts disabled
332  */
333 static int
334 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
335 {
336 	int ipl = 0;
337 	uint32_t bpending, gpu0irq, gpu1irq, armirq;
338 
339 	bcm2835_barrier();
340 	bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
341 	if (bpending == 0)
342 		return 0;
343 
344 	armirq = bpending & BCM2835_INTBIT_ARM;
345 	gpu0irq = bpending & BCM2835_INTBIT_GPU0;
346 	gpu1irq = bpending & BCM2835_INTBIT_GPU1;
347 
348 	if (armirq) {
349 		ipl |= pic_mark_pending_sources(pic,
350 		    BCM2835_INT_BASICBASE - BCM2835_INT_BASE, armirq);
351 	}
352 
353 	if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
354 		uint32_t pending1;
355 
356 		pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
357 		ipl |= pic_mark_pending_sources(pic,
358 		    BCM2835_INT_GPU0BASE - BCM2835_INT_BASE, pending1);
359 	}
360 	if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
361 		uint32_t pending2;
362 
363 		pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
364 		ipl |= pic_mark_pending_sources(pic,
365 		    BCM2835_INT_GPU1BASE - BCM2835_INT_BASE, pending2);
366 	}
367 
368 	return ipl;
369 }
370 
371 static void
372 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
373 {
374 
375 	/* Nothing really*/
376 	KASSERT(is->is_irq < BCM2835_NIRQ);
377 	KASSERT(is->is_type == IST_LEVEL);
378 }
379 
380 static void
381 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
382 {
383 
384 	strlcpy(buf, bcm2835_sources[irq], len);
385 }
386 
387 static int
388 bcm2835_icu_fdt_decode_irq(u_int *specifier)
389 {
390 	u_int base;
391 
392 	if (!specifier)
393 		return -1;
394 
395 	/* 1st cell is the bank number. 0 = ARM, 1 = GPU0, 2 = GPU1 */
396 	/* 2nd cell is the irq relative to that bank */
397 
398 	const u_int bank = be32toh(specifier[0]);
399 	switch (bank) {
400 	case 0:
401 		base = BCM2835_INT_BASICBASE;
402 		break;
403 	case 1:
404 		base = BCM2835_INT_GPU0BASE;
405 		break;
406 	case 2:
407 		base = BCM2835_INT_GPU1BASE;
408 		break;
409 	default:
410 		return -1;
411 	}
412 	const u_int off = be32toh(specifier[1]);
413 
414 	return base + off;
415 }
416 
417 static void *
418 bcm2835_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
419     int (*func)(void *), void *arg)
420 {
421 	int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
422 	int irq;
423 
424 	irq = bcm2835_icu_fdt_decode_irq(specifier);
425 	if (irq == -1)
426 		return NULL;
427 
428 	return intr_establish(irq, ipl, IST_LEVEL | iflags, func, arg);
429 }
430 
431 static void
432 bcm2835_icu_fdt_disestablish(device_t dev, void *ih)
433 {
434 	intr_disestablish(ih);
435 }
436 
437 static bool
438 bcm2835_icu_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
439 {
440 	int irq;
441 
442 	irq = bcm2835_icu_fdt_decode_irq(specifier);
443 	if (irq == -1)
444 		return false;
445 
446 	snprintf(buf, buflen, "icu irq %d", irq);
447 
448 	return true;
449 }
450 
451 #define	BCM2836MP_TIMER_IRQS	__BITS(3,0)
452 #define	BCM2836MP_MAILBOX_IRQS	__BITS(4,4)
453 
454 #define	BCM2836MP_ALL_IRQS	(BCM2836MP_TIMER_IRQS | BCM2836MP_MAILBOX_IRQS)
455 
456 static void
457 bcm2836mp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
458     uint32_t irq_mask)
459 {
460 	struct cpu_info * const ci = curcpu();
461 	const cpuid_t cpuid = ci->ci_cpuid;
462 	const bus_space_tag_t iot = bcml1icu_sc->sc_iot;
463 	const bus_space_handle_t ioh = bcml1icu_sc->sc_ioh;
464 
465 	KASSERT(pic == &bcm2836mp_pic[cpuid]);
466 	KASSERT(irqbase == 0);
467 
468 	if (irq_mask & BCM2836MP_TIMER_IRQS) {
469 		uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
470 		uint32_t val = bus_space_read_4(iot, ioh,
471 		    BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
472 		val |= mask;
473 		bus_space_write_4(iot, ioh,
474 		    BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
475 		    val);
476 		bus_space_barrier(iot, ioh,
477 		    BCM2836_LOCAL_TIMER_IRQ_CONTROL_BASE,
478 		    BCM2836_LOCAL_TIMER_IRQ_CONTROL_SIZE,
479 		    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
480 	}
481 	if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
482 		uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
483 		uint32_t val = bus_space_read_4(iot, ioh,
484 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
485 		val |= mask;
486 		bus_space_write_4(iot, ioh,
487 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
488 		    val);
489 		bus_space_barrier(iot, ioh,
490 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_BASE,
491 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_SIZE,
492 		    BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
493 	}
494 
495 	return;
496 }
497 
498 static void
499 bcm2836mp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
500     uint32_t irq_mask)
501 {
502 	struct cpu_info * const ci = curcpu();
503 	const cpuid_t cpuid = ci->ci_cpuid;
504 	const bus_space_tag_t iot = bcml1icu_sc->sc_iot;
505 	const bus_space_handle_t ioh = bcml1icu_sc->sc_ioh;
506 
507 	KASSERT(pic == &bcm2836mp_pic[cpuid]);
508 	KASSERT(irqbase == 0);
509 
510 	if (irq_mask & BCM2836MP_TIMER_IRQS) {
511 		uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
512 		uint32_t val = bus_space_read_4(iot, ioh,
513 		    BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
514 		val &= ~mask;
515 		bus_space_write_4(iot, ioh,
516 		    BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
517 		    val);
518 	}
519 	if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
520 		uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
521 		uint32_t val = bus_space_read_4(iot, ioh,
522 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
523 		val &= ~mask;
524 		bus_space_write_4(iot, ioh,
525 		    BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
526 		    val);
527 	}
528 
529 	bcm2835_barrier();
530 	return;
531 }
532 
533 static int
534 bcm2836mp_pic_find_pending_irqs(struct pic_softc *pic)
535 {
536 	struct cpu_info * const ci = curcpu();
537 	const cpuid_t cpuid = ci->ci_cpuid;
538 	uint32_t lpending;
539 	int ipl = 0;
540 
541 	KASSERT(pic == &bcm2836mp_pic[cpuid]);
542 
543 	bcm2835_barrier();
544 
545 	lpending = bus_space_read_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
546 	    BCM2836_LOCAL_INTC_IRQPENDINGN(cpuid));
547 
548 	lpending &= ~BCM2836_INTBIT_GPUPENDING;
549 	if (lpending & BCM2836MP_ALL_IRQS) {
550 		ipl |= pic_mark_pending_sources(pic, 0 /* BCM2836_INT_LOCALBASE */,
551 		    lpending & BCM2836MP_ALL_IRQS);
552 	}
553 
554 	return ipl;
555 }
556 
557 static void
558 bcm2836mp_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
559 {
560 	/* Nothing really*/
561 	KASSERT(is->is_irq >= 0);
562 	KASSERT(is->is_irq < BCM2836_NIRQPERCPU);
563 }
564 
565 static void
566 bcm2836mp_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
567 {
568 
569 	irq %= BCM2836_NIRQPERCPU;
570 	strlcpy(buf, bcm2836mp_sources[irq], len);
571 }
572 
573 
574 #if defined(MULTIPROCESSOR)
575 static void bcm2836mp_cpu_init(struct pic_softc *pic, struct cpu_info *ci)
576 {
577 
578 	/* Enable IRQ and not FIQ */
579 	bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
580 	    BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(ci->ci_cpuid), 1);
581 }
582 
583 static void
584 bcm2836mp_send_ipi(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
585 {
586 	KASSERT(pic != NULL);
587 	KASSERT(pic != &bcm2835_pic);
588 	KASSERT(pic->pic_cpus != NULL);
589 
590 	const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
591 
592 	bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
593 	    BCM2836_LOCAL_MAILBOX0_SETN(cpuid), __BIT(ipi));
594 }
595 
596 int
597 bcm2836mp_ipi_handler(void *priv)
598 {
599 	const struct cpu_info *ci = curcpu();
600 	const cpuid_t cpuid = ci->ci_cpuid;
601 	uint32_t ipimask, bit;
602 
603 	ipimask = bus_space_read_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
604 	    BCM2836_LOCAL_MAILBOX0_CLRN(cpuid));
605 	bus_space_write_4(bcml1icu_sc->sc_iot, bcml1icu_sc->sc_ioh,
606 	    BCM2836_LOCAL_MAILBOX0_CLRN(cpuid), ipimask);
607 
608 	while ((bit = ffs(ipimask)) > 0) {
609 		const u_int ipi = bit - 1;
610 		switch (ipi) {
611 		case IPI_AST:
612 			pic_ipi_ast(priv);
613 			break;
614 		case IPI_NOP:
615 			pic_ipi_nop(priv);
616 			break;
617 #ifdef __HAVE_PREEMPTION
618 		case IPI_KPREEMPT:
619 			pic_ipi_kpreempt(priv);
620 			break;
621 #endif
622 		case IPI_XCALL:
623 			pic_ipi_xcall(priv);
624 			break;
625 		case IPI_GENERIC:
626 			pic_ipi_generic(priv);
627 			break;
628 		case IPI_SHOOTDOWN:
629 			pic_ipi_shootdown(priv);
630 			break;
631 #ifdef DDB
632 		case IPI_DDB:
633 			pic_ipi_ddb(priv);
634 			break;
635 #endif
636 		}
637 		ipimask &= ~__BIT(ipi);
638 	}
639 
640 	return 1;
641 }
642 #endif
643 
644 static void
645 bcm2836mp_intr_init(void *priv, struct cpu_info *ci)
646 {
647 	const cpuid_t cpuid = ci->ci_cpuid;
648 	struct pic_softc * const pic = &bcm2836mp_pic[cpuid];
649 
650 #if defined(MULTIPROCESSOR)
651 	pic->pic_cpus = ci->ci_kcpuset;
652 #endif
653 	pic_add(pic, BCM2836_INT_BASECPUN(cpuid));
654 
655 #if defined(MULTIPROCESSOR)
656 	intr_establish(BCM2836_INT_MAILBOX0_CPUN(cpuid), IPL_HIGH,
657 	    IST_LEVEL | IST_MPSAFE, bcm2836mp_ipi_handler, NULL);
658 #endif
659 	/* clock interrupt will attach with gtmr */
660 	if (cpuid == 0)
661 		return;
662 #if defined(SOC_BCM2836)
663 	intr_establish(BCM2836_INT_CNTVIRQ_CPUN(cpuid), IPL_CLOCK,
664 	    IST_LEVEL | IST_MPSAFE, gtmr_intr, NULL);
665 
666 #endif
667 }
668 
669 static int
670 bcm2836mp_icu_fdt_decode_irq(u_int *specifier)
671 {
672 	if (!specifier)
673 		return -1;
674 	return be32toh(specifier[0]) + BCM2836_INT_LOCALBASE;
675 }
676 
677 static void *
678 bcm2836mp_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
679     int (*func)(void *), void *arg)
680 {
681 	int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
682 	int irq;
683 
684 	irq = bcm2836mp_icu_fdt_decode_irq(specifier);
685 	if (irq == -1)
686 		return NULL;
687 
688 	return intr_establish(irq, ipl, IST_LEVEL | iflags, func, arg);
689 }
690 
691 static void
692 bcm2836mp_icu_fdt_disestablish(device_t dev, void *ih)
693 {
694 	intr_disestablish(ih);
695 }
696 
697 static bool
698 bcm2836mp_icu_fdt_intrstr(device_t dev, u_int *specifier, char *buf,
699     size_t buflen)
700 {
701 	int irq;
702 
703 	irq = bcm2836mp_icu_fdt_decode_irq(specifier);
704 	if (irq == -1)
705 		return false;
706 
707 	snprintf(buf, buflen, "local_intc irq %d", irq);
708 
709 	return true;
710 }
711