bcm2835_intr.c revision 1.4.2.2 1 /* $NetBSD: bcm2835_intr.c,v 1.4.2.2 2015/06/06 14:39:55 skrll 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.4.2.2 2015/06/06 14:39:55 skrll 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] = {
109 .pic_ops = &bcm2836mp_picops,
110 .pic_maxsources = BCM2836_NIRQPERCPU,
111 .pic_name = "bcm2836 pic",
112 },
113 [1] = {
114 .pic_ops = &bcm2836mp_picops,
115 .pic_maxsources = BCM2836_NIRQPERCPU,
116 .pic_name = "bcm2836 pic",
117 },
118 [2] = {
119 .pic_ops = &bcm2836mp_picops,
120 .pic_maxsources = BCM2836_NIRQPERCPU,
121 .pic_name = "bcm2836 pic",
122 },
123 [3] = {
124 .pic_ops = &bcm2836mp_picops,
125 .pic_maxsources = BCM2836_NIRQPERCPU,
126 .pic_name = "bcm2836 pic",
127 },
128 };
129 #endif
130
131 struct bcm2835icu_softc {
132 device_t sc_dev;
133 bus_space_tag_t sc_iot;
134 bus_space_handle_t sc_ioh;
135 struct pic_softc *sc_pic;
136 };
137
138 struct bcm2835icu_softc *bcmicu_sc;
139
140 #define read_bcm2835reg(o) \
141 bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
142
143 #define write_bcm2835reg(o, v) \
144 bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
145
146
147 #define bcm2835_barrier() \
148 bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
149 BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
150
151 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
152 "(unused 0)", "(unused 1)", "(unused 2)", "timer3",
153 "(unused 4)", "(unused 5)", "(unused 6)", "jpeg",
154 "(unused 8)", "usb", "(unused 10)", "(unused 11)",
155 "(unused 12)", "(unused 13)", "(unused 14)", "(unused 15)",
156 "dma0", "dma1", "dma2", "dma3",
157 "dma4", "dma5", "dma6", "dma7",
158 "dma8", "dma9", "dma10", "dma11",
159 "dma12", "aux", "(unused 30)", "(unused 31)",
160 "(unused 32)", "(unused 33)", "(unused 34)", "(unused 35)",
161 "(unused 36)", "(unused 37)", "(unused 38)", "(unused 39)",
162 "(unused 40)", "(unused 41)", "(unused 42)", "i2c spl slv",
163 "(unused 44)", "pwa0", "pwa1", "(unused 47)",
164 "smi", "gpio[0]", "gpio[1]", "gpio[2]",
165 "gpio[3]", "i2c", "spi", "pcm",
166 "sdio", "uart", "(unused 58)", "(unused 59)",
167 "(unused 60)", "(unused 61)", "emmc", "(unused 63)",
168 "Timer", "Mailbox", "Doorbell0", "Doorbell1",
169 "GPU0 Halted", "GPU1 Halted", "Illegal #1", "Illegal #0"
170 };
171
172 #if defined(BCM2836)
173 static const char * const bcm2836mp_sources[BCM2836_NIRQPERCPU] = {
174 "cntpsirq", "cntpnsirq", "cnthpirq", "cntvirq",
175 "mailbox0", "mailbox1", "mailbox2", "mailbox3",
176 };
177 #endif
178
179 #define BCM2836_INTBIT_GPUPENDING __BIT(8)
180
181 #define BCM2835_INTBIT_PENDING1 __BIT(8)
182 #define BCM2835_INTBIT_PENDING2 __BIT(9)
183 #define BCM2835_INTBIT_ARM __BITS(0,7)
184 #define BCM2835_INTBIT_GPU0 __BITS(10,14)
185 #define BCM2835_INTBIT_GPU1 __BITS(15,20)
186
187 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
188 bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
189
190 static int
191 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
192 {
193 struct amba_attach_args *aaa = aux;
194
195 if (strcmp(aaa->aaa_name, "icu") != 0)
196 return 0;
197
198 return 1;
199 }
200
201 static void
202 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
203 {
204 struct bcm2835icu_softc *sc = device_private(self);
205 struct amba_attach_args *aaa = aux;
206
207 sc->sc_dev = self;
208 sc->sc_iot = aaa->aaa_iot;
209 sc->sc_pic = &bcm2835_pic;
210
211 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, aaa->aaa_size, 0,
212 &sc->sc_ioh)) {
213 aprint_error_dev(self, "unable to map device\n");
214 return;
215 }
216
217 bcmicu_sc = sc;
218
219 #if defined(BCM2836)
220 #if defined(MULTIPROCESSOR)
221 aprint_normal(": Multiprocessor");
222 #endif
223
224 bcm2836mp_intr_init(curcpu());
225 #endif
226 pic_add(sc->sc_pic, BCM2835_INT_BASE);
227
228 aprint_normal("\n");
229 }
230
231 void
232 bcm2835_irq_handler(void *frame)
233 {
234 struct cpu_info * const ci = curcpu();
235 const int oldipl = ci->ci_cpl;
236 const cpuid_t cpuid = ci->ci_cpuid;
237 const uint32_t oldipl_mask = __BIT(oldipl);
238 int ipl_mask = 0;
239
240 ci->ci_data.cpu_nintr++;
241
242 bcm2835_barrier();
243 if (cpuid == 0) {
244 ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
245 }
246 #if defined(BCM2836)
247 ipl_mask |= bcm2836mp_pic_find_pending_irqs(&bcm2836mp_pic[cpuid]);
248 #endif
249
250 /*
251 * Record the pending_ipls and deliver them if we can.
252 */
253 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
254 pic_do_pending_ints(I32_bit, oldipl, frame);
255 }
256
257 static void
258 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
259 uint32_t irq_mask)
260 {
261
262 write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
263 bcm2835_barrier();
264 }
265
266 static void
267 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
268 uint32_t irq_mask)
269 {
270
271 write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
272 bcm2835_barrier();
273 }
274
275 /*
276 * Called with interrupts disabled
277 */
278 static int
279 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
280 {
281 int ipl = 0;
282 uint32_t bpending, gpu0irq, gpu1irq, armirq;
283
284 bcm2835_barrier();
285 bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
286 if (bpending == 0)
287 return 0;
288
289 armirq = bpending & BCM2835_INTBIT_ARM;
290 gpu0irq = bpending & BCM2835_INTBIT_GPU0;
291 gpu1irq = bpending & BCM2835_INTBIT_GPU1;
292
293 if (armirq) {
294 ipl |= pic_mark_pending_sources(pic,
295 BCM2835_INT_BASICBASE - BCM2835_INT_BASE, armirq);
296
297 }
298
299 if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
300 uint32_t pending1;
301
302 pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
303 ipl |= pic_mark_pending_sources(pic,
304 BCM2835_INT_GPU0BASE - BCM2835_INT_BASE, pending1);
305 }
306 if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
307 uint32_t pending2;
308
309 pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
310 ipl |= pic_mark_pending_sources(pic,
311 BCM2835_INT_GPU1BASE - BCM2835_INT_BASE, pending2);
312 }
313
314 return ipl;
315 }
316
317 static void
318 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
319 {
320
321 /* Nothing really*/
322 KASSERT(is->is_irq < BCM2835_NIRQ);
323 KASSERT(is->is_type == IST_LEVEL);
324 }
325
326 static void
327 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
328 {
329
330 strlcpy(buf, bcm2835_sources[irq], len);
331 }
332
333
334 #if defined(BCM2836)
335
336 #define BCM2836MP_TIMER_IRQS __BITS(3,0)
337 #define BCM2836MP_MAILBOX_IRQS __BITS(4,4)
338
339 #define BCM2836MP_ALL_IRQS \
340 (BCM2836MP_TIMER_IRQS | BCM2836MP_MAILBOX_IRQS)
341
342 static void
343 bcm2836mp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
344 uint32_t irq_mask)
345 {
346 struct cpu_info * const ci = curcpu();
347 const cpuid_t cpuid = ci->ci_cpuid;
348
349 KASSERT(pic == &bcm2836mp_pic[cpuid]);
350 KASSERT(irqbase == 0);
351
352 if (irq_mask & BCM2836MP_TIMER_IRQS) {
353 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
354 uint32_t val = bus_space_read_4(al_iot, al_ioh,
355 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
356 val |= mask;
357 bus_space_write_4(al_iot, al_ioh,
358 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
359 val);
360 bus_space_barrier(al_iot, al_ioh,
361 BCM2836_LOCAL_TIMER_IRQ_CONTROL_BASE,
362 BCM2836_LOCAL_TIMER_IRQ_CONTROL_SIZE,
363 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
364 } else if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
365 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
366 uint32_t val = bus_space_read_4(al_iot, al_ioh,
367 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
368 val |= mask;
369 bus_space_write_4(al_iot, al_ioh,
370 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
371 val);
372 bus_space_barrier(al_iot, al_ioh,
373 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_BASE,
374 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_SIZE,
375 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
376 }
377
378 return;
379 }
380
381 static void
382 bcm2836mp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
383 uint32_t irq_mask)
384 {
385 struct cpu_info * const ci = curcpu();
386 const cpuid_t cpuid = ci->ci_cpuid;
387
388 KASSERT(pic == &bcm2836mp_pic[cpuid]);
389 KASSERT(irqbase == 0);
390
391 if (irq_mask & BCM2836MP_TIMER_IRQS) {
392 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
393 uint32_t val = bus_space_read_4(al_iot, al_ioh,
394 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
395 val &= ~mask;
396 bus_space_write_4(al_iot, al_ioh,
397 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
398 val);
399 } else if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
400 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
401 uint32_t val = bus_space_read_4(al_iot, al_ioh,
402 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
403 val &= ~mask;
404 bus_space_write_4(al_iot, al_ioh,
405 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
406 val);
407 }
408
409 bcm2835_barrier();
410 return;
411 }
412
413 static int
414 bcm2836mp_pic_find_pending_irqs(struct pic_softc *pic)
415 {
416 struct cpu_info * const ci = curcpu();
417 const cpuid_t cpuid = ci->ci_cpuid;
418 uint32_t lpending;
419 int ipl = 0;
420
421 KASSERT(pic == &bcm2836mp_pic[cpuid]);
422
423 bcm2835_barrier();
424
425 lpending = bus_space_read_4(al_iot, al_ioh,
426 BCM2836_LOCAL_INTC_IRQPENDINGN(cpuid));
427
428 lpending &= ~BCM2836_INTBIT_GPUPENDING;
429 if (lpending & BCM2836MP_ALL_IRQS) {
430 ipl |= pic_mark_pending_sources(pic, 0 /* BCM2836_INT_LOCALBASE */,
431 lpending & BCM2836MP_ALL_IRQS);
432 }
433
434 return ipl;
435 }
436
437 static void
438 bcm2836mp_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
439 {
440 /* Nothing really*/
441 KASSERT(is->is_irq >= 0);
442 KASSERT(is->is_irq < BCM2836_NIRQPERCPU);
443 }
444
445 static void
446 bcm2836mp_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
447 {
448
449 irq %= BCM2836_NIRQPERCPU;
450 strlcpy(buf, bcm2836mp_sources[irq], len);
451 }
452
453
454 #ifdef MULTIPROCESSOR
455 static void bcm2836mp_cpu_init(struct pic_softc *pic, struct cpu_info *ci)
456 {
457
458 /* Enable IRQ and not FIQ */
459 bus_space_write_4(al_iot, al_ioh,
460 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(ci->ci_cpuid), 1);
461 }
462
463
464 static void
465 bcm2836mp_send_ipi(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
466 {
467 const cpuid_t cpuid = pic - &bcm2836mp_pic[0];
468
469 bus_space_write_4(al_iot, al_ioh,
470 BCM2836_LOCAL_MAILBOX0_SETN(cpuid), __BIT(ipi));
471 }
472
473 int
474 bcm2836mp_ipi_handler(void *priv)
475 {
476 const struct cpu_info *ci = curcpu();
477 const cpuid_t cpuid = ci->ci_cpuid;
478 uint32_t ipimask, bit;
479
480 ipimask = bus_space_read_4(al_iot, al_ioh,
481 BCM2836_LOCAL_MAILBOX0_CLRN(cpuid));
482 bus_space_write_4(al_iot, al_ioh, BCM2836_LOCAL_MAILBOX0_CLRN(cpuid),
483 ipimask);
484
485 while ((bit = ffs(ipimask)) > 0) {
486 const u_int ipi = bit - 1;
487 switch (ipi) {
488 case IPI_AST:
489 case IPI_NOP:
490 #ifdef __HAVE_PREEMPTION
491 case IPI_KPREEMPT:
492 #endif
493 pic_ipi_nop(priv);
494 break;
495 case IPI_XCALL:
496 pic_ipi_xcall(priv);
497 break;
498 case IPI_GENERIC:
499 pic_ipi_generic(priv);
500 break;
501 case IPI_SHOOTDOWN:
502 pic_ipi_shootdown(priv);
503 break;
504 #ifdef DDB
505 case IPI_DDB:
506 pic_ipi_ddb(priv);
507 break;
508 #endif
509 }
510 ipimask &= ~__BIT(ipi);
511 }
512
513 return 1;
514 }
515
516 void
517 bcm2836mp_intr_init(struct cpu_info *ci)
518 {
519 const cpuid_t cpuid = ci->ci_cpuid;
520 struct pic_softc * const pic = &bcm2836mp_pic[cpuid];
521
522 pic->pic_cpus = ci->ci_kcpuset;
523 pic_add(pic, BCM2836_INT_BASECPUN(cpuid));
524
525 intr_establish(BCM2836_INT_MAILBOX0_CPUN(cpuid), IPL_VM,
526 IST_LEVEL | IST_MPSAFE, bcm2836mp_ipi_handler, NULL);
527
528 /* clock interrupt will attach with gtmr */
529 if (cpuid == 0)
530 return;
531
532 intr_establish(BCM2836_INT_CNTVIRQ_CPUN(cpuid), IPL_CLOCK,
533 IST_LEVEL | IST_MPSAFE, gtmr_intr, NULL);
534
535 }
536 #endif
537
538 #endif
539