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