bcm2835_intr.c revision 1.3.12.1 1 /* $NetBSD: bcm2835_intr.c,v 1.3.12.1 2014/09/11 14:20:11 martin 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.1 2014/09/11 14:20:11 martin Exp $");
34
35 #define _INTR_PRIVATE
36
37 #include <sys/param.h>
38 #include <sys/proc.h>
39 #include <sys/device.h>
40
41 #include <machine/intr.h>
42 #include <sys/bus.h>
43
44 #include <arm/pic/picvar.h>
45
46 #include <arm/broadcom/bcm_amba.h>
47 #include <arm/broadcom/bcm2835reg.h>
48
49 static void bcm2835_pic_unblock_irqs(struct pic_softc *, size_t, uint32_t);
50 static void bcm2835_pic_block_irqs(struct pic_softc *, size_t, uint32_t);
51 static int bcm2835_pic_find_pending_irqs(struct pic_softc *);
52 static void bcm2835_pic_establish_irq(struct pic_softc *, struct intrsource *);
53 static void bcm2835_pic_source_name(struct pic_softc *, int, char *,
54 size_t);
55
56 static int bcm2835_icu_match(device_t, cfdata_t, void *);
57 static void bcm2835_icu_attach(device_t, device_t, void *);
58
59 static struct pic_ops bcm2835_picops = {
60 .pic_unblock_irqs = bcm2835_pic_unblock_irqs,
61 .pic_block_irqs = bcm2835_pic_block_irqs,
62 .pic_find_pending_irqs = bcm2835_pic_find_pending_irqs,
63 .pic_establish_irq = bcm2835_pic_establish_irq,
64 .pic_source_name = bcm2835_pic_source_name,
65 };
66
67 struct pic_softc bcm2835_pic = {
68 .pic_ops = &bcm2835_picops,
69 .pic_maxsources = BCM2835_NIRQ,
70 .pic_name = "bcm2835 pic",
71 };
72
73 struct bcm2835icu_softc {
74 device_t sc_dev;
75 bus_space_tag_t sc_iot;
76 bus_space_handle_t sc_ioh;
77 struct pic_softc *sc_pic;
78 };
79
80 struct bcm2835icu_softc *bcmicu_sc;
81
82 #define read_bcm2835reg(o) \
83 bus_space_read_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o))
84
85 #define write_bcm2835reg(o, v) \
86 bus_space_write_4(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, (o), (v))
87
88
89 #define bcm2835_barrier() \
90 bus_space_barrier(bcmicu_sc->sc_iot, bcmicu_sc->sc_ioh, 0, \
91 BCM2835_ARMICU_SIZE, BUS_SPACE_BARRIER_READ|BUS_SPACE_BARRIER_WRITE)
92
93 static const char * const bcm2835_sources[BCM2835_NIRQ] = {
94 "(unused 0)", "(unused 1)", "(unused 2)", "timer3",
95 "(unused 4)", "(unused 5)", "(unused 6)", "jpeg",
96 "(unused 8)", "usb", "(unused 10)", "(unused 11)",
97 "(unused 12)", "(unused 13)", "(unused 14)", "(unused 15)",
98 "dma0", "dma1", "dma2", "dma3",
99 "dma4", "dma5", "dma6", "dma7",
100 "dma8", "dma9", "dma10", "dma11",
101 "dma12", "aux", "(unused 30)", "(unused 31)",
102 "(unused 32)", "(unused 33)", "(unused 34)", "(unused 35)",
103 "(unused 36)", "(unused 37)", "(unused 38)", "(unused 39)",
104 "(unused 40)", "(unused 41)", "(unused 42)", "i2c spl slv",
105 "(unused 44)", "pwa0", "pwa1", "(unused 47)",
106 "smi", "gpio[0]", "gpio[1]", "gpio[2]",
107 "gpio[3]", "i2c", "spi", "pcm",
108 "sdio", "uart", "(unused 58)", "(unused 59)",
109 "(unused 60)", "(unused 61)", "emmc", "(unused 63)",
110 "Timer", "Mailbox", "Doorbell0", "Doorbell1",
111 "GPU0 Halted", "GPU1 Halted", "Illegal #1", "Illegal #0"
112 };
113
114 #define BCM2835_INTBIT_PENDING1 __BIT(8)
115 #define BCM2835_INTBIT_PENDING2 __BIT(9)
116 #define BCM2835_INTBIT_ARM __BITS(0,7)
117 #define BCM2835_INTBIT_GPU0 __BITS(10,14)
118 #define BCM2835_INTBIT_GPU1 __BITS(15,20)
119
120 CFATTACH_DECL_NEW(bcmicu, sizeof(struct bcm2835icu_softc),
121 bcm2835_icu_match, bcm2835_icu_attach, NULL, NULL);
122
123 static int
124 bcm2835_icu_match(device_t parent, cfdata_t cf, void *aux)
125 {
126 struct amba_attach_args *aaa = aux;
127
128 if (strcmp(aaa->aaa_name, "icu") != 0)
129 return 0;
130
131 return 1;
132 }
133
134 static void
135 bcm2835_icu_attach(device_t parent, device_t self, void *aux)
136 {
137 struct bcm2835icu_softc *sc = device_private(self);
138 struct amba_attach_args *aaa = aux;
139
140 sc->sc_dev = self;
141 sc->sc_iot = aaa->aaa_iot;
142 sc->sc_pic = &bcm2835_pic;
143
144 if (bus_space_map(aaa->aaa_iot, aaa->aaa_addr, aaa->aaa_size, 0,
145 &sc->sc_ioh)) {
146 aprint_error_dev(self, "unable to map device\n");
147 return;
148 }
149
150 bcmicu_sc = sc;
151 pic_add(sc->sc_pic, 0);
152 aprint_normal("\n");
153 }
154
155 void
156 bcm2835_irq_handler(void *frame)
157 {
158 struct cpu_info * const ci = curcpu();
159 const int oldipl = ci->ci_cpl;
160 const uint32_t oldipl_mask = __BIT(oldipl);
161 int ipl_mask = 0;
162
163 ci->ci_data.cpu_nintr++;
164
165 bcm2835_barrier();
166 ipl_mask = bcm2835_pic_find_pending_irqs(&bcm2835_pic);
167
168 /*
169 * Record the pending_ipls and deliver them if we can.
170 */
171 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
172 pic_do_pending_ints(I32_bit, oldipl, frame);
173 }
174
175 static void
176 bcm2835_pic_unblock_irqs(struct pic_softc *pic, size_t irqbase,
177 uint32_t irq_mask)
178 {
179
180 write_bcm2835reg(BCM2835_INTC_ENABLEBASE + (irqbase >> 3), irq_mask);
181 bcm2835_barrier();
182 }
183
184 static void
185 bcm2835_pic_block_irqs(struct pic_softc *pic, size_t irqbase,
186 uint32_t irq_mask)
187 {
188
189 write_bcm2835reg(BCM2835_INTC_DISABLEBASE + (irqbase >> 3), irq_mask);
190 bcm2835_barrier();
191 }
192
193 /*
194 * Called with interrupts disabled
195 */
196 static int
197 bcm2835_pic_find_pending_irqs(struct pic_softc *pic)
198 {
199 int ipl = 0;
200 uint32_t bpending, gpu0irq, gpu1irq, armirq;
201
202 bcm2835_barrier();
203 bpending = read_bcm2835reg(BCM2835_INTC_IRQBPENDING);
204 if (bpending == 0)
205 return 0;
206
207 armirq = bpending & BCM2835_INTBIT_ARM;
208 gpu0irq = bpending & BCM2835_INTBIT_GPU0;
209 gpu1irq = bpending & BCM2835_INTBIT_GPU1;
210
211 if (armirq) {
212 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_BASICBASE,
213 armirq);
214
215 }
216
217 if (gpu0irq || (bpending & BCM2835_INTBIT_PENDING1)) {
218 uint32_t pending1;
219
220 pending1 = read_bcm2835reg(BCM2835_INTC_IRQ1PENDING);
221 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_GPU0BASE,
222 pending1);
223 }
224 if (gpu1irq || (bpending & BCM2835_INTBIT_PENDING2)) {
225 uint32_t pending2;
226
227 pending2 = read_bcm2835reg(BCM2835_INTC_IRQ2PENDING);
228 ipl |= pic_mark_pending_sources(pic, BCM2835_INT_GPU1BASE,
229 pending2);
230 }
231
232 return ipl;
233 }
234
235 static void
236 bcm2835_pic_establish_irq(struct pic_softc *pic, struct intrsource *is)
237 {
238
239 /* Nothing really*/
240 KASSERT(is->is_irq < BCM2835_NIRQ);
241 KASSERT(is->is_type == IST_LEVEL);
242 }
243
244 static void
245 bcm2835_pic_source_name(struct pic_softc *pic, int irq, char *buf, size_t len)
246 {
247
248 strlcpy(buf, bcm2835_sources[irq], len);
249 }
250