ifpga_intr.c revision 1.6 1 /* $NetBSD: ifpga_intr.c,v 1.6 2008/01/05 12:40:34 ad Exp $ */
2
3 /*
4 * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed for the NetBSD Project by
20 * Wasabi Systems, Inc.
21 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
22 * or promote products derived from this software without specific prior
23 * written permission.
24 *
25 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
27 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
28 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
29 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
30 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
31 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
32 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
33 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35 * POSSIBILITY OF SUCH DAMAGE.
36 */
37
38 #ifndef EVBARM_SPL_NOINLINE
39 #define EVBARM_SPL_NOINLINE
40 #endif
41
42 /*
43 * Interrupt support for the Integrator FPGA.
44 */
45
46 #include <sys/param.h>
47 #include <sys/systm.h>
48 #include <sys/malloc.h>
49 #include <sys/bus.h>
50 #include <sys/intr.h>
51
52 #include <uvm/uvm_extern.h>
53
54 #include <arm/cpufunc.h>
55
56 #include <evbarm/ifpga/ifpgareg.h>
57 #include <evbarm/ifpga/ifpgavar.h>
58
59 /* Interrupt handler queues. */
60 struct intrq intrq[NIRQ];
61
62 /* Interrupts to mask at each level. */
63 int ifpga_imask[NIPL];
64
65 /* Current interrupt priority level. */
66 volatile int current_spl_level;
67
68 /* Interrupts pending. */
69 volatile int ifpga_ipending;
70
71 /* Software copy of the IRQs we have enabled. */
72 volatile uint32_t intr_enabled;
73
74 /* Mask if interrupts steered to FIQs. */
75 uint32_t intr_steer;
76
77 /*
78 * Map a software interrupt queue index (to the unused bits in the
79 * ICU registers -- XXX will need to revisit this if those bits are
80 * ever used in future steppings).
81 */
82 static const uint32_t si_to_irqbit[SI_NQUEUES] = {
83 IFPGA_INTR_bit31, /* SI_SOFTCLOCK */
84 IFPGA_INTR_bit30, /* SI_SOFTBIO */
85 IFPGA_INTR_bit29, /* SI_SOFTNET */
86 IFPGA_INTR_bit28, /* SI_SOFTSERIAL */
87 };
88
89 #define SI_TO_IRQBIT(si) (si_to_irqbit[(si)])
90
91 /*
92 * Map a software interrupt queue to an interrupt priority level.
93 */
94 static const int si_to_ipl[SI_NQUEUES] = {
95 IPL_SOFTCLOCK, /* SI_SOFTCLOCK */
96 IPL_SOFTBIO, /* SI_SOFTBIO */
97 IPL_SOFTNET, /* SI_SOFTNET */
98 IPL_SOFTSERIAL, /* SI_SOFTSERIAL */
99 };
100
101 /*
102 * Interrupt bit names.
103 */
104 const char *ifpga_irqnames[] = {
105 "soft", /* 0 */
106 "uart 0", /* 1 */
107 "uart 1", /* 2 */
108 "kbd", /* 3 */
109 "mouse", /* 4 */
110 "tmr 0", /* 5 */
111 "tmr 1 hard", /* 6 */
112 "tmr 2 stat", /* 7 */
113 "rtc", /* 8 */
114 "exp 0", /* 9 */
115 "exp 1", /* 10 */
116 "exp 2", /* 11 */
117 "exp 3", /* 12 */
118 "pci 0", /* 13 */
119 "pci 1", /* 14 */
120 "pci 2", /* 15 */
121 "pci 3", /* 16 */
122 "V3 br", /* 17 */
123 "deg", /* 18 */
124 "enum", /* 19 */
125 "pci lb", /* 20 */
126 "autoPC", /* 21 */
127 "irq 22", /* 22 */
128 "irq 23", /* 23 */
129 "irq 24", /* 24 */
130 "irq 25", /* 25 */
131 "irq 26", /* 26 */
132 "irq 27", /* 27 */
133 "irq 28", /* 28 */
134 "irq 29", /* 29 */
135 "irq 30", /* 30 */
136 "irq 31", /* 31 */
137 };
138
139 void ifpga_intr_dispatch(struct clockframe *frame);
140
141 extern struct ifpga_softc *ifpga_sc;
142
143 static inline uint32_t
144 ifpga_iintsrc_read(void)
145 {
146 return bus_space_read_4(ifpga_sc->sc_iot, ifpga_sc->sc_irq_ioh,
147 IFPGA_INTR_STATUS);
148 }
149
150 static inline void
151 ifpga_enable_irq(int irq)
152 {
153
154 intr_enabled |= (1U << irq);
155 ifpga_set_intrmask();
156 }
157
158 static inline void
159 ifpga_disable_irq(int irq)
160 {
161
162 intr_enabled &= ~(1U << irq);
163 ifpga_set_intrmask();
164 }
165
166 /*
167 * NOTE: This routine must be called with interrupts disabled in the CPSR.
168 */
169 static void
170 ifpga_intr_calculate_masks(void)
171 {
172 struct intrq *iq;
173 struct intrhand *ih;
174 int irq, ipl;
175
176 /* First, figure out which IPLs each IRQ has. */
177 for (irq = 0; irq < NIRQ; irq++) {
178 int levels = 0;
179 iq = &intrq[irq];
180 ifpga_disable_irq(irq);
181 for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
182 ih = TAILQ_NEXT(ih, ih_list))
183 levels |= (1U << ih->ih_ipl);
184 iq->iq_levels = levels;
185 }
186
187 /* Next, figure out which IRQs are used by each IPL. */
188 for (ipl = 0; ipl < NIPL; ipl++) {
189 int irqs = 0;
190 for (irq = 0; irq < NIRQ; irq++) {
191 if (intrq[irq].iq_levels & (1U << ipl))
192 irqs |= (1U << irq);
193 }
194 ifpga_imask[ipl] = irqs;
195 }
196
197 ifpga_imask[IPL_NONE] = 0;
198
199 /*
200 * Initialize the soft interrupt masks to block themselves.
201 */
202 ifpga_imask[IPL_SOFTCLOCK] = SI_TO_IRQBIT(SI_SOFTCLOCK);
203 ifpga_imask[IPL_SOFTBIO] = SI_TO_IRQBIT(SI_SOFTBIO);
204 ifpga_imask[IPL_SOFTNET] = SI_TO_IRQBIT(SI_SOFTNET);
205 ifpga_imask[IPL_SOFTSERIAL] = SI_TO_IRQBIT(SI_SOFTSERIAL);
206
207 /*
208 * Enforce a hierarchy that gives "slow" device (or devices with
209 * limited input buffer space/"real-time" requirements) a better
210 * chance at not dropping data.
211 */
212 ifpga_imask[IPL_SOFTBIO] |= ifpga_imask[IPL_SOFTCLOCK];
213 ifpga_imask[IPL_SOFTNET] |= ifpga_imask[IPL_SOFTBIO];
214 ifpga_imask[IPL_SOFTSERIAL] |= ifpga_imask[IPL_SOFTNET];
215 ifpga_imask[IPL_VM] |= ifpga_imask[IPL_SOFTSERIAL];
216 ifpga_imask[IPL_SCHED] |= ifpga_imask[IPL_VM];
217 ifpga_imask[IPL_HIGH] |= ifpga_imask[IPL_SCHED];
218
219 /*
220 * Now compute which IRQs must be blocked when servicing any
221 * given IRQ.
222 */
223 for (irq = 0; irq < NIRQ; irq++) {
224 int irqs = (1U << irq);
225 iq = &intrq[irq];
226 if (TAILQ_FIRST(&iq->iq_list) != NULL)
227 ifpga_enable_irq(irq);
228 for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
229 ih = TAILQ_NEXT(ih, ih_list))
230 irqs |= ifpga_imask[ih->ih_ipl];
231 iq->iq_mask = irqs;
232 }
233 }
234
235 void
236 ifpga_do_pending(void)
237 {
238 #ifdef __HAVE_FAST_SOFTINTS
239 static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED;
240 int new, oldirqstate;
241
242 if (__cpu_simple_lock_try(&processing) == 0)
243 return;
244
245 new = current_spl_level;
246
247 oldirqstate = disable_interrupts(I32_bit);
248
249 #define DO_SOFTINT(si) \
250 if ((ifpga_ipending & ~new) & SI_TO_IRQBIT(si)) { \
251 ifpga_ipending &= ~SI_TO_IRQBIT(si); \
252 current_spl_level |= ifpga_imask[si_to_ipl[(si)]]; \
253 restore_interrupts(oldirqstate); \
254 softintr_dispatch(si); \
255 oldirqstate = disable_interrupts(I32_bit); \
256 current_spl_level = new; \
257 }
258
259 DO_SOFTINT(SI_SOFTSERIAL);
260 DO_SOFTINT(SI_SOFTNET);
261 DO_SOFTINT(SI_SOFTBIO);
262 DO_SOFTINT(SI_SOFTCLOCK);
263
264 __cpu_simple_unlock(&processing);
265
266 restore_interrupts(oldirqstate);
267 #endif
268 }
269
270 void
271 splx(int new)
272 {
273
274 ifpga_splx(new);
275 }
276
277 int
278 _spllower(int ipl)
279 {
280
281 return (ifpga_spllower(ipl));
282 }
283
284 int
285 _splraise(int ipl)
286 {
287
288 return (ifpga_splraise(ipl));
289 }
290
291 void
292 _setsoftintr(int si)
293 {
294 int oldirqstate;
295
296 oldirqstate = disable_interrupts(I32_bit);
297 ifpga_ipending |= SI_TO_IRQBIT(si);
298 restore_interrupts(oldirqstate);
299
300 /* Process unmasked pending soft interrupts. */
301 if ((ifpga_ipending & INT_SWMASK) & ~current_spl_level)
302 ifpga_do_pending();
303 }
304
305 /*
306 * ifpga_intr_init:
307 *
308 * Initialize the rest of the interrupt subsystem, making it
309 * ready to handle interrupts from devices.
310 */
311 void
312 ifpga_intr_init(void)
313 {
314 struct intrq *iq;
315 int i;
316
317 intr_enabled = 0;
318
319 for (i = 0; i < NIRQ; i++) {
320 iq = &intrq[i];
321 TAILQ_INIT(&iq->iq_list);
322
323 evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
324 NULL, "ifpga", ifpga_irqnames[i]);
325 }
326 }
327
328 void
329 ifpga_intr_postinit(void)
330 {
331 ifpga_intr_calculate_masks();
332
333 /* Enable IRQs (don't yet use FIQs). */
334 enable_interrupts(I32_bit);
335 }
336
337 void *
338 ifpga_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
339 {
340 struct intrq *iq;
341 struct intrhand *ih;
342 u_int oldirqstate;
343
344 if (irq < 0 || irq > NIRQ)
345 panic("ifpga_intr_establish: IRQ %d out of range", irq);
346
347 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
348 if (ih == NULL)
349 return (NULL);
350
351 ih->ih_func = func;
352 ih->ih_arg = arg;
353 ih->ih_ipl = ipl;
354 ih->ih_irq = irq;
355
356 iq = &intrq[irq];
357
358 /* All IOP321 interrupts are level-triggered. */
359 iq->iq_ist = IST_LEVEL;
360
361 oldirqstate = disable_interrupts(I32_bit);
362
363 TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
364
365 ifpga_intr_calculate_masks();
366
367 restore_interrupts(oldirqstate);
368
369 return (ih);
370 }
371
372 void
373 ifpga_intr_disestablish(void *cookie)
374 {
375 struct intrhand *ih = cookie;
376 struct intrq *iq = &intrq[ih->ih_irq];
377 int oldirqstate;
378
379 oldirqstate = disable_interrupts(I32_bit);
380
381 TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
382
383 ifpga_intr_calculate_masks();
384
385 restore_interrupts(oldirqstate);
386 }
387
388 void
389 ifpga_intr_dispatch(struct clockframe *frame)
390 {
391 struct intrq *iq;
392 struct intrhand *ih;
393 int oldirqstate, pcpl, irq, ibit, hwpend;
394 struct cpu_info *ci;
395
396 ci = curcpu();
397 ci->ci_idepth++;
398
399 pcpl = current_spl_level;
400
401 hwpend = ifpga_iintsrc_read();
402
403 /*
404 * Disable all the interrupts that are pending. We will
405 * reenable them once they are processed and not masked.
406 */
407 intr_enabled &= ~hwpend;
408 ifpga_set_intrmask();
409
410 /* Wait for these interrupts to be suppressed. */
411 while ((ifpga_iintsrc_read() & hwpend) != 0)
412 ;
413
414 while (hwpend != 0) {
415 irq = ffs(hwpend) - 1;
416 ibit = (1U << irq);
417
418 hwpend &= ~ibit;
419
420 if (pcpl & ibit) {
421 /*
422 * IRQ is masked; mark it as pending and check
423 * the next one. Note: the IRQ is already disabled.
424 */
425 ifpga_ipending |= ibit;
426 continue;
427 }
428
429 ifpga_ipending &= ~ibit;
430
431 iq = &intrq[irq];
432 iq->iq_ev.ev_count++;
433 uvmexp.intrs++;
434 current_spl_level |= iq->iq_mask;
435 oldirqstate = enable_interrupts(I32_bit);
436 for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
437 ih = TAILQ_NEXT(ih, ih_list)) {
438 (void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
439 }
440 restore_interrupts(oldirqstate);
441 current_spl_level = pcpl;
442
443 hwpend |= (ifpga_ipending & IFPGA_INTR_HWMASK) & ~pcpl;
444
445 /* Re-enable this interrupt now that's it's cleared. */
446 intr_enabled |= ibit;
447 ifpga_set_intrmask();
448 }
449
450 ci->ci_idepth--;
451
452 /* Check for pendings soft intrs. */
453 if ((ifpga_ipending & INT_SWMASK) & ~current_spl_level) {
454 oldirqstate = enable_interrupts(I32_bit);
455 ifpga_do_pending();
456 restore_interrupts(oldirqstate);
457 }
458 }
459