1 /* $OpenBSD: bcm2836_intr.c,v 1.8 2022/01/03 03:06:49 jsg 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
bcm_intc_match(struct device * parent,void * cfdata,void * aux)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
bcm_intc_attach(struct device * parent,struct device * self,void * aux)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
bcm_intc_intr_enable(int irq,int ipl)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
bcm_intc_intr_disable(int irq,int ipl)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
bcm_intc_calc_mask(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
bcm_intc_splx(int new)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
bcm_intc_spllower(int new)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
bcm_intc_splraise(int new)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
bcm_intc_setipl(int new)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
bcm_intc_get_next_irq(int last_irq)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
bcm_intc_call_handler(int irq,void * frame)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)
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
bcm_intc_irq_handler(void * frame)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 *
bcm_intc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)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 *
l1_intc_intr_establish_fdt(void * cookie,int * cell,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)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 *
bcm_intc_intr_establish(int irqno,int level,struct cpu_info * ci,int (* func)(void *),void * arg,char * name)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
bcm_intc_intr_disestablish(void * cookie)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 *
bcm_intc_intr_string(void * cookie)569 bcm_intc_intr_string(void *cookie)
570 {
571 return "huh?";
572 }
573