1 /* $NetBSD: ep93xx_intr.c,v 1.12 2008/04/27 18:58:44 matt Exp $ */
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * This code is derived from software contributed to The NetBSD Foundation
11 * by Ichiro FUKUHARA and Naoto Shimazaki.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
22 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
23 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
24 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
25 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
26 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
28 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
29 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
30 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
31 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
35 #include <sys/cdefs.h>
36 __KERNEL_RCSID(0, "$NetBSD: ep93xx_intr.c,v 1.12 2008/04/27 18:58:44 matt Exp $");
39 * Interrupt support for the Cirrus Logic EP93XX
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/malloc.h>
45 #include <sys/termios.h>
47 #include <uvm/uvm_extern.h>
49 #include <machine/bus.h>
50 #include <machine/intr.h>
52 #include <arm/cpufunc.h>
54 #include <arm/ep93xx/ep93xxreg.h>
55 #include <arm/ep93xx/ep93xxvar.h>
57 /* Interrupt handler queues. */
58 struct intrq intrq
[NIRQ
];
60 /* Interrupts to mask at each level. */
61 static u_int32_t vic1_imask
[NIPL
];
62 static u_int32_t vic2_imask
[NIPL
];
64 /* Current interrupt priority level. */
65 volatile int hardware_spl_level
;
67 /* Software copy of the IRQs we have enabled. */
68 volatile u_int32_t vic1_intr_enabled
;
69 volatile u_int32_t vic2_intr_enabled
;
71 /* Interrupts pending. */
72 static volatile int ipending
;
74 void ep93xx_intr_dispatch(struct irqframe
*frame
);
76 #define VIC1REG(reg) *((volatile u_int32_t*) (EP93XX_AHB_VBASE + \
77 EP93XX_AHB_VIC1 + (reg)))
78 #define VIC2REG(reg) *((volatile u_int32_t*) (EP93XX_AHB_VBASE + \
79 EP93XX_AHB_VIC2 + (reg)))
82 ep93xx_set_intrmask(u_int32_t vic1_irqs
, u_int32_t vic2_irqs
)
84 VIC1REG(EP93XX_VIC_IntEnClear
) = vic1_irqs
;
85 VIC1REG(EP93XX_VIC_IntEnable
) = vic1_intr_enabled
& ~vic1_irqs
;
86 VIC2REG(EP93XX_VIC_IntEnClear
) = vic2_irqs
;
87 VIC2REG(EP93XX_VIC_IntEnable
) = vic2_intr_enabled
& ~vic2_irqs
;
91 ep93xx_enable_irq(int irq
)
94 vic1_intr_enabled
|= (1U << irq
);
95 VIC1REG(EP93XX_VIC_IntEnable
) = (1U << irq
);
97 vic2_intr_enabled
|= (1U << (irq
- VIC_NIRQ
));
98 VIC2REG(EP93XX_VIC_IntEnable
) = (1U << (irq
- VIC_NIRQ
));
103 ep93xx_disable_irq(int irq
)
105 if (irq
< VIC_NIRQ
) {
106 vic1_intr_enabled
&= ~(1U << irq
);
107 VIC1REG(EP93XX_VIC_IntEnClear
) = (1U << irq
);
109 vic2_intr_enabled
&= ~(1U << (irq
- VIC_NIRQ
));
110 VIC2REG(EP93XX_VIC_IntEnClear
) = (1U << (irq
- VIC_NIRQ
));
115 * NOTE: This routine must be called with interrupts disabled in the CPSR.
118 ep93xx_intr_calculate_masks(void)
124 /* First, figure out which IPLs each IRQ has. */
125 for (irq
= 0; irq
< NIRQ
; irq
++) {
128 ep93xx_disable_irq(irq
);
129 for (ih
= TAILQ_FIRST(&iq
->iq_list
); ih
!= NULL
;
130 ih
= TAILQ_NEXT(ih
, ih_list
))
131 levels
|= (1U << ih
->ih_ipl
);
132 iq
->iq_levels
= levels
;
135 /* Next, figure out which IRQs are used by each IPL. */
136 for (ipl
= 0; ipl
< NIPL
; ipl
++) {
139 for (irq
= 0; irq
< VIC_NIRQ
; irq
++) {
140 if (intrq
[irq
].iq_levels
& (1U << ipl
))
141 vic1_irqs
|= (1U << irq
);
143 vic1_imask
[ipl
] = vic1_irqs
;
144 for (irq
= 0; irq
< VIC_NIRQ
; irq
++) {
145 if (intrq
[irq
+ VIC_NIRQ
].iq_levels
& (1U << ipl
))
146 vic2_irqs
|= (1U << irq
);
148 vic2_imask
[ipl
] = vic2_irqs
;
151 KASSERT(vic1_imask
[IPL_NONE
] == 0);
152 KASSERT(vic2_imask
[IPL_NONE
] == 0);
155 * splclock() must block anything that uses the scheduler.
157 vic1_imask
[IPL_SCHED
] |= vic1_imask
[IPL_VM
];
158 vic2_imask
[IPL_SCHED
] |= vic2_imask
[IPL_VM
];
161 * splhigh() must block "everything".
163 vic1_imask
[IPL_HIGH
] |= vic1_imask
[IPL_SCHED
];
164 vic2_imask
[IPL_HIGH
] |= vic2_imask
[IPL_SCHED
];
167 * Now compute which IRQs must be blocked when servicing any
170 for (irq
= 0; irq
< NIRQ
; irq
++) {
174 if (irq
< VIC_NIRQ
) {
175 vic1_irqs
= (1U << irq
);
179 vic2_irqs
= (1U << (irq
- VIC_NIRQ
));
182 if (TAILQ_FIRST(&iq
->iq_list
) != NULL
)
183 ep93xx_enable_irq(irq
);
184 for (ih
= TAILQ_FIRST(&iq
->iq_list
); ih
!= NULL
;
185 ih
= TAILQ_NEXT(ih
, ih_list
)) {
186 vic1_irqs
|= vic1_imask
[ih
->ih_ipl
];
187 vic2_irqs
|= vic2_imask
[ih
->ih_ipl
];
189 iq
->iq_vic1_mask
= vic1_irqs
;
190 iq
->iq_vic2_mask
= vic2_irqs
;
200 oldirqstate
= disable_interrupts(I32_bit
);
203 if (new != hardware_spl_level
) {
204 hardware_spl_level
= new;
205 ep93xx_set_intrmask(vic1_imask
[new], vic2_imask
[new]);
207 restore_interrupts(oldirqstate
);
209 #ifdef __HAVE_FAST_SOFTINTS
220 oldirqstate
= disable_interrupts(I32_bit
);
223 restore_interrupts(oldirqstate
);
241 * Initialize the rest of the interrupt subsystem, making it
242 * ready to handle interrupts from devices.
245 ep93xx_intr_init(void)
250 vic1_intr_enabled
= 0;
251 vic2_intr_enabled
= 0;
253 for (i
= 0; i
< NIRQ
; i
++) {
255 TAILQ_INIT(&iq
->iq_list
);
257 sprintf(iq
->iq_name
, "irq %d", i
);
258 evcnt_attach_dynamic(&iq
->iq_ev
, EVCNT_TYPE_INTR
,
259 NULL
, (i
< VIC_NIRQ
? "vic1" : "vic2"),
262 curcpu()->ci_intr_depth
= 0;
264 hardware_spl_level
= 0;
266 /* All interrupts should use IRQ not FIQ */
267 VIC1REG(EP93XX_VIC_IntSelect
) = 0;
268 VIC2REG(EP93XX_VIC_IntSelect
) = 0;
270 ep93xx_intr_calculate_masks();
272 /* Enable IRQs (don't yet use FIQs). */
273 enable_interrupts(I32_bit
);
277 ep93xx_intr_establish(int irq
, int ipl
, int (*ih_func
)(void *), void *arg
)
283 if (irq
< 0 || irq
> NIRQ
)
284 panic("ep93xx_intr_establish: IRQ %d out of range", irq
);
285 if (ipl
< 0 || ipl
> NIPL
)
286 panic("ep93xx_intr_establish: IPL %d out of range", ipl
);
288 ih
= malloc(sizeof(*ih
), M_DEVBUF
, M_NOWAIT
);
292 ih
->ih_func
= ih_func
;
299 oldirqstate
= disable_interrupts(I32_bit
);
300 TAILQ_INSERT_TAIL(&iq
->iq_list
, ih
, ih_list
);
301 ep93xx_intr_calculate_masks();
302 restore_interrupts(oldirqstate
);
308 ep93xx_intr_disestablish(void *cookie
)
310 struct intrhand
* ih
= cookie
;
311 struct intrq
* iq
= &intrq
[ih
->ih_irq
];
314 oldirqstate
= disable_interrupts(I32_bit
);
315 TAILQ_REMOVE(&iq
->iq_list
, ih
, ih_list
);
316 ep93xx_intr_calculate_masks();
317 restore_interrupts(oldirqstate
);
321 ep93xx_intr_dispatch(struct irqframe
*frame
)
327 u_int32_t vic1_hwpend
;
328 u_int32_t vic2_hwpend
;
333 vic1_hwpend
= VIC1REG(EP93XX_VIC_IRQStatus
);
334 vic2_hwpend
= VIC2REG(EP93XX_VIC_IRQStatus
);
336 hardware_spl_level
= pcpl
;
337 ep93xx_set_intrmask(vic1_imask
[pcpl
] | vic1_hwpend
,
338 vic2_imask
[pcpl
] | vic2_hwpend
);
340 vic1_hwpend
&= ~vic1_imask
[pcpl
];
341 vic2_hwpend
&= ~vic2_imask
[pcpl
];
344 irq
= ffs(vic1_hwpend
) - 1;
347 iq
->iq_ev
.ev_count
++;
349 TAILQ_FOREACH(ih
, &iq
->iq_list
, ih_list
) {
350 set_curcpl(ih
->ih_ipl
);
351 oldirqstate
= enable_interrupts(I32_bit
);
352 (void) (*ih
->ih_func
)(ih
->ih_arg
? ih
->ih_arg
: frame
);
353 restore_interrupts(oldirqstate
);
355 } else if (vic2_hwpend
) {
356 irq
= ffs(vic2_hwpend
) - 1;
358 iq
= &intrq
[irq
+ VIC_NIRQ
];
359 iq
->iq_ev
.ev_count
++;
361 TAILQ_FOREACH(ih
, &iq
->iq_list
, ih_list
) {
362 set_curcpl(ih
->ih_ipl
);
363 oldirqstate
= enable_interrupts(I32_bit
);
364 (void) (*ih
->ih_func
)(ih
->ih_arg
? ih
->ih_arg
: frame
);
365 restore_interrupts(oldirqstate
);
370 hardware_spl_level
= pcpl
;
371 ep93xx_set_intrmask(vic1_imask
[pcpl
], vic2_imask
[pcpl
]);
373 #ifdef __HAVE_FAST_SOFTINTS