gemini_icu.c revision 1.4 1 /* $NetBSD: gemini_icu.c,v 1.4 2010/12/20 00:25:27 matt Exp $ */
2
3 /* adapted from:
4 * NetBSD: omap2_icu.c,v 1.4 2008/08/27 11:03:10 matt Exp
5 */
6
7 /*
8 * Define the SDP2430 specific information and then include the generic OMAP
9 * interrupt header.
10 */
11
12 /*
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain this list of conditions
17 * and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce this list of conditions
19 * and the following disclaimer in the documentation and/or other materials
20 * provided with the distribution.
21 *
22 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
24 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ANY
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33 #include "opt_gemini.h"
34 #include "geminiicu.h"
35
36 #define _INTR_PRIVATE
37
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD: gemini_icu.c,v 1.4 2010/12/20 00:25:27 matt Exp $");
40
41 #include <sys/param.h>
42 #include <sys/evcnt.h>
43
44 #include <uvm/uvm_extern.h>
45
46 #include <machine/intr.h>
47 #include <machine/bus.h>
48
49 #include <arm/cpu.h>
50 #include <arm/armreg.h>
51 #include <arm/cpufunc.h>
52 #include <arm/atomic.h>
53
54 #include <arm/pic/picvar.h>
55
56 #include <arm/gemini/gemini_reg.h>
57 #include <arm/gemini/gemini_obiovar.h>
58
59
60 #define INTC_READ(sc, o) \
61 bus_space_read_4((sc)->sc_memt, (sc)->sc_memh, (o))
62 #define INTC_WRITE(sc, o, v) \
63 bus_space_write_4((sc)->sc_memt, (sc)->sc_memh, (o), v)
64
65 static int geminiicu_match(device_t, cfdata_t, void *);
66 static void geminiicu_attach(device_t, device_t, void *);
67
68 static void geminiicu_unblock_irqs(struct pic_softc *, size_t, uint32_t);
69 static void geminiicu_block_irqs(struct pic_softc *, size_t, uint32_t);
70 static void geminiicu_establish_irq(struct pic_softc *, struct intrsource *);
71 static void geminiicu_source_name(struct pic_softc *, int, char *, size_t);
72
73 static const struct pic_ops geminiicu_picops = {
74 .pic_unblock_irqs = geminiicu_unblock_irqs,
75 .pic_block_irqs = geminiicu_block_irqs,
76 .pic_establish_irq = geminiicu_establish_irq,
77 .pic_source_name = geminiicu_source_name,
78 };
79
80 #define PICTOSOFTC(pic) \
81 ((void *)((uintptr_t)(pic) - offsetof(struct geminiicu_softc, sc_pic)))
82
83 static struct geminiicu_softc {
84 device_t sc_dev;
85 bus_space_tag_t sc_memt;
86 bus_space_handle_t sc_memh;
87 struct pic_softc sc_pic;
88 uint32_t sc_enabled_mask;
89 uint32_t sc_edge_mask;
90 uint32_t sc_edge_rising_mask;
91 uint32_t sc_edge_falling_mask;
92 uint32_t sc_level_mask;
93 uint32_t sc_level_hi_mask;
94 uint32_t sc_level_lo_mask;
95 } geminiicu_softc = {
96 .sc_pic = {
97 .pic_ops = &geminiicu_picops,
98 .pic_maxsources = 32,
99 .pic_name = "geminiicu",
100 },
101 };
102
103 static const char * const sources[32] = {
104 "ipi(0)", "gmac0(1)", "gmac1(2)", "wdt(3)",
105 "ide0(4)", "ide1(5)", "raid(6)", "crypto(7)",
106 "pci(8)", "dma(9)", "usb0(10)", "usb1(11)",
107 "flash(12)", "tve(13)", "timer0(14)", "timer1(15)",
108 "timer2(16)", "rtc(17)", "uart(18)", "lcd(19)",
109 "lpc(20)", "ssp(21)", "gpio0(22)", "gpio1(23)",
110 "gpio2(24)", "cir(25)", "power(26)", "irq 27",
111 "irq 28", "irq 29", "usbc0(30)", "usbc1(31)"
112 };
113
114 static void geminiicu_source_name(struct pic_softc *pic, int irq,
115 char *buf, size_t len)
116 {
117 KASSERT((unsigned int)irq < 32);
118 strlcpy(buf, sources[irq], len);
119 }
120
121 static void
122 geminiicu_unblock_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
123 {
124 struct geminiicu_softc * const sc = PICTOSOFTC(pic);
125 KASSERT(irqbase == 0 && (irq_mask & sc->sc_enabled_mask) == 0);
126 sc->sc_enabled_mask |= irq_mask;
127 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
128 /*
129 * If this is a level source, ack it now. If it's still asserted
130 * it'll come back.
131 */
132 if (irq_mask & sc->sc_level_mask)
133 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
134 irq_mask & sc->sc_level_mask);
135 }
136
137 static void
138 geminiicu_block_irqs(struct pic_softc *pic, size_t irqbase, uint32_t irq_mask)
139 {
140 struct geminiicu_softc * const sc = PICTOSOFTC(pic);
141 KASSERT(irqbase == 0);
142
143 sc->sc_enabled_mask &= ~irq_mask;
144 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
145 /*
146 * If any of the source are edge triggered, ack them now so
147 * we won't lose them.
148 */
149 if (irq_mask & sc->sc_edge_mask)
150 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR,
151 irq_mask & sc->sc_edge_mask);
152 }
153
154 /*
155 * Called with interrupts disabled
156 */
157 static int
158 find_pending_irqs(struct geminiicu_softc *sc)
159 {
160 uint32_t pending = INTC_READ(sc, GEMINI_ICU_IRQ_STATUS);
161
162 KASSERT((sc->sc_enabled_mask & pending) == pending);
163
164 if (pending == 0)
165 return 0;
166
167 return pic_mark_pending_sources(&sc->sc_pic, 0, pending);
168 }
169
170 void
171 gemini_irq_handler(void *frame)
172 {
173 struct cpu_info * const ci = curcpu();
174 struct geminiicu_softc * const sc = &geminiicu_softc;
175 const int oldipl = ci->ci_cpl;
176 const uint32_t oldipl_mask = __BIT(oldipl);
177 int ipl_mask = 0;
178
179 ci->ci_data.cpu_nintr++;
180
181 KASSERT(sc->sc_enabled_mask != 0);
182
183 ipl_mask = find_pending_irqs(sc);
184
185 /*
186 * Record the pending_ipls and deliver them if we can.
187 */
188 if ((ipl_mask & ~oldipl_mask) > oldipl_mask)
189 pic_do_pending_ints(I32_bit, oldipl, frame);
190 }
191
192 void
193 geminiicu_establish_irq(struct pic_softc *pic, struct intrsource *is)
194 {
195 struct geminiicu_softc * const sc = PICTOSOFTC(pic);
196 const uint32_t irq_mask = __BIT(is->is_irq);
197
198 KASSERT(is->is_irq < 32);
199
200 sc->sc_enabled_mask &= ~irq_mask;
201 /* Have to do with this interrupt disabled. */
202 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, sc->sc_enabled_mask);
203 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, irq_mask);
204
205 sc->sc_edge_rising_mask &= ~irq_mask;
206 sc->sc_edge_falling_mask &= ~irq_mask;
207 sc->sc_level_lo_mask &= ~irq_mask;
208 sc->sc_level_hi_mask &= ~irq_mask;
209
210 switch (is->is_type) {
211 case IST_LEVEL_LOW: sc->sc_level_lo_mask |= irq_mask; break;
212 case IST_LEVEL_HIGH: sc->sc_level_hi_mask |= irq_mask; break;
213 case IST_EDGE_FALLING: sc->sc_edge_falling_mask |= irq_mask; break;
214 case IST_EDGE_RISING: sc->sc_edge_rising_mask |= irq_mask; break;
215 }
216
217 sc->sc_edge_mask = sc->sc_edge_rising_mask | sc->sc_edge_falling_mask;
218 sc->sc_level_mask = sc->sc_level_hi_mask|sc->sc_level_lo_mask;
219
220 /*
221 * Set the new interrupt mode.
222 */
223 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, sc->sc_edge_mask);
224 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL,
225 sc->sc_level_lo_mask | sc->sc_edge_falling_mask);
226 }
227
228 int
229 geminiicu_match(device_t parent, cfdata_t cf, void *aux)
230 {
231 struct obio_attach_args * const oa = aux;
232
233 #if defined(SL3516)
234 if ((oa->obio_addr == GEMINI_IC0_BASE)
235 || (oa->obio_addr == GEMINI_IC1_BASE))
236 return 1;
237
238 return 0;
239 #else
240 #error unsupported GEMINI variant
241 #endif
242 }
243
244 void
245 geminiicu_attach(device_t parent, device_t self, void *aux)
246 {
247 struct obio_attach_args * const oa = aux;
248 struct geminiicu_softc * const sc = &geminiicu_softc;
249 int error;
250
251 aprint_normal("\n");
252
253 sc->sc_memt = oa->obio_iot;
254
255 error = bus_space_map(sc->sc_memt, oa->obio_addr, 0x1000, 0,
256 &sc->sc_memh);
257 if (error)
258 panic("failed to map interrupt registers: %d", error);
259
260 INTC_WRITE(sc, GEMINI_ICU_IRQ_ENABLE, 0);
261 INTC_WRITE(sc, GEMINI_ICU_IRQ_CLEAR, 0xffffffff);
262 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGMODE, 0);
263 INTC_WRITE(sc, GEMINI_ICU_IRQ_TRIGLEVEL, 0xffffffff);
264
265 sc->sc_dev = self;
266 self->dv_private = sc;
267
268 pic_add(&sc->sc_pic, 0);
269 }
270
271 CFATTACH_DECL_NEW(geminiicu,
272 0,
273 geminiicu_match, geminiicu_attach,
274 NULL, NULL);
275