bcm2835_intr.c revision 1.2.2.2 1 /* $NetBSD: bcm2835_intr.c,v 1.2.2.2 2017/12/03 11:35:52 jdolecek Exp $ */
2
3 /*-
4 * Copyright (c) 2012, 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Nick Hudson
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.2.2.2 2017/12/03 11:35:52 jdolecek Exp $");
34
35 #define _INTR_PRIVATE
36
37 #include "opt_bcm283x.h"
38
39 #include <sys/param.h>
40 #include <sys/bus.h>
41 #include <sys/cpu.h>
42 #include <sys/device.h>
43 #include <sys/proc.h>
44
45 #include <machine/intr.h>
46
47 #include <arm/locore.h>
48
49 #include <arm/pic/picvar.h>
50 #include <arm/cortex/gtmr_var.h>
51
52 #include <arm/broadcom/bcm_amba.h>
53 #include <arm/broadcom/bcm2835reg.h>
54 #include <arm/broadcom/bcm2835var.h>
55
56 static void bcm2835_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
57 static void bcm2835_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
58 static int bcm2835_pic_find_pending_irqs(struct pic_softc *);
59 static void bcm2835_pic_establish_irq(struct pic_softc *, struct intrsource *);
60 static void bcm2835_pic_source_name(struct pic_softc *, int, char *,
61 size_t);
62
63 #if defined(BCM2836)
64 static void bcm2836mp_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
65 static void bcm2836mp_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
66 static int bcm2836mp_pic_find_pending_irqs(struct pic_softc *);
67 static void bcm2836mp_pic_establish_irq(struct pic_softc *, struct intrsource *);
68 static void bcm2836mp_pic_source_name(struct pic_softc *, int, char *,
69 size_t);
70 #ifdef MULTIPROCESSOR
71 int bcm2836mp_ipi_handler(void *);
72 static void bcm2836mp_cpu_init(struct pic_softc *, struct cpu_info *);
73 static void bcm2836mp_send_ipi(struct pic_softc *, const kcpuset_t *, u_long);
74 #endif
75 #endif
76
77 static int bcm2835_icu_match(device_t, cfdata_t, void *);
78 static void bcm2835_icu_attach(device_t, device_t, void *);
79
80 static struct pic_ops bcm2835_picops = {
81 .pic_unblock_irqs = bcm2835_pic_unblock_irqs,
82 .pic_block_irqs = bcm2835_pic_block_irqs,
83 .pic_find_pending_irqs = bcm2835_pic_find_pending_irqs,
84 .pic_establish_irq = bcm2835_pic_establish_irq,
85 .pic_source_name = bcm2835_pic_source_name,
86 };
87
88 struct pic_softc bcm2835_pic = {
89 .pic_ops = &bcm2835_picops,
90 .pic_maxsources = BCM2835_NIRQ,
91 .pic_name = "bcm2835 pic",
92 };
93
94 #if defined(BCM2836)
95 static struct pic_ops bcm2836mp_picops = {
96 .pic_unblock_irqs = bcm2836mp_pic_unblock_irqs,
97 .pic_block_irqs = bcm2836mp_pic_block_irqs,
98 .pic_find_pending_irqs = bcm2836mp_pic_find_pending_irqs,
99 .pic_establish_irq = bcm2836mp_pic_establish_irq,
100 .pic_source_name = bcm2836mp_pic_source_name,
101 #if defined(MULTIPROCESSOR)
102 .pic_cpu_init = bcm2836mp_cpu_init,
103 .pic_ipi_send = bcm2836mp_send_ipi,
104 #endif
105 };
106
107 struct pic_softc bcm2836mp_pic[BCM2836_NCPUS] = {
108 [0 ... BCM2836_NCPUS - 1] = {
109 .pic_ops = &bcm2836mp_picops,
110 .pic_maxsources = BCM2836_NIRQPERCPU,
111 .pic_name = "bcm2836 pic",
112 }
113 };
114 #endif
115
116 struct bcm2835icu_softc {
117 device_t sc_dev;
118 bus_space_tag_t sc_iot;
119 bus_space_handle_t sc_ioh;
120 struct pic_softc *sc_pic;
121 };
122
123 struct bcm2835icu_softc *bcmicu_sc;
124
125 #define read_bcm2835reg(o) \
126 bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
127
128 #define write_bcm2835reg(o, v) \
129 bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
130
131
132 #define bcm2835_barrier() \
133 bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
134 BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
135
136 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
137 "(unused 0)", "(unused 1)", "(unused 2)", "timer3",
138 "(unused 4)", "(unused 5)", "(unused 6)", "jpeg",
139 "(unused 8)", "usb", "(unused 10)", "(unused 11)",
140 "(unused 12)", "(unused 13)", "(unused 14)", "(unused 15)",
141 "dma0", "dma1", "dma2", "dma3",
142 "dma4", "dma5", "dma6", "dma7",
143 "dma8", "dma9", "dma10", "dma11",
144 "dma12", "aux", "(unused 30)", "(unused 31)",
145 "(unused 32)", "(unused 33)", "(unused 34)", "(unused 35)",
146 "(unused 36)", "(unused 37)", "(unused 38)", "(unused 39)",
147 "(unused 40)", "(unused 41)", "(unused 42)", "i2c spl slv",
148 "(unused 44)", "pwa0", "pwa1", "(unused 47)",
149 "smi", "gpio[0]", "gpio[1]", "gpio[2]",
150 "gpio[3]", "i2c", "spi", "pcm",
151 "sdhost", "uart", "(unused 58)", "(unused 59)",
152 "(unused 60)", "(unused 61)", "emmc", "(unused 63)",
153 "Timer", "Mailbox", "Doorbell0", "Doorbell1",
154 "GPU0 Halted", "GPU1 Halted", "Illegal #1", "Illegal #0"
155 };
156
157 #if defined(BCM2836)
158 static const char * const bcm2836mp_sources[BCM2836_NIRQPERCPU] = {
159 "cntpsirq", "cntpnsirq", "cnthpirq", "cntvirq",
160 "mailbox0", "mailbox1", "mailbox2", "mailbox3",
161 };
162 #endif
163
164 #define BCM2836_INTBIT_GPUPENDING __BIT(8)
165
166 #define BCM2835_INTBIT_PENDING1 __BIT(8)
167 #define BCM2835_INTBIT_PENDING2 __BIT(9)
168 #define BCM2835_INTBIT_ARM __BITS(0,7)
169 #define BCM2835_INTBIT_GPU0 __BITS(10,14)
170 #define BCM2835_INTBIT_GPU1 __BITS(15,20)
171
172 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
173 bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
174
175 static int
176 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
177 {
178 struct amba_attach_args *aaa = aux;
179
180 if (strcmp(aaa->aaa_name, "icu") != 0)
181 return 0;
182
183 return 1;
184 }
185
186 static void
187 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
188 {
189 struct bcm2835icu_softc *sc = device_private(self);
190 struct amba_attach_args *aaa = aux;
191
192 sc->sc_dev = self;
193 sc->sc_iot = aaa->aaa_iot;
194 sc->sc_pic = &bcm2835_pic;
195
196 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, aaa->aaa_size, 0,
197 &sc->sc_ioh)) {
198 aprint_error_dev(self, "unable to map device\n");
199 return;
200 }
201
202 bcmicu_sc = sc;
203
204 #if defined(BCM2836)
205 #if defined(MULTIPROCESSOR)
206 aprint_normal(": Multiprocessor");
207 bcm2836mp_intr_init(curcpu());
208 #else
209 pic_add(&bcm2836mp_pic[0], BCM2836_INT_BASECPUN(0));
210 #endif
211 #endif /* BCM2836 */
212 pic_add(sc->sc_pic, BCM2835_INT_BASE);
213
214 aprint_normal("\n");
215 }
216
217 void
218 bcm2835_irq_handler(void *frame)
219 {
220 struct cpu_info * const ci = curcpu();
221 const int oldipl = ci->ci_cpl;
222 const cpuid_t cpuid = ci->ci_cpuid;
223 const uint32_t oldipl_mask = __BIT(oldipl);
224 int ipl_mask = 0;
225
226 ci->ci_data.cpu_nintr++;
227
228 bcm2835_barrier();
229 if (cpuid == 0) {
230 ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
231 }
232 #if defined(BCM2836)
233 ipl_mask |= bcm2836mp_pic_find_pending_irqs(&bcm2836mp_pic[cpuid]);
234 #endif
235
236 /*
237 * Record the pending_ipls and deliver them if we can.
238 */
239 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
240 pic_do_pending_ints(I32_bit, oldipl, frame);
241 }
242
243 static void
244 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
245 uint32_t irq_mask)
246 {
247
248 write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
249 bcm2835_barrier();
250 }
251
252 static void
253 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
254 uint32_t irq_mask)
255 {
256
257 write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
258 bcm2835_barrier();
259 }
260
261 /*
262 * Called with interrupts disabled
263 */
264 static int
265 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
266 {
267 int ipl = 0;
268 uint32_t bpending, gpu0irq, gpu1irq, armirq;
269
270 bcm2835_barrier();
271 bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
272 if (bpending == 0)
273 return 0;
274
275 armirq = bpending & BCM2835_INTBIT_ARM;
276 gpu0irq = bpending & BCM2835_INTBIT_GPU0;
277 gpu1irq = bpending & BCM2835_INTBIT_GPU1;
278
279 if (armirq) {
280 ipl |= pic_mark_pending_sources(pic,
281 BCM2835_INT_BASICBASE - BCM2835_INT_BASE, armirq);
282 }
283
284 if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
285 uint32_t pending1;
286
287 pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
288 ipl |= pic_mark_pending_sources(pic,
289 BCM2835_INT_GPU0BASE - BCM2835_INT_BASE, pending1);
290 }
291 if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
292 uint32_t pending2;
293
294 pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
295 ipl |= pic_mark_pending_sources(pic,
296 BCM2835_INT_GPU1BASE - BCM2835_INT_BASE, pending2);
297 }
298
299 return ipl;
300 }
301
302 static void
303 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
304 {
305
306 /* Nothing really*/
307 KASSERT(is->is_irq < BCM2835_NIRQ);
308 KASSERT(is->is_type == IST_LEVEL);
309 }
310
311 static void
312 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
313 {
314
315 strlcpy(buf, bcm2835_sources[irq], len);
316 }
317
318
319 #if defined(BCM2836)
320
321 #define BCM2836MP_TIMER_IRQS __BITS(3,0)
322 #define BCM2836MP_MAILBOX_IRQS __BITS(4,4)
323
324 #define BCM2836MP_ALL_IRQS \
325 (BCM2836MP_TIMER_IRQS | BCM2836MP_MAILBOX_IRQS)
326
327 static void
328 bcm2836mp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
329 uint32_t irq_mask)
330 {
331 struct cpu_info * const ci = curcpu();
332 const cpuid_t cpuid = ci->ci_cpuid;
333
334 KASSERT(pic == &bcm2836mp_pic[cpuid]);
335 KASSERT(irqbase == 0);
336
337 if (irq_mask & BCM2836MP_TIMER_IRQS) {
338 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
339 uint32_t val = bus_space_read_4(al_iot, al_ioh,
340 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
341 val |= mask;
342 bus_space_write_4(al_iot, al_ioh,
343 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
344 val);
345 bus_space_barrier(al_iot, al_ioh,
346 BCM2836_LOCAL_TIMER_IRQ_CONTROL_BASE,
347 BCM2836_LOCAL_TIMER_IRQ_CONTROL_SIZE,
348 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
349 }
350 if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
351 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
352 uint32_t val = bus_space_read_4(al_iot, al_ioh,
353 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
354 val |= mask;
355 bus_space_write_4(al_iot, al_ioh,
356 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
357 val);
358 bus_space_barrier(al_iot, al_ioh,
359 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_BASE,
360 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_SIZE,
361 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
362 }
363
364 return;
365 }
366
367 static void
368 bcm2836mp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
369 uint32_t irq_mask)
370 {
371 struct cpu_info * const ci = curcpu();
372 const cpuid_t cpuid = ci->ci_cpuid;
373
374 KASSERT(pic == &bcm2836mp_pic[cpuid]);
375 KASSERT(irqbase == 0);
376
377 if (irq_mask & BCM2836MP_TIMER_IRQS) {
378 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
379 uint32_t val = bus_space_read_4(al_iot, al_ioh,
380 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
381 val &= ~mask;
382 bus_space_write_4(al_iot, al_ioh,
383 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
384 val);
385 }
386 if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
387 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
388 uint32_t val = bus_space_read_4(al_iot, al_ioh,
389 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
390 val &= ~mask;
391 bus_space_write_4(al_iot, al_ioh,
392 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
393 val);
394 }
395
396 bcm2835_barrier();
397 return;
398 }
399
400 static int
401 bcm2836mp_pic_find_pending_irqs(struct pic_softc *pic)
402 {
403 struct cpu_info * const ci = curcpu();
404 const cpuid_t cpuid = ci->ci_cpuid;
405 uint32_t lpending;
406 int ipl = 0;
407
408 KASSERT(pic == &bcm2836mp_pic[cpuid]);
409
410 bcm2835_barrier();
411
412 lpending = bus_space_read_4(al_iot, al_ioh,
413 BCM2836_LOCAL_INTC_IRQPENDINGN(cpuid));
414
415 lpending &= ~BCM2836_INTBIT_GPUPENDING;
416 if (lpending & BCM2836MP_ALL_IRQS) {
417 ipl |= pic_mark_pending_sources(pic, 0 /* BCM2836_INT_LOCALBASE */,
418 lpending & BCM2836MP_ALL_IRQS);
419 }
420
421 return ipl;
422 }
423
424 static void
425 bcm2836mp_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
426 {
427 /* Nothing really*/
428 KASSERT(is->is_irq >= 0);
429 KASSERT(is->is_irq < BCM2836_NIRQPERCPU);
430 }
431
432 static void
433 bcm2836mp_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
434 {
435
436 irq %= BCM2836_NIRQPERCPU;
437 strlcpy(buf, bcm2836mp_sources[irq], len);
438 }
439
440
441 #ifdef MULTIPROCESSOR
442 static void bcm2836mp_cpu_init(struct pic_softc *pic, struct cpu_info *ci)
443 {
444
445 /* Enable IRQ and not FIQ */
446 bus_space_write_4(al_iot, al_ioh,
447 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(ci->ci_cpuid), 1);
448 }
449
450
451 static void
452 bcm2836mp_send_ipi(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
453 {
454 KASSERT(pic != NULL);
455 KASSERT(pic != &bcm2835_pic);
456 KASSERT(pic->pic_cpus != NULL);
457
458 const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
459
460 bus_space_write_4(al_iot, al_ioh,
461 BCM2836_LOCAL_MAILBOX0_SETN(cpuid), __BIT(ipi));
462 }
463
464 int
465 bcm2836mp_ipi_handler(void *priv)
466 {
467 const struct cpu_info *ci = curcpu();
468 const cpuid_t cpuid = ci->ci_cpuid;
469 uint32_t ipimask, bit;
470
471 ipimask = bus_space_read_4(al_iot, al_ioh,
472 BCM2836_LOCAL_MAILBOX0_CLRN(cpuid));
473 bus_space_write_4(al_iot, al_ioh, BCM2836_LOCAL_MAILBOX0_CLRN(cpuid),
474 ipimask);
475
476 while ((bit = ffs(ipimask)) > 0) {
477 const u_int ipi = bit - 1;
478 switch (ipi) {
479 case IPI_AST:
480 pic_ipi_ast(priv);
481 break;
482 case IPI_NOP:
483 pic_ipi_nop(priv);
484 break;
485 #ifdef __HAVE_PREEMPTION
486 case IPI_KPREEMPT:
487 pic_ipi_kpreempt(priv);
488 break;
489 #endif
490 case IPI_XCALL:
491 pic_ipi_xcall(priv);
492 break;
493 case IPI_GENERIC:
494 pic_ipi_generic(priv);
495 break;
496 case IPI_SHOOTDOWN:
497 pic_ipi_shootdown(priv);
498 break;
499 #ifdef DDB
500 case IPI_DDB:
501 pic_ipi_ddb(priv);
502 break;
503 #endif
504 }
505 ipimask &= ~__BIT(ipi);
506 }
507
508 return 1;
509 }
510
511 void
512 bcm2836mp_intr_init(struct cpu_info *ci)
513 {
514 const cpuid_t cpuid = ci->ci_cpuid;
515 struct pic_softc * const pic = &bcm2836mp_pic[cpuid];
516
517 pic->pic_cpus = ci->ci_kcpuset;
518 pic_add(pic, BCM2836_INT_BASECPUN(cpuid));
519
520 intr_establish(BCM2836_INT_MAILBOX0_CPUN(cpuid), IPL_HIGH,
521 IST_LEVEL | IST_MPSAFE, bcm2836mp_ipi_handler, NULL);
522
523 /* clock interrupt will attach with gtmr */
524 if (cpuid == 0)
525 return;
526
527 intr_establish(BCM2836_INT_CNTVIRQ_CPUN(cpuid), IPL_CLOCK,
528 IST_LEVEL | IST_MPSAFE, gtmr_intr, NULL);
529
530 }
531 #endif
532
533 #endif
534