bcm2835_intr.c revision 1.3.12.2 1 /* $NetBSD: bcm2835_intr.c,v 1.3.12.2 2015/03/11 20:22:55 snj Exp $ */
2
3 /*-
4 * Copyright (c) 2012 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.3.12.2 2015/03/11 20:22:55 snj 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 #if 0
71 #ifdef MULTIPROCESSOR
72 int bcm2836mp_ipi_handler(void *);
73 static void bcm2836mp_cpu_init(struct pic_softc *, struct cpu_info *);
74 static void bcm2836mp_send_ipi(struct pic_softc *, const kcpuset_t *, u_long);
75 #endif
76 #endif
77 #endif
78
79 #ifdef MULTIPROCESSOR
80 static void
81 bcm2835_dummy(struct pic_softc *pic, const kcpuset_t *kcp, u_long ipi)
82 {
83 }
84 #endif
85
86 static int bcm2835_icu_match(device_t, cfdata_t, void *);
87 static void bcm2835_icu_attach(device_t, device_t, void *);
88
89 static struct pic_ops bcm2835_picops = {
90 .pic_unblock_irqs = bcm2835_pic_unblock_irqs,
91 .pic_block_irqs = bcm2835_pic_block_irqs,
92 .pic_find_pending_irqs = bcm2835_pic_find_pending_irqs,
93 .pic_establish_irq = bcm2835_pic_establish_irq,
94 .pic_source_name = bcm2835_pic_source_name,
95 #if defined(MULTIPROCESSOR)
96 .pic_ipi_send = bcm2835_dummy,
97 #endif
98 };
99
100 struct pic_softc bcm2835_pic = {
101 .pic_ops = &bcm2835_picops,
102 .pic_maxsources = BCM2835_NIRQ,
103 .pic_name = "bcm2835 pic",
104 };
105
106 #if defined(BCM2836)
107 static struct pic_ops bcm2836mp_picops = {
108 .pic_unblock_irqs = bcm2836mp_pic_unblock_irqs,
109 .pic_block_irqs = bcm2836mp_pic_block_irqs,
110 .pic_find_pending_irqs = bcm2836mp_pic_find_pending_irqs,
111 .pic_establish_irq = bcm2836mp_pic_establish_irq,
112 .pic_source_name = bcm2836mp_pic_source_name,
113 #if 0 && defined(MULTIPROCESSOR)
114 .pic_cpu_init = bcm2836mp_cpu_init,
115 .pic_ipi_send = bcm2836mp_send_ipi,
116 #endif
117 };
118
119 struct pic_softc bcm2836mp_pic = {
120 .pic_ops = &bcm2836mp_picops,
121 .pic_maxsources = BCM2836MP_NIRQ,
122 .pic_name = "bcm2836 mp pic",
123 };
124 #endif
125
126 struct bcm2835icu_softc {
127 device_t sc_dev;
128 bus_space_tag_t sc_iot;
129 bus_space_handle_t sc_ioh;
130 struct pic_softc *sc_pic;
131 };
132
133 struct bcm2835icu_softc *bcmicu_sc;
134
135 #define read_bcm2835reg(o) \
136 bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
137
138 #define write_bcm2835reg(o, v) \
139 bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
140
141
142 #define bcm2835_barrier() \
143 bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
144 BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
145
146 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
147 "(unused 0)", "(unused 1)", "(unused 2)", "timer3",
148 "(unused 4)", "(unused 5)", "(unused 6)", "jpeg",
149 "(unused 8)", "usb", "(unused 10)", "(unused 11)",
150 "(unused 12)", "(unused 13)", "(unused 14)", "(unused 15)",
151 "dma0", "dma1", "dma2", "dma3",
152 "dma4", "dma5", "dma6", "dma7",
153 "dma8", "dma9", "dma10", "dma11",
154 "dma12", "aux", "(unused 30)", "(unused 31)",
155 "(unused 32)", "(unused 33)", "(unused 34)", "(unused 35)",
156 "(unused 36)", "(unused 37)", "(unused 38)", "(unused 39)",
157 "(unused 40)", "(unused 41)", "(unused 42)", "i2c spl slv",
158 "(unused 44)", "pwa0", "pwa1", "(unused 47)",
159 "smi", "gpio[0]", "gpio[1]", "gpio[2]",
160 "gpio[3]", "i2c", "spi", "pcm",
161 "sdio", "uart", "(unused 58)", "(unused 59)",
162 "(unused 60)", "(unused 61)", "emmc", "(unused 63)",
163 "Timer", "Mailbox", "Doorbell0", "Doorbell1",
164 "GPU0 Halted", "GPU1 Halted", "Illegal #1", "Illegal #0"
165 };
166
167 #if defined(BCM2836)
168 static const char * const bcm2836mp_sources[BCM2836MP_NIRQ] = {
169 "cntpsirq", "cntpnsirq", "cnthpirq", "cntvirq",
170 "mailbox0", "mailbox1", "mailbox2", "mailbox3",
171 };
172 #endif
173
174 #define BCM2836_INTBIT_GPUPENDING __BIT(8)
175
176 #define BCM2835_INTBIT_PENDING1 __BIT(8)
177 #define BCM2835_INTBIT_PENDING2 __BIT(9)
178 #define BCM2835_INTBIT_ARM __BITS(0,7)
179 #define BCM2835_INTBIT_GPU0 __BITS(10,14)
180 #define BCM2835_INTBIT_GPU1 __BITS(15,20)
181
182 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
183 bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
184
185 static int
186 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
187 {
188 struct amba_attach_args *aaa = aux;
189
190 if (strcmp(aaa->aaa_name, "icu") != 0)
191 return 0;
192
193 return 1;
194 }
195
196 static void
197 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
198 {
199 struct bcm2835icu_softc *sc = device_private(self);
200 struct amba_attach_args *aaa = aux;
201
202 sc->sc_dev = self;
203 sc->sc_iot = aaa->aaa_iot;
204 sc->sc_pic = &bcm2835_pic;
205
206 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, aaa->aaa_size, 0,
207 &sc->sc_ioh)) {
208 aprint_error_dev(self, "unable to map device\n");
209 return;
210 }
211
212 bcmicu_sc = sc;
213
214 pic_add(sc->sc_pic, 0);
215
216 #if defined(BCM2836)
217 #if 0 && defined(MULTIPROCESSOR)
218 aprint_normal(": Multiprocessor");
219 #endif
220 pic_add(&bcm2836mp_pic, BCM2836_INT_LOCALBASE);
221 #endif
222
223 aprint_normal("\n");
224 }
225
226 void
227 bcm2835_irq_handler(void *frame)
228 {
229 struct cpu_info * const ci = curcpu();
230 const int oldipl = ci->ci_cpl;
231 const uint32_t oldipl_mask = __BIT(oldipl);
232 int ipl_mask = 0;
233
234 ci->ci_data.cpu_nintr++;
235
236 bcm2835_barrier();
237 ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
238 #if defined(BCM2836)
239 ipl_mask |= bcm2836mp_pic_find_pending_irqs(&bcm2836mp_pic);
240 #endif
241
242 /*
243 * Record the pending_ipls and deliver them if we can.
244 */
245 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
246 pic_do_pending_ints(I32_bit, oldipl, frame);
247 }
248
249 static void
250 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
251 uint32_t irq_mask)
252 {
253
254 write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
255 bcm2835_barrier();
256 }
257
258 static void
259 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
260 uint32_t irq_mask)
261 {
262
263 write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
264 bcm2835_barrier();
265 }
266
267 /*
268 * Called with interrupts disabled
269 */
270 static int
271 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
272 {
273 int ipl = 0;
274 uint32_t bpending, gpu0irq, gpu1irq, armirq;
275
276 bcm2835_barrier();
277 bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
278 if (bpending == 0)
279 return 0;
280
281 armirq = bpending & BCM2835_INTBIT_ARM;
282 gpu0irq = bpending & BCM2835_INTBIT_GPU0;
283 gpu1irq = bpending & BCM2835_INTBIT_GPU1;
284
285 if (armirq) {
286 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_BASICBASE,
287 armirq);
288
289 }
290
291 if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
292 uint32_t pending1;
293
294 pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
295 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_GPU0BASE,
296 pending1);
297 }
298 if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
299 uint32_t pending2;
300
301 pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
302 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_GPU1BASE,
303 pending2);
304 }
305
306 return ipl;
307 }
308
309 static void
310 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
311 {
312
313 /* Nothing really*/
314 KASSERT(is->is_irq < BCM2835_NIRQ);
315 KASSERT(is->is_type == IST_LEVEL);
316 }
317
318 static void
319 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
320 {
321
322 strlcpy(buf, bcm2835_sources[irq], len);
323 }
324
325
326 #if defined(BCM2836)
327
328 #define BCM2836MP_TIMER_IRQS __BITS(3,0)
329 #define BCM2836MP_MAILBOX_IRQS __BITS(4,4)
330
331 #define BCM2836MP_ALL_IRQS \
332 (BCM2836MP_TIMER_IRQS | BCM2836MP_MAILBOX_IRQS)
333
334 static void
335 bcm2836mp_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
336 uint32_t irq_mask)
337 {
338 const int cpuid = 0;
339
340 //printf("%s: irqbase %zu irq_mask %08x\n", __func__, irqbase, irq_mask);
341
342 if (irq_mask & BCM2836MP_TIMER_IRQS) {
343 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
344 uint32_t val = bus_space_read_4(al_iot, al_ioh,
345 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
346 val |= mask;
347 bus_space_write_4(al_iot, al_ioh,
348 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
349 val);
350 bus_space_barrier(al_iot, al_ioh,
351 BCM2836_LOCAL_TIMER_IRQ_CONTROL_BASE,
352 BCM2836_LOCAL_TIMER_IRQ_CONTROL_SIZE,
353 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
354 //printf("%s: val %08x\n", __func__, val);
355 } else if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
356 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
357 uint32_t val = bus_space_read_4(al_iot, al_ioh,
358 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
359 val |= mask;
360 bus_space_write_4(al_iot, al_ioh,
361 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
362 val);
363 bus_space_barrier(al_iot, al_ioh,
364 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_BASE,
365 BCM2836_LOCAL_MAILBOX_IRQ_CONTROL_SIZE,
366 BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE);
367 }
368
369 return;
370 }
371
372 static void
373 bcm2836mp_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
374 uint32_t irq_mask)
375 {
376 const int cpuid = 0;
377
378 //printf("%s: irqbase %zu irq_mask %08x\n", __func__, irqbase, irq_mask);
379 if (irq_mask & BCM2836MP_TIMER_IRQS) {
380 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_TIMER_IRQS);
381 uint32_t val = bus_space_read_4(al_iot, al_ioh,
382 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid));
383 val &= ~mask;
384 bus_space_write_4(al_iot, al_ioh,
385 BCM2836_LOCAL_TIMER_IRQ_CONTROLN(cpuid),
386 val);
387 //printf("%s: val %08x\n", __func__, val);
388 } else if (irq_mask & BCM2836MP_MAILBOX_IRQS) {
389 uint32_t mask = __SHIFTOUT(irq_mask, BCM2836MP_MAILBOX_IRQS);
390 uint32_t val = bus_space_read_4(al_iot, al_ioh,
391 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid));
392 val &= ~mask;
393 bus_space_write_4(al_iot, al_ioh,
394 BCM2836_LOCAL_MAILBOX_IRQ_CONTROLN(cpuid),
395 val);
396 }
397
398 bcm2835_barrier();
399 return;
400 }
401
402
403 static int
404 bcm2836mp_pic_find_pending_irqs(struct pic_softc *pic)
405 {
406 const int cpuid = 0;
407 uint32_t lpending;
408 int ipl = 0;
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
428 /* Nothing really*/
429 KASSERT(is->is_irq >= 0);
430 KASSERT(is->is_irq < BCM2836MP_NIRQ);
431 // KASSERT(is->is_type == IST_LEVEL);
432
433
434 }
435
436 static void
437 bcm2836mp_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
438 {
439 irq %= 32;
440 strlcpy(buf, bcm2836mp_sources[irq], len);
441 }
442 #endif
443