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