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