xref: /openbsd/sys/arch/armv7/broadcom/bcm2836_intr.c (revision 73471bf0)
1 /* $OpenBSD: bcm2836_intr.c,v 1.7 2021/10/24 17:52:27 mpi 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 <arm/cpufunc.h>
30 
31 #include <dev/ofw/openfirm.h>
32 #include <dev/ofw/fdt.h>
33 
34 /* registers */
35 #define	INTC_PENDING_BANK0	0x00
36 #define	INTC_PENDING_BANK1	0x04
37 #define	INTC_PENDING_BANK2	0x08
38 #define	INTC_FIQ_CONTROL	0x0C
39 #define	INTC_ENABLE_BANK1	0x10
40 #define	INTC_ENABLE_BANK2	0x14
41 #define	INTC_ENABLE_BANK0	0x18
42 #define	INTC_DISABLE_BANK1	0x1C
43 #define	INTC_DISABLE_BANK2	0x20
44 #define	INTC_DISABLE_BANK0	0x24
45 
46 /* arm local */
47 #define	ARM_LOCAL_CONTROL		0x00
48 #define	ARM_LOCAL_PRESCALER		0x08
49 #define	 PRESCALER_19_2			0x80000000 /* 19.2 MHz */
50 #define	ARM_LOCAL_INT_TIMER(n)		(0x40 + (n) * 4)
51 #define	ARM_LOCAL_INT_MAILBOX(n)	(0x50 + (n) * 4)
52 #define	ARM_LOCAL_INT_PENDING(n)	(0x60 + (n) * 4)
53 #define	 ARM_LOCAL_INT_PENDING_MASK	0x0f
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	INTC_NIRQ	128
74 #define	INTC_NBANK	4
75 
76 #define INTC_IRQ_TO_REG(i)	(((i) >> 5) & 0x3)
77 #define INTC_IRQ_TO_REGi(i)	((i) & 0x1f)
78 
79 struct intrhand {
80 	TAILQ_ENTRY(intrhand) ih_list;	/* link on intrq list */
81 	int (*ih_fun)(void *);		/* handler */
82 	void *ih_arg;			/* arg for handler */
83 	int ih_ipl;			/* IPL_* */
84 	int ih_irq;			/* IRQ number */
85 	struct evcount ih_count;	/* interrupt counter */
86 	char *ih_name;			/* device name */
87 };
88 
89 struct intrsource {
90 	TAILQ_HEAD(, intrhand) is_list;	/* handler list */
91 	int is_irq;			/* IRQ to mask while handling */
92 };
93 
94 struct bcm_intc_softc {
95 	struct device		 sc_dev;
96 	struct intrsource	 sc_bcm_intc_handler[INTC_NIRQ];
97 	uint32_t		 sc_bcm_intc_imask[INTC_NBANK][NIPL];
98 	bus_space_tag_t		 sc_iot;
99 	bus_space_handle_t	 sc_ioh;
100 	bus_space_handle_t	 sc_lioh;
101 	struct interrupt_controller sc_intc;
102 	struct interrupt_controller sc_l1_intc;
103 };
104 struct bcm_intc_softc *bcm_intc;
105 
106 int	 bcm_intc_match(struct device *, void *, void *);
107 void	 bcm_intc_attach(struct device *, struct device *, void *);
108 void	 bcm_intc_splx(int new);
109 int	 bcm_intc_spllower(int new);
110 int	 bcm_intc_splraise(int new);
111 void	 bcm_intc_setipl(int new);
112 void	 bcm_intc_calc_mask(void);
113 void	*bcm_intc_intr_establish(int, int, struct cpu_info *,
114     int (*)(void *), void *, char *);
115 void	*bcm_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
116     int (*)(void *), void *, char *);
117 void	*l1_intc_intr_establish_fdt(void *, int *, int, struct cpu_info *,
118     int (*)(void *), void *, char *);
119 void	 bcm_intc_intr_disestablish(void *);
120 const char *bcm_intc_intr_string(void *);
121 void	 bcm_intc_irq_handler(void *);
122 
123 const struct cfattach	bcmintc_ca = {
124 	sizeof (struct bcm_intc_softc), bcm_intc_match, bcm_intc_attach
125 };
126 
127 struct cfdriver bcmintc_cd = {
128 	NULL, "bcmintc", DV_DULL
129 };
130 
131 int
132 bcm_intc_match(struct device *parent, void *cfdata, void *aux)
133 {
134 	struct fdt_attach_args *faa = aux;
135 
136 	if (OF_is_compatible(faa->fa_node, "brcm,bcm2836-armctrl-ic"))
137 		return 1;
138 
139 	return 0;
140 }
141 
142 void
143 bcm_intc_attach(struct device *parent, struct device *self, void *aux)
144 {
145 	struct bcm_intc_softc *sc = (struct bcm_intc_softc *)self;
146 	struct fdt_attach_args *faa = aux;
147 	uint32_t reg[2];
148 	int node;
149 	int i;
150 
151 	if (faa->fa_nreg < 1)
152 		return;
153 
154 	bcm_intc = sc;
155 
156 	sc->sc_iot = faa->fa_iot;
157 
158 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
159 	    faa->fa_reg[0].size, 0, &sc->sc_ioh))
160 		panic("%s: bus_space_map failed!", __func__);
161 
162 	/*
163 	 * ARM control logic.
164 	 *
165 	 * XXX Should really be implemented as a separate interrupt
166 	 * controller, but for now it is easier to handle it together
167 	 * with its BCM2835 partner.
168 	 */
169 	node = OF_finddevice("/soc/local_intc");
170 	if (node == -1)
171 		panic("%s: can't find ARM control logic", __func__);
172 
173 	if (OF_getpropintarray(node, "reg", reg, sizeof(reg)) != sizeof(reg))
174 		panic("%s: can't map ARM control logic", __func__);
175 
176 	if (bus_space_map(sc->sc_iot, reg[0], reg[1], 0, &sc->sc_lioh))
177 		panic("%s: bus_space_map failed!", __func__);
178 
179 	printf("\n");
180 
181 	/* mask all interrupts */
182 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0,
183 	    0xffffffff);
184 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1,
185 	    0xffffffff);
186 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2,
187 	    0xffffffff);
188 
189 	/* ARM control specific */
190 	bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_CONTROL, 0);
191 	bus_space_write_4(sc->sc_iot, sc->sc_lioh, ARM_LOCAL_PRESCALER,
192 	    PRESCALER_19_2);
193 	for (i = 0; i < 4; i++)
194 		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
195 		    ARM_LOCAL_INT_TIMER(i), 0);
196 	for (i = 0; i < 4; i++)
197 		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
198 		    ARM_LOCAL_INT_MAILBOX(i), 1);
199 
200 	for (i = 0; i < INTC_NIRQ; i++) {
201 		TAILQ_INIT(&sc->sc_bcm_intc_handler[i].is_list);
202 	}
203 
204 	bcm_intc_calc_mask();
205 
206 	/* insert self as interrupt handler */
207 	arm_set_intr_handler(bcm_intc_splraise, bcm_intc_spllower, bcm_intc_splx,
208 	    bcm_intc_setipl,
209 	    bcm_intc_intr_establish, bcm_intc_intr_disestablish, bcm_intc_intr_string,
210 	    bcm_intc_irq_handler);
211 
212 	sc->sc_intc.ic_node = faa->fa_node;
213 	sc->sc_intc.ic_cookie = sc;
214 	sc->sc_intc.ic_establish = bcm_intc_intr_establish_fdt;
215 	sc->sc_intc.ic_disestablish = bcm_intc_intr_disestablish;
216 	arm_intr_register_fdt(&sc->sc_intc);
217 
218 	sc->sc_l1_intc.ic_node = node;
219 	sc->sc_l1_intc.ic_cookie = sc;
220 	sc->sc_l1_intc.ic_establish = l1_intc_intr_establish_fdt;
221 	sc->sc_l1_intc.ic_disestablish = bcm_intc_intr_disestablish;
222 	arm_intr_register_fdt(&sc->sc_l1_intc);
223 
224 	bcm_intc_setipl(IPL_HIGH);  /* XXX ??? */
225 	enable_interrupts(PSR_I);
226 }
227 
228 void
229 bcm_intc_intr_enable(int irq, int ipl)
230 {
231 	struct bcm_intc_softc	*sc = bcm_intc;
232 
233 	if (IS_IRQ_BANK0(irq))
234 		sc->sc_bcm_intc_imask[0][ipl] |= (1 << IRQ_BANK0(irq));
235 	else if (IS_IRQ_BANK1(irq))
236 		sc->sc_bcm_intc_imask[1][ipl] |= (1 << IRQ_BANK1(irq));
237 	else if (IS_IRQ_BANK2(irq))
238 		sc->sc_bcm_intc_imask[2][ipl] |= (1 << IRQ_BANK2(irq));
239 	else if (IS_IRQ_LOCAL(irq))
240 		sc->sc_bcm_intc_imask[3][ipl] |= (1 << IRQ_LOCAL(irq));
241 	else
242 		printf("%s: invalid irq number: %d\n", __func__, irq);
243 }
244 
245 void
246 bcm_intc_intr_disable(int irq, int ipl)
247 {
248 	struct bcm_intc_softc	*sc = bcm_intc;
249 
250 	if (IS_IRQ_BANK0(irq))
251 		sc->sc_bcm_intc_imask[0][ipl] &= ~(1 << IRQ_BANK0(irq));
252 	else if (IS_IRQ_BANK1(irq))
253 		sc->sc_bcm_intc_imask[1][ipl] &= ~(1 << IRQ_BANK1(irq));
254 	else if (IS_IRQ_BANK2(irq))
255 		sc->sc_bcm_intc_imask[2][ipl] &= ~(1 << IRQ_BANK2(irq));
256 	else if (IS_IRQ_LOCAL(irq))
257 		sc->sc_bcm_intc_imask[3][ipl] &= ~(1 << IRQ_LOCAL(irq));
258 	else
259 		printf("%s: invalid irq number: %d\n", __func__, irq);
260 }
261 
262 void
263 bcm_intc_calc_mask(void)
264 {
265 	struct cpu_info *ci = curcpu();
266 	struct bcm_intc_softc *sc = bcm_intc;
267 	int irq;
268 	struct intrhand *ih;
269 	int i;
270 
271 	for (irq = 0; irq < INTC_NIRQ; irq++) {
272 		int max = IPL_NONE;
273 		int min = IPL_HIGH;
274 		TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list,
275 		    ih_list) {
276 			if (ih->ih_ipl > max)
277 				max = ih->ih_ipl;
278 
279 			if (ih->ih_ipl < min)
280 				min = ih->ih_ipl;
281 		}
282 
283 		sc->sc_bcm_intc_handler[irq].is_irq = max;
284 
285 		if (max == IPL_NONE)
286 			min = IPL_NONE;
287 
288 #ifdef DEBUG_INTC
289 		if (min != IPL_NONE) {
290 			printf("irq %d to block at %d %d reg %d bit %d\n",
291 			    irq, max, min, INTC_IRQ_TO_REG(irq),
292 			    INTC_IRQ_TO_REGi(irq));
293 		}
294 #endif
295 		/* Enable interrupts at lower levels, clear -> enable */
296 		for (i = 0; i < min; i++)
297 			bcm_intc_intr_enable(irq, i);
298 		for (; i <= IPL_HIGH; i++)
299 			bcm_intc_intr_disable(irq, i);
300 	}
301 	arm_init_smask();
302 	bcm_intc_setipl(ci->ci_cpl);
303 }
304 
305 void
306 bcm_intc_splx(int new)
307 {
308 	struct cpu_info *ci = curcpu();
309 
310 	if (ci->ci_ipending & arm_smask[new])
311 		arm_do_pending_intr(new);
312 
313 	bcm_intc_setipl(new);
314 }
315 
316 int
317 bcm_intc_spllower(int new)
318 {
319 	struct cpu_info *ci = curcpu();
320 	int old = ci->ci_cpl;
321 	bcm_intc_splx(new);
322 	return (old);
323 }
324 
325 int
326 bcm_intc_splraise(int new)
327 {
328 	struct cpu_info *ci = curcpu();
329 	int old;
330 	old = ci->ci_cpl;
331 
332 	/*
333 	 * setipl must always be called because there is a race window
334 	 * where the variable is updated before the mask is set
335 	 * an interrupt occurs in that window without the mask always
336 	 * being set, the hardware might not get updated on the next
337 	 * splraise completely messing up spl protection.
338 	 */
339 	if (old > new)
340 		new = old;
341 
342 	bcm_intc_setipl(new);
343 
344 	return (old);
345 }
346 
347 void
348 bcm_intc_setipl(int new)
349 {
350 	struct cpu_info *ci = curcpu();
351 	struct bcm_intc_softc *sc = bcm_intc;
352 	int i, psw;
353 
354 	psw = disable_interrupts(PSR_I);
355 	ci->ci_cpl = new;
356 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK0,
357 	    0xffffffff);
358 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK1,
359 	    0xffffffff);
360 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_DISABLE_BANK2,
361 	    0xffffffff);
362 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK0,
363 	    sc->sc_bcm_intc_imask[0][new]);
364 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK1,
365 	    sc->sc_bcm_intc_imask[1][new]);
366 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, INTC_ENABLE_BANK2,
367 	    sc->sc_bcm_intc_imask[2][new]);
368 	/* XXX: SMP */
369 	for (i = 0; i < 4; i++)
370 		bus_space_write_4(sc->sc_iot, sc->sc_lioh,
371 		    ARM_LOCAL_INT_TIMER(i), sc->sc_bcm_intc_imask[3][new]);
372 	restore_interrupts(psw);
373 }
374 
375 int
376 bcm_intc_get_next_irq(int last_irq)
377 {
378 	struct bcm_intc_softc *sc = bcm_intc;
379 	uint32_t pending;
380 	int32_t irq = last_irq + 1;
381 
382 	/* Sanity check */
383 	if (irq < 0)
384 		irq = 0;
385 
386 	/* We need to keep this order. */
387 	/* TODO: should we mask last_irq? */
388 	if (IS_IRQ_BANK1(irq)) {
389 		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
390 		    INTC_PENDING_BANK1);
391 		if (pending == 0) {
392 			irq = BANK2_START;	/* skip to next bank */
393 		} else do {
394 			if (pending & (1 << IRQ_BANK1(irq)))
395 				return irq;
396 			irq++;
397 		} while (IS_IRQ_BANK1(irq));
398 	}
399 	if (IS_IRQ_BANK2(irq)) {
400 		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
401 		    INTC_PENDING_BANK2);
402 		if (pending == 0) {
403 			irq = BANK0_START;	/* skip to next bank */
404 		} else do {
405 			if (pending & (1 << IRQ_BANK2(irq)))
406 				return irq;
407 			irq++;
408 		} while (IS_IRQ_BANK2(irq));
409 	}
410 	if (IS_IRQ_BANK0(irq)) {
411 		pending = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
412 		    INTC_PENDING_BANK0);
413 		if (pending == 0) {
414 			irq = LOCAL_START;	/* skip to next bank */
415 		} else do {
416 			if (pending & (1 << IRQ_BANK0(irq)))
417 				return irq;
418 			irq++;
419 		} while (IS_IRQ_BANK0(irq));
420 	}
421 	if (IS_IRQ_LOCAL(irq)) {
422 		/* XXX: SMP */
423 		pending = bus_space_read_4(sc->sc_iot, sc->sc_lioh,
424 		    ARM_LOCAL_INT_PENDING(0));
425 		pending &= ARM_LOCAL_INT_PENDING_MASK;
426 		if (pending != 0) do {
427 			if (pending & (1 << IRQ_LOCAL(irq)))
428 				return irq;
429 			irq++;
430 		} while (IS_IRQ_LOCAL(irq));
431 	}
432 	return (-1);
433 }
434 
435 static void
436 bcm_intc_call_handler(int irq, void *frame)
437 {
438 	struct bcm_intc_softc *sc = bcm_intc;
439 	struct intrhand *ih;
440 	int pri, s;
441 	void *arg;
442 
443 #ifdef DEBUG_INTC
444 	if (irq != 99)
445 		printf("irq  %d fired\n", irq);
446 	else {
447 		static int cnt = 0;
448 		if ((cnt++ % 100) == 0) {
449 			printf("irq  %d fired * _100\n", irq);
450 			db_enter();
451 		}
452 	}
453 #endif
454 
455 	pri = sc->sc_bcm_intc_handler[irq].is_irq;
456 	s = bcm_intc_splraise(pri);
457 	TAILQ_FOREACH(ih, &sc->sc_bcm_intc_handler[irq].is_list, ih_list) {
458 		if (ih->ih_arg != 0)
459 			arg = ih->ih_arg;
460 		else
461 			arg = frame;
462 
463 		if (ih->ih_fun(arg))
464 			ih->ih_count.ec_count++;
465 
466 	}
467 
468 	bcm_intc_splx(s);
469 }
470 
471 void
472 bcm_intc_irq_handler(void *frame)
473 {
474 	int irq = -1;
475 
476 	while ((irq = bcm_intc_get_next_irq(irq)) != -1)
477 		bcm_intc_call_handler(irq, frame);
478 }
479 
480 void *
481 bcm_intc_intr_establish_fdt(void *cookie, int *cell, int level,
482     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
483 {
484 	struct bcm_intc_softc	*sc = (struct bcm_intc_softc *)cookie;
485 	int irq;
486 
487 	irq = cell[1];
488 	if (cell[0] == 0)
489 		irq += BANK0_START;
490 	else if (cell[0] == 1)
491 		irq += BANK1_START;
492 	else if (cell[0] == 2)
493 		irq += BANK2_START;
494 	else if (cell[0] == 3)
495 		irq += LOCAL_START;
496 	else
497 		panic("%s: bogus interrupt type", sc->sc_dev.dv_xname);
498 
499 	return bcm_intc_intr_establish(irq, level, ci, func, arg, name);
500 }
501 
502 void *
503 l1_intc_intr_establish_fdt(void *cookie, int *cell, int level,
504     struct cpu_info *ci, int (*func)(void *), void *arg, char *name)
505 {
506 	int irq;
507 
508 	irq = cell[0] + LOCAL_START;
509 	return bcm_intc_intr_establish(irq, level, ci, func, arg, name);
510 }
511 
512 void *
513 bcm_intc_intr_establish(int irqno, int level, struct cpu_info *ci,
514     int (*func)(void *), void *arg, char *name)
515 {
516 	struct bcm_intc_softc *sc = bcm_intc;
517 	struct intrhand *ih;
518 	int psw;
519 
520 	if (irqno < 0 || irqno >= INTC_NIRQ)
521 		panic("bcm_intc_intr_establish: bogus irqnumber %d: %s",
522 		     irqno, name);
523 
524 	if (ci == NULL)
525 		ci = &cpu_info_primary;
526 	else if (!CPU_IS_PRIMARY(ci))
527 		return NULL;
528 
529 	psw = disable_interrupts(PSR_I);
530 
531 	ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK);
532 	ih->ih_fun = func;
533 	ih->ih_arg = arg;
534 	ih->ih_ipl = level & IPL_IRQMASK;
535 	ih->ih_irq = irqno;
536 	ih->ih_name = name;
537 
538 	TAILQ_INSERT_TAIL(&sc->sc_bcm_intc_handler[irqno].is_list, ih, ih_list);
539 
540 	if (name != NULL)
541 		evcount_attach(&ih->ih_count, name, &ih->ih_irq);
542 
543 #ifdef DEBUG_INTC
544 	printf("%s irq %d level %d [%s]\n", __func__, irqno, level,
545 	    name);
546 #endif
547 	bcm_intc_calc_mask();
548 
549 	restore_interrupts(psw);
550 	return (ih);
551 }
552 
553 void
554 bcm_intc_intr_disestablish(void *cookie)
555 {
556 	struct bcm_intc_softc *sc = bcm_intc;
557 	struct intrhand *ih = cookie;
558 	int irqno = ih->ih_irq;
559 	int psw;
560 	psw = disable_interrupts(PSR_I);
561 	TAILQ_REMOVE(&sc->sc_bcm_intc_handler[irqno].is_list, ih, ih_list);
562 	if (ih->ih_name != NULL)
563 		evcount_detach(&ih->ih_count);
564 	free(ih, M_DEVBUF, 0);
565 	restore_interrupts(psw);
566 }
567 
568 const char *
569 bcm_intc_intr_string(void *cookie)
570 {
571 	return "huh?";
572 }
573