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