1/* $OpenBSD: bcm2836_intr.c,v 1.15 2022/12/21 22:30:42 kettenis Exp $ */
2/*
3 * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org>
4 * Copyright (c) 2015 Patrick Wildt <patrick@blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>
20#include <sys/systm.h>
21#include <sys/queue.h>
22#include <sys/malloc.h>
23#include <sys/device.h>
24#include <sys/evcount.h>
25
26#include <machine/bus.h>
27#include <machine/fdt.h>
28
29#include <dev/ofw/openfirm.h>
30#include <dev/ofw/fdt.h>
31
32/* registers */
33#define	INTC_PENDING_BANK0	0x00
34#define	INTC_PENDING_BANK1	0x04
35#define	INTC_PENDING_BANK2	0x08
36#define	INTC_FIQ_CONTROL	0x0C
37#define	INTC_ENABLE_BANK1	0x10
38#define	INTC_ENABLE_BANK2	0x14
39#define	INTC_ENABLE_BANK0	0x18
40#define	INTC_DISABLE_BANK1	0x1C
41#define	INTC_DISABLE_BANK2	0x20
42#define	INTC_DISABLE_BANK0	0x24
43
44/* arm local */
45#define	ARM_LOCAL_CONTROL		0x00
46#define	ARM_LOCAL_PRESCALER		0x08
47#define	 PRESCALER_19_2			0x80000000 /* 19.2 MHz */
48#define	ARM_LOCAL_INT_TIMER(n)		(0x40 + (n) * 4)
49#define	ARM_LOCAL_INT_MAILBOX(n)	(0x50 + (n) * 4)
50#define	ARM_LOCAL_INT_PENDING(n)	(0x60 + (n) * 4)
51#define	 ARM_LOCAL_INT_PENDING_MASK	0x0f
52#define	ARM_LOCAL_INT_MAILBOX_SET(n)	(0x80 + (n) * 16)
53#define	ARM_LOCAL_INT_MAILBOX_CLR(n)	(0xc0 + (n) * 16)
54
55#define	BANK0_START	64
56#define	BANK0_END	(BANK0_START + 32 - 1)
57#define	BANK1_START	0
58#define	BANK1_END	(BANK1_START + 32 - 1)
59#define	BANK2_START	32
60#define	BANK2_END	(BANK2_START + 32 - 1)
61#define	LOCAL_START	96
62#define	LOCAL_END	(LOCAL_START + 32 - 1)
63
64#define	IS_IRQ_BANK0(n)	(((n) >= BANK0_START) && ((n) <= BANK0_END))
65#define	IS_IRQ_BANK1(n)	(((n) >= BANK1_START) && ((n) <= BANK1_END))
66#define	IS_IRQ_BANK2(n)	(((n) >= BANK2_START) && ((n) <= BANK2_END))
67#define	IS_IRQ_LOCAL(n)	(((n) >= LOCAL_START) && ((n) <= LOCAL_END))
68#define	IRQ_BANK0(n)	((n) - BANK0_START)
69#define	IRQ_BANK1(n)	((n) - BANK1_START)
70#define	IRQ_BANK2(n)	((n) - BANK2_START)
71#define	IRQ_LOCAL(n)	((n) - LOCAL_START)
72
73#define ARM_LOCAL_IRQ_MAILBOX(n) (4 + (n))
74
75#define	INTC_NIRQ	128
76#define	INTC_NBANK	4
77
78#define INTC_IRQ_TO_REG(i)	(((i) >> 5) & 0x3)
79#define INTC_IRQ_TO_REGi(i)	((i) & 0x1f)
80
81struct intrhand {
82	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
83	int (*ih_func)(void *);		/* handler */
84	void *ih_arg;			/* arg for handler */
85	int ih_ipl;			/* IPL_* */
86	int ih_flags;
87	int ih_irq;			/* IRQ number */
88	struct evcount ih_count;	/* interrupt counter */
89	char *ih_name;			/* device name */
90};
91
92struct intrsource {
93	TAILQ_HEAD(, intrhand) is_list;	/* handler list */
94	int is_irq;			/* IRQ to mask while handling */
95};
96
97struct bcm_intc_softc {
98	struct device		 sc_dev;
99	struct intrsource	 sc_handler[INTC_NIRQ];
100	uint32_t		 sc_imask[INTC_NBANK][NIPL];
101	int32_t			 sc_localcoremask[MAXCPUS];
102	bus_space_tag_t		 sc_iot;
103	bus_space_handle_t	 sc_ioh;
104	bus_space_handle_t	 sc_lioh;
105	struct interrupt_controller sc_intc;
106	struct interrupt_controller sc_l1_intc;
107};
108struct bcm_intc_softc *bcm_intc;
109
110int	 bcm_intc_match(struct device *, void *, void *);
111void	 bcm_intc_attach(struct device *, struct device *, void *);
112void	 bcm_intc_splx(int new);
113int	 bcm_intc_spllower(int new);
114int	 bcm_intc_splraise(int new);
115void	 bcm_intc_setipl(int new);
116void	 bcm_intc_calc_mask(void);
117void	*bcm_intc_intr_establish(int, int, struct cpu_info *,
118	    int (*)(void *), void *, char *);
119void	*bcm_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
120	    int (*)(void *), void *, char *);
121void	*l1_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
122	    int (*)(void *), void *, char *);
123void	 bcm_intc_intr_disestablish(void *);
124void	 bcm_intc_irq_handler(void *);
125void	 bcm_intc_intr_route(void *, int , struct cpu_info *);
126void	 bcm_intc_handle_ipi(void);
127void	 bcm_intc_send_ipi(struct cpu_info *, int);
128
129const struct cfattach	bcmintc_ca = {
130	sizeof (struct bcm_intc_softc), bcm_intc_match, bcm_intc_attach
131};
132
133struct cfdriver bcmintc_cd = {
134	NULL, "bcmintc", DV_DULL
135};
136
137int
138bcm_intc_match(struct device *parent, void *cfdata, void *aux)
139{
140	struct fdt_attach_args *faa = aux;
141
142	if (OF_is_compatible(faa->fa_node, "brcm,bcm2836-armctrl-ic"))
143		return 1;
144
145	return 0;
146}
147
148void
149bcm_intc_attach(struct device *parent, struct device *self, void *aux)
150{
151	struct bcm_intc_softc *sc = (struct bcm_intc_softc *)self;
152	struct fdt_attach_args *faa = aux;
153	uint32_t reg[2];
154	int node;
155	int i;
156
157	if (faa->fa_nreg < 1)
158		return;
159
160	bcm_intc = sc;
161
162	sc->sc_iot = faa->fa_iot;
163
164	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
165	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
166		panic("%s: bus_space_map failed!", __func__);
167
168	/*
169	 * ARM control logic.
170	 *
171	 * XXX Should really be implemented as a separate interrupt
172	 * controller, but for now it is easier to handle it together
173	 * with its BCM2835 partner.
174	 */
175	node = OF_finddevice("/soc/local_intc");
176	if (node == -1)
177		panic("%s: can't find ARM control logic", __func__);
178
179	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg))
180		panic("%s: can't map ARM control logic", __func__);
181
182	if (bus_space_map(sc->sc_iot, reg[0], reg[1], 0, &sc->sc_lioh))
183		panic("%s: bus_space_map failed!", __func__);
184
185	printf("\n");
186
187	/* mask all interrupts */
188	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0,
189	    0xffffffff);
190	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1,
191	    0xffffffff);
192	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2,
193	    0xffffffff);
194
195	/* ARM control specific */
196	bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_CONTROL, 0);
197	bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_PRESCALER,
198	    PRESCALER_19_2);
199	for (i = 0; i < 4; i++)
200		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
201		    ARM_LOCAL_INT_TIMER(i), 0);
202	for (i = 0; i < 4; i++)
203		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
204		    ARM_LOCAL_INT_MAILBOX(i), 0);
205
206	for (i = 0; i < INTC_NIRQ; i++) {
207		TAILQ_INIT(&sc->sc_handler[i].is_list);
208	}
209
210	bcm_intc_calc_mask();
211
212	/* insert self as interrupt handler */
213	arm_set_intr_handler(bcm_intc_splraise, bcm_intc_spllower,
214	    bcm_intc_splx, bcm_intc_setipl, bcm_intc_irq_handler, NULL,
215	    NULL, NULL);
216
217	sc->sc_intc.ic_node = faa->fa_node;
218	sc->sc_intc.ic_cookie = sc;
219	sc->sc_intc.ic_establish = bcm_intc_intr_establish_fdt;
220	sc->sc_intc.ic_disestablish = bcm_intc_intr_disestablish;
221	sc->sc_intc.ic_route = bcm_intc_intr_route;
222	arm_intr_register_fdt(&sc->sc_intc);
223
224	sc->sc_l1_intc.ic_node = node;
225	sc->sc_l1_intc.ic_cookie = sc;
226	sc->sc_l1_intc.ic_establish = l1_intc_intr_establish_fdt;
227	sc->sc_l1_intc.ic_disestablish = bcm_intc_intr_disestablish;
228	sc->sc_l1_intc.ic_route = bcm_intc_intr_route;
229	arm_intr_register_fdt(&sc->sc_l1_intc);
230
231	intr_send_ipi_func = bcm_intc_send_ipi;
232
233	bcm_intc_setipl(IPL_HIGH);  /* XXX ??? */
234	intr_enable();
235}
236
237void
238bcm_intc_intr_enable(int irq, int ipl)
239{
240	struct bcm_intc_softc	*sc = bcm_intc;
241
242	if (IS_IRQ_BANK0(irq))
243		sc->sc_imask[0][ipl] |= (1 << IRQ_BANK0(irq));
244	else if (IS_IRQ_BANK1(irq))
245		sc->sc_imask[1][ipl] |= (1 << IRQ_BANK1(irq));
246	else if (IS_IRQ_BANK2(irq))
247		sc->sc_imask[2][ipl] |= (1 << IRQ_BANK2(irq));
248	else if (IS_IRQ_LOCAL(irq))
249		sc->sc_imask[3][ipl] |= (1 << IRQ_LOCAL(irq));
250	else
251		printf("%s: invalid irq number: %d\n", __func__, irq);
252}
253
254void
255bcm_intc_intr_disable(int irq, int ipl)
256{
257	struct bcm_intc_softc	*sc = bcm_intc;
258
259	if (IS_IRQ_BANK0(irq))
260		sc->sc_imask[0][ipl] &= ~(1 << IRQ_BANK0(irq));
261	else if (IS_IRQ_BANK1(irq))
262		sc->sc_imask[1][ipl] &= ~(1 << IRQ_BANK1(irq));
263	else if (IS_IRQ_BANK2(irq))
264		sc->sc_imask[2][ipl] &= ~(1 << IRQ_BANK2(irq));
265	else if (IS_IRQ_LOCAL(irq))
266		sc->sc_imask[3][ipl] &= ~(1 << IRQ_LOCAL(irq));
267	else
268		printf("%s: invalid irq number: %d\n", __func__, irq);
269}
270
271void
272bcm_intc_calc_mask(void)
273{
274	struct cpu_info *ci = curcpu();
275	struct bcm_intc_softc *sc = bcm_intc;
276	int irq;
277	struct intrhand *ih;
278	int i;
279
280	for (irq = 0; irq < INTC_NIRQ; irq++) {
281		int max = IPL_NONE;
282		int min = IPL_HIGH;
283		TAILQ_FOREACH(ih, &sc->sc_handler[irq].is_list, ih_list) {
284			if (ih->ih_ipl > max)
285				max = ih->ih_ipl;
286
287			if (ih->ih_ipl < min)
288				min = ih->ih_ipl;
289		}
290
291		sc->sc_handler[irq].is_irq = max;
292
293		if (max == IPL_NONE)
294			min = IPL_NONE;
295
296#ifdef DEBUG_INTC
297		if (min != IPL_NONE) {
298			printf("irq %d to block at %d %d reg %d bit %d\n",
299			    irq, max, min, INTC_IRQ_TO_REG(irq),
300			    INTC_IRQ_TO_REGi(irq));
301		}
302#endif
303		/* Enable interrupts at lower levels, clear -> enable */
304		for (i = 0; i < min; i++)
305			bcm_intc_intr_enable(irq, i);
306		for (; i <= IPL_HIGH; i++)
307			bcm_intc_intr_disable(irq, i);
308	}
309	arm_init_smask();
310	bcm_intc_setipl(ci->ci_cpl);
311}
312
313void
314bcm_intc_splx(int new)
315{
316	struct cpu_info *ci = curcpu();
317
318	if (ci->ci_ipending & arm_smask[new])
319		arm_do_pending_intr(new);
320
321	bcm_intc_setipl(new);
322}
323
324int
325bcm_intc_spllower(int new)
326{
327	struct cpu_info *ci = curcpu();
328	int old = ci->ci_cpl;
329	bcm_intc_splx(new);
330	return (old);
331}
332
333int
334bcm_intc_splraise(int new)
335{
336	struct cpu_info *ci = curcpu();
337	int old;
338	old = ci->ci_cpl;
339
340	/*
341	 * setipl must always be called because there is a race window
342	 * where the variable is updated before the mask is set
343	 * an interrupt occurs in that window without the mask always
344	 * being set, the hardware might not get updated on the next
345	 * splraise completely messing up spl protection.
346	 */
347	if (old > new)
348		new = old;
349
350	bcm_intc_setipl(new);
351
352	return (old);
353}
354
355void
356bcm_intc_setipl(int new)
357{
358	struct cpu_info *ci = curcpu();
359	struct bcm_intc_softc *sc = bcm_intc;
360	u_long psw;
361
362	psw = intr_disable();
363	ci->ci_cpl = new;
364	if (cpu_number() == 0) {
365		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0,
366		    0xffffffff);
367		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1,
368		    0xffffffff);
369		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2,
370		    0xffffffff);
371		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK0,
372		    sc->sc_imask[0][new]);
373		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK1,
374		    sc->sc_imask[1][new]);
375		bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK2,
376		    sc->sc_imask[2][new]);
377	}
378	/* timer for current core */
379	bus_space_write_4(sc->sc_iot, sc->sc_lioh,
380	    ARM_LOCAL_INT_TIMER(cpu_number()),
381	    sc->sc_imask[3][ci->ci_cpl] &
382	    sc->sc_localcoremask[cpu_number()]);
383	intr_restore(psw);
384}
385
386int
387bcm_intc_get_next_irq(int last_irq)
388{
389	struct bcm_intc_softc *sc = bcm_intc;
390	uint32_t pending;
391	int32_t irq = last_irq + 1;
392
393	/* Sanity check */
394	if (irq < 0)
395		irq = 0;
396
397	/* We need to keep this order. */
398	/* TODO: should we mask last_irq? */
399	if (IS_IRQ_BANK1(irq)) {
400		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
401		    INTC_PENDING_BANK1);
402		if (pending == 0) {
403			irq = BANK2_START;	/* skip to next bank */
404		} else do {
405			if (pending & (1 << IRQ_BANK1(irq)))
406				return irq;
407			irq++;
408		} while (IS_IRQ_BANK1(irq));
409	}
410	if (IS_IRQ_BANK2(irq)) {
411		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
412		    INTC_PENDING_BANK2);
413		if (pending == 0) {
414			irq = BANK0_START;	/* skip to next bank */
415		} else do {
416			if (pending & (1 << IRQ_BANK2(irq)))
417				return irq;
418			irq++;
419		} while (IS_IRQ_BANK2(irq));
420	}
421	if (IS_IRQ_BANK0(irq)) {
422		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
423		    INTC_PENDING_BANK0);
424		if (pending == 0) {
425			irq = LOCAL_START;	/* skip to next bank */
426		} else do {
427			if (pending & (1 << IRQ_BANK0(irq)))
428				return irq;
429			irq++;
430		} while (IS_IRQ_BANK0(irq));
431	}
432	if (IS_IRQ_LOCAL(irq)) {
433		pending = bus_space_read_4(sc->sc_iot, sc->sc_lioh,
434		    ARM_LOCAL_INT_PENDING(cpu_number()));
435		pending &= ARM_LOCAL_INT_PENDING_MASK;
436		if (pending != 0) do {
437			if (pending & (1 << IRQ_LOCAL(irq)))
438				return irq;
439			irq++;
440		} while (IS_IRQ_LOCAL(irq));
441	}
442	return (-1);
443}
444
445void
446bcm_intc_run_handler(struct intrhand *ih, void *frame, int s)
447{
448	int handled;
449	void *arg;
450
451#ifdef MULTIPROCESSOR
452	int need_lock;
453
454	if (ih->ih_flags & IPL_MPSAFE)
455		need_lock = 0;
456	else
457		need_lock = s < IPL_SCHED;
458
459	if (need_lock)
460		KERNEL_LOCK();
461#endif
462
463	if (ih->ih_arg)
464		arg = ih->ih_arg;
465	else
466		arg = frame;
467
468	handled = ih->ih_func(arg);
469	if (handled)
470		ih->ih_count.ec_count++;
471
472#ifdef MULTIPROCESSOR
473	if (need_lock)
474		KERNEL_UNLOCK();
475#endif
476}
477
478void
479bcm_intc_irq_handler(void *frame)
480{
481	struct bcm_intc_softc *sc = bcm_intc;
482	struct intrhand *ih;
483	int irq, pri, s;
484
485	irq = (cpu_number() == 0 ? 0 : LOCAL_START) - 1;
486	while ((irq = bcm_intc_get_next_irq(irq)) != -1) {
487#ifdef MULTIPROCESSOR
488		if (irq == ARM_LOCAL_IRQ_MAILBOX(cpu_number())) {
489			bcm_intc_handle_ipi();
490			continue;
491		}
492#endif
493
494		pri = sc->sc_handler[irq].is_irq;
495		s = bcm_intc_splraise(pri);
496		TAILQ_FOREACH(ih, &sc->sc_handler[irq].is_list, ih_list) {
497			intr_enable();
498			bcm_intc_run_handler(ih, frame, s);
499			intr_disable();
500		}
501		bcm_intc_splx(s);
502	}
503}
504
505void *
506bcm_intc_intr_establish_fdt(void *cookie, int *cell, int level,
507    struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
508{
509	struct bcm_intc_softc	*sc = (struct bcm_intc_softc *)cookie;
510	int irq;
511
512	irq = cell[1];
513	if (cell[0] == 0)
514		irq += BANK0_START;
515	else if (cell[0] == 1)
516		irq += BANK1_START;
517	else if (cell[0] == 2)
518		irq += BANK2_START;
519	else if (cell[0] == 3)
520		irq += LOCAL_START;
521	else
522		panic("%s: bogus interrupt type", sc->sc_dev.dv_xname);
523
524	return bcm_intc_intr_establish(irq, level, ci, func, arg, name);
525}
526
527void *
528l1_intc_intr_establish_fdt(void *cookie, int *cell, int level,
529    struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
530{
531	int irq;
532
533	irq = cell[0] + LOCAL_START;
534	return bcm_intc_intr_establish(irq, level, ci, func, arg, name);
535}
536
537void *
538bcm_intc_intr_establish(int irqno, int level, struct cpu_info *ci,
539    int (*func)(void *), void *arg, char *name)
540{
541	struct bcm_intc_softc *sc = bcm_intc;
542	struct intrhand *ih;
543	u_long psw;
544
545	if (irqno < 0 || irqno >= INTC_NIRQ)
546		panic("bcm_intc_intr_establish: bogus irqnumber %d: %s",
547		     irqno, name);
548
549	if (ci != NULL && !CPU_IS_PRIMARY(ci))
550		return NULL;
551
552	psw = intr_disable();
553
554	ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK);
555	ih->ih_func = func;
556	ih->ih_arg = arg;
557	ih->ih_ipl = level & IPL_IRQMASK;
558	ih->ih_flags = level & IPL_FLAGMASK;
559	ih->ih_irq = irqno;
560	ih->ih_name = name;
561
562	if (IS_IRQ_LOCAL(irqno))
563		sc->sc_localcoremask[0] |= (1 << IRQ_LOCAL(irqno));
564
565	TAILQ_INSERT_TAIL(&sc->sc_handler[irqno].is_list, ih, ih_list);
566
567	if (name != NULL)
568		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
569
570#ifdef DEBUG_INTC
571	printf("%s irq %d level %d [%s]\n", __func__, irqno, level,
572	    name);
573#endif
574	bcm_intc_calc_mask();
575
576	intr_restore(psw);
577	return (ih);
578}
579
580void
581bcm_intc_intr_disestablish(void *cookie)
582{
583	struct bcm_intc_softc *sc = bcm_intc;
584	struct intrhand *ih = cookie;
585	int irqno = ih->ih_irq;
586	u_long psw;
587
588	psw = intr_disable();
589	TAILQ_REMOVE(&sc->sc_handler[irqno].is_list, ih, ih_list);
590	if (ih->ih_name != NULL)
591		evcount_detach(&ih->ih_count);
592	intr_restore(psw);
593
594	free(ih, M_DEVBUF, 0);
595}
596
597void
598bcm_intc_intr_route(void *cookie, int enable, struct cpu_info *ci)
599{
600	struct bcm_intc_softc *sc = bcm_intc;
601	struct intrhand *ih = cookie;
602	int lirq = IRQ_LOCAL(ih->ih_irq);
603
604	if (enable)
605		sc->sc_localcoremask[ci->ci_cpuid] |= (1 << lirq);
606	else
607		sc->sc_localcoremask[ci->ci_cpuid] &= ~(1 << lirq);
608
609	if (ci == curcpu()) {
610		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
611		    ARM_LOCAL_INT_TIMER(cpu_number()),
612		    sc->sc_imask[3][ci->ci_cpl] &
613		    sc->sc_localcoremask[cpu_number()]);
614#ifdef MULTIPROCESSOR
615		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
616		    ARM_LOCAL_INT_MAILBOX(cpu_number()),
617		    sc->sc_imask[3][ci->ci_cpl] &
618		    sc->sc_localcoremask[cpu_number()]);
619#endif
620	}
621}
622
623void
624bcm_intc_handle_ipi(void)
625{
626	struct bcm_intc_softc *sc = bcm_intc;
627	int cpuno = cpu_number();
628	uint32_t mbox_val;
629	int ipi;
630
631	mbox_val = bus_space_read_4(sc->sc_iot, sc->sc_lioh,
632		ARM_LOCAL_INT_MAILBOX_CLR(cpuno));
633	ipi = ffs(mbox_val) - 1;
634	bus_space_write_4(sc->sc_iot, sc->sc_lioh,
635	    ARM_LOCAL_INT_MAILBOX_CLR(cpuno), 1 << ipi);
636	switch (ipi) {
637	case ARM_IPI_DDB:
638		/* XXX */
639#ifdef DDB
640		db_enter();
641#endif
642		break;
643	case ARM_IPI_NOP:
644		break;
645	}
646}
647
648void
649bcm_intc_send_ipi(struct cpu_info *ci, int id)
650{
651	struct bcm_intc_softc *sc = bcm_intc;
652
653	__asm volatile("dsb sy"); /* XXX */
654
655	bus_space_write_4(sc->sc_iot, sc->sc_lioh,
656	    ARM_LOCAL_INT_MAILBOX_SET(ci->ci_cpuid), 1 << id);
657}
658