4 * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
7 * Written by Jason R. Thorpe for Wasabi Systems, Inc.
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
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
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.
38 #include <sys/cdefs.h>
39 __KERNEL_RCSID(0, "$NetBSD$");
41 #ifndef EVBARM_SPL_NOINLINE
42 #define EVBARM_SPL_NOINLINE
46 * Interrupt support for the Intel IQ80310.
49 #include <sys/param.h>
50 #include <sys/systm.h>
51 #include <sys/malloc.h>
53 #include <uvm/uvm_extern.h>
55 #include <machine/bus.h>
56 #include <machine/intr.h>
58 #include <arm/cpufunc.h>
60 #include <arm/xscale/i80200reg.h>
61 #include <arm/xscale/i80200var.h>
63 #include <evbarm/iq80310/iq80310reg.h>
64 #include <evbarm/iq80310/iq80310var.h>
65 #include <evbarm/iq80310/obiovar.h>
67 /* Interrupt handler queues. */
68 struct intrq intrq
[NIRQ
];
70 /* Interrupts to mask at each level. */
71 int iq80310_imask
[NIPL
];
73 /* Interrupts pending. */
74 volatile int iq80310_ipending
;
76 /* Software copy of the IRQs we have enabled. */
77 uint32_t intr_enabled
;
79 #ifdef __HAVE_FAST_SOFTINTRS
81 * Map a software interrupt queue index (at the top of the word, and
82 * highest priority softintr is encountered first in an ffs()).
84 #define SI_TO_IRQBIT(si) (1U << (31 - (si)))
87 * Map a software interrupt queue to an interrupt priority level.
89 static const int si_to_ipl
[SI_NQUEUES
] = {
90 IPL_SOFT
, /* SI_SOFT */
91 IPL_SOFTCLOCK
, /* SI_SOFTCLOCK */
92 IPL_SOFTNET
, /* SI_SOFTNET */
93 IPL_SOFTSERIAL
, /* SI_SOFTSERIAL */
97 void iq80310_intr_dispatch(struct irqframe
*frame
);
99 static inline uint32_t
100 iq80310_intstat_read(void)
104 intstat
= CPLD_READ(IQ80310_XINT3_STATUS
) & 0x1f;
105 #if defined(IRQ_READ_XINT0)
107 intstat
|= (CPLD_READ(IQ80310_XINT0_STATUS
) & 0x7) << 5;
110 /* XXX Why do we have to mask off? */
111 return (intstat
& intr_enabled
);
115 iq80310_set_intrmask(void)
119 intr_enabled
|= IRQ_BITS_ALWAYS_ON
;
121 /* The XINT_MASK register sets a bit to *disable*. */
122 disabled
= (~intr_enabled
) & IRQ_BITS
;
124 CPLD_WRITE(IQ80310_XINT_MASK
, disabled
& 0x1f);
128 iq80310_enable_irq(int irq
)
131 intr_enabled
|= (1U << irq
);
132 iq80310_set_intrmask();
136 iq80310_disable_irq(int irq
)
139 intr_enabled
&= ~(1U << irq
);
140 iq80310_set_intrmask();
144 * NOTE: This routine must be called with interrupts disabled in the CPSR.
147 iq80310_intr_calculate_masks(void)
153 /* First, figure out which IPLs each IRQ has. */
154 for (irq
= 0; irq
< NIRQ
; irq
++) {
157 iq80310_disable_irq(irq
);
158 for (ih
= TAILQ_FIRST(&iq
->iq_list
); ih
!= NULL
;
159 ih
= TAILQ_NEXT(ih
, ih_list
))
160 levels
|= (1U << ih
->ih_ipl
);
161 iq
->iq_levels
= levels
;
164 /* Next, figure out which IRQs are used by each IPL. */
165 for (ipl
= 0; ipl
< NIPL
; ipl
++) {
167 for (irq
= 0; irq
< NIRQ
; irq
++) {
168 if (intrq
[irq
].iq_levels
& (1U << ipl
))
171 iq80310_imask
[ipl
] = irqs
;
174 iq80310_imask
[IPL_NONE
] = 0;
175 iq80310_imask
[IPL_SOFTCLOCK
] = 0;
176 iq80310_imask
[IPL_SOFTNET
] = 0;
177 iq80310_imask
[IPL_SOFTSERIAL
] = 0;
180 * splsoftnet() must also block splsoftclock(), since we don't
181 * want timer-driven network events to occur while we're
182 * processing incoming packets.
184 iq80310_imask
[IPL_SOFTNET
] |= iq80310_imask
[IPL_SOFTCLOCK
];
187 * Enforce a hierarchy that gives "slow" device (or devices with
188 * limited input buffer space/"real-time" requirements) a better
189 * chance at not dropping data.
191 iq80310_imask
[IPL_BIO
] |= iq80310_imask
[IPL_SOFTNET
];
192 iq80310_imask
[IPL_NET
] |= iq80310_imask
[IPL_BIO
];
193 iq80310_imask
[IPL_SOFTSERIAL
] |= iq80310_imask
[IPL_NET
];
194 iq80310_imask
[IPL_TTY
] |= iq80310_imask
[IPL_SOFTSERIAL
];
197 * splvm() blocks all interrupts that use the kernel memory
198 * allocation facilities.
200 iq80310_imask
[IPL_VM
] |= iq80310_imask
[IPL_TTY
];
203 * Audio devices are not allowed to perform memory allocation
204 * in their interrupt routines, and they have fairly "real-time"
205 * requirements, so give them a high interrupt priority.
207 iq80310_imask
[IPL_AUDIO
] |= iq80310_imask
[IPL_VM
];
210 * splclock() must block anything that uses the scheduler.
212 iq80310_imask
[IPL_CLOCK
] |= iq80310_imask
[IPL_AUDIO
];
215 * No separate statclock on the IQ80310.
218 iq80310_imask
[IPL_STATCLOCK
] |= iq80310_imask
[IPL_CLOCK
];
222 * splhigh() must block "everything".
225 iq80310_imask
[IPL_HIGH
] |= iq80310_imask
[IPL_STATCLOCK
];
227 iq80310_imask
[IPL_HIGH
] |= iq80310_imask
[IPL_CLOCK
];
231 * XXX We need serial drivers to run at the absolute highest priority
232 * in order to avoid overruns, so serial > high.
234 iq80310_imask
[IPL_SERIAL
] |= iq80310_imask
[IPL_HIGH
];
237 * Now compute which IRQs must be blocked when servicing any
240 for (irq
= 0; irq
< NIRQ
; irq
++) {
241 int irqs
= (1U << irq
);
243 if (TAILQ_FIRST(&iq
->iq_list
) != NULL
)
244 iq80310_enable_irq(irq
);
245 for (ih
= TAILQ_FIRST(&iq
->iq_list
); ih
!= NULL
;
246 ih
= TAILQ_NEXT(ih
, ih_list
))
247 irqs
|= iq80310_imask
[ih
->ih_ipl
];
252 #ifdef __HAVE_FAST_SOFTINTRS
254 iq80310_do_soft(void)
256 static __cpu_simple_lock_t processing
= __SIMPLELOCK_UNLOCKED
;
257 struct cpu_info
* const ci
= curcpu();
258 int new, oldirqstate
;
260 if (__cpu_simple_lock_try(&processing
) == 0)
265 oldirqstate
= disable_interrupts(I32_bit
);
267 #define DO_SOFTINT(si) \
268 if ((iq80310_ipending & ~new) & SI_TO_IRQBIT(si)) { \
269 iq80310_ipending &= ~SI_TO_IRQBIT(si); \
270 ci->ci_cpl |= iq80310_imask[si_to_ipl[(si)]]; \
271 restore_interrupts(oldirqstate); \
272 softintr_dispatch(si); \
273 oldirqstate = disable_interrupts(I32_bit); \
277 DO_SOFTINT(SI_SOFTSERIAL
);
278 DO_SOFTINT(SI_SOFTNET
);
279 DO_SOFTINT(SI_SOFTCLOCK
);
282 __cpu_simple_unlock(&processing
);
284 restore_interrupts(oldirqstate
);
286 #endif /* __HAVE_SOFT_FASTINTRS */
292 return (iq80310_splraise(ipl
));
299 return (iq80310_splx(new));
306 return (iq80310_spllower(ipl
));
309 #ifdef __HAVE_FAST_SOFTINTRS
315 oldirqstate
= disable_interrupts(I32_bit
);
316 iq80310_ipending
|= SI_TO_IRQBIT(si
);
317 restore_interrupts(oldirqstate
);
319 /* Process unmasked pending soft interrupts. */
320 if ((iq80310_ipending
& ~IRQ_BITS
) & ~curcpl())
326 iq80310_intr_init(void)
332 * The Secondary PCI interrupts INTA, INTB, and INTC
333 * area always enabled, since they cannot be masked
336 intr_enabled
|= IRQ_BITS_ALWAYS_ON
;
338 for (i
= 0; i
< NIRQ
; i
++) {
340 TAILQ_INIT(&iq
->iq_list
);
342 sprintf(iq
->iq_name
, "irq %d", i
);
343 evcnt_attach_dynamic(&iq
->iq_ev
, EVCNT_TYPE_INTR
,
344 NULL
, "iq80310", iq
->iq_name
);
347 iq80310_intr_calculate_masks();
349 /* Enable external interrupts on the i80200. */
350 i80200_extirq_dispatch
= iq80310_intr_dispatch
;
351 i80200_intr_enable(INTCTL_IM
| INTCTL_PM
);
353 /* Enable IRQs (don't yet use FIQs). */
354 enable_interrupts(I32_bit
);
358 iq80310_intr_establish(int irq
, int ipl
, int (*func
)(void *), void *arg
)
364 if (irq
< 0 || irq
> NIRQ
)
365 panic("iq80310_intr_establish: IRQ %d out of range", irq
);
367 ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
);
378 /* All IQ80310 interrupts are level-triggered. */
379 iq
->iq_ist
= IST_LEVEL
;
381 oldirqstate
= disable_interrupts(I32_bit
);
383 TAILQ_INSERT_TAIL(&iq
->iq_list
, ih
, ih_list
);
385 iq80310_intr_calculate_masks();
387 restore_interrupts(oldirqstate
);
393 iq80310_intr_disestablish(void *cookie
)
395 struct intrhand
*ih
= cookie
;
396 struct intrq
*iq
= &intrq
[ih
->ih_irq
];
399 oldirqstate
= disable_interrupts(I32_bit
);
401 TAILQ_REMOVE(&iq
->iq_list
, ih
, ih_list
);
403 iq80310_intr_calculate_masks();
405 restore_interrupts(oldirqstate
);
409 iq80310_intr_dispatch(struct irqframe
*frame
)
413 int oldirqstate
, pcpl
, irq
, ibit
, hwpend
, rv
, stray
;
414 struct cpu_info
* const ci
= curcpu();
418 /* First, disable external IRQs. */
419 i80200_intr_disable(INTCTL_IM
| INTCTL_PM
);
423 for (hwpend
= iq80310_intstat_read(); hwpend
!= 0;) {
424 irq
= ffs(hwpend
) - 1;
433 * IRQ is masked; mark it as pending and check
434 * the next one. Note: external IRQs are already
437 iq80310_ipending
|= ibit
;
441 iq80310_ipending
&= ~ibit
;
445 iq
->iq_ev
.ev_count
++;
447 ci
->ci_cpl
|= iq
->iq_mask
;
448 oldirqstate
= enable_interrupts(I32_bit
);
449 for (ih
= TAILQ_FIRST(&iq
->iq_list
); ih
!= NULL
;
450 ih
= TAILQ_NEXT(ih
, ih_list
)) {
451 rv
|= (*ih
->ih_func
)(ih
->ih_arg
? ih
->ih_arg
: frame
);
453 restore_interrupts(oldirqstate
);
459 printf("Stray interrupt: IRQ %d\n", irq
);
465 printf("Stray external interrupt\n");
468 #ifdef __HAVE_FAST_SOFTINTS
469 /* Check for pendings soft intrs. */
470 if ((iq80310_ipending
& ~IRQ_BITS
) & ~ci
->ci_cpl
) {
471 oldirqstate
= enable_interrupts(I32_bit
);
473 restore_interrupts(oldirqstate
);
478 * If no hardware interrupts are masked, re-enable external
481 if ((iq80310_ipending
& IRQ_BITS
) == 0)
482 i80200_intr_enable(INTCTL_IM
| INTCTL_PM
);