1 /* $NetBSD: s3c2410_intr.c,v 1.10 2008/11/19 06:37:51 matt Exp $ */
4 * Copyright (c) 2003 Genetec corporation. All rights reserved.
5 * Written by Hiroyuki Bessho for Genetec corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. The name of Genetec corporation may not be used to endorse
16 * or promote products derived from this software without specific prior
19 * THIS SOFTWARE IS PROVIDED BY GENETEC CORP. ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GENETEC CORP.
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
33 * IRQ handler for Samsung S3C2410 processor.
34 * It has integrated interrupt controller.
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: s3c2410_intr.c,v 1.10 2008/11/19 06:37:51 matt Exp $");
40 #include <sys/param.h>
41 #include <sys/systm.h>
42 #include <sys/malloc.h>
43 #include <uvm/uvm_extern.h>
44 #include <machine/bus.h>
45 #include <machine/intr.h>
46 #include <arm/cpufunc.h>
48 #include <arm/s3c2xx0/s3c2410reg.h>
49 #include <arm/s3c2xx0/s3c2410var.h>
52 * interrupt dispatch table.
55 struct s3c2xx0_intr_dispatch handler
[ICU_LEN
];
58 volatile int intr_mask
;
59 #ifdef __HAVE_FAST_SOFTINTS
60 volatile int softint_pending
;
61 volatile int soft_intr_mask
;
63 volatile int global_intr_mask
= 0; /* mask some interrupts at all spl level */
65 /* interrupt masks for each level */
66 int s3c2xx0_imask
[NIPL
];
67 int s3c2xx0_ilevel
[ICU_LEN
];
68 #ifdef __HAVE_FAST_SOFTINTS
69 int s3c24x0_soft_imask
[NIPL
];
72 vaddr_t intctl_base
; /* interrupt controller registers */
73 #define icreg(offset) \
74 (*(volatile uint32_t *)(intctl_base+(offset)))
76 #ifdef __HAVE_FAST_SOFTINTS
78 * Map a software interrupt queue to an interrupt priority level.
80 static const int si_to_ipl
[] = {
81 [SI_SOFTBIO
] = IPL_SOFTBIO
,
82 [SI_SOFTCLOCK
] = IPL_SOFTCLOCK
,
83 [SI_SOFTNET
] = IPL_SOFTNET
,
84 [SI_SOFTSERIAL
] = IPL_SOFTSERIAL
,
88 #define PENDING_CLEAR_MASK (~0)
91 * called from irq_entry.
93 void s3c2410_irq_handler(struct clockframe
*);
95 s3c2410_irq_handler(struct clockframe
*frame
)
101 saved_spl_level
= curcpl();
104 if (curcpu()->ci_intr_depth
> 10)
105 panic("nested intr too deep");
108 while ((irqbits
= icreg(INTCTL_INTPND
)) != 0) {
110 /* Note: Only one bit in INTPND register is set */
112 irqno
= icreg(INTCTL_INTOFFSET
);
115 if (__predict_false((irqbits
& (1<<irqno
)) == 0)) {
116 /* This shouldn't happen */
117 printf("INTOFFSET=%d, INTPND=%x\n", irqno
, irqbits
);
121 /* raise spl to stop interrupts of lower priorities */
122 if (saved_spl_level
< handler
[irqno
].level
)
123 s3c2xx0_setipl(handler
[irqno
].level
);
125 /* clear pending bit */
126 icreg(INTCTL_SRCPND
) = PENDING_CLEAR_MASK
& (1 << irqno
);
127 icreg(INTCTL_INTPND
) = PENDING_CLEAR_MASK
& (1 << irqno
);
129 enable_interrupts(I32_bit
); /* allow nested interrupts */
131 (*handler
[irqno
].func
) (
132 handler
[irqno
].cookie
== 0
133 ? frame
: handler
[irqno
].cookie
);
135 disable_interrupts(I32_bit
);
137 /* restore spl to that was when this interrupt happen */
138 s3c2xx0_setipl(saved_spl_level
);
142 #ifdef __HAVE_FAST_SOFTINTS
148 * Handler for main IRQ of cascaded interrupts.
151 cascade_irq_handler(void *cookie
)
153 int index
= (int)cookie
- 1;
156 int save
= disable_interrupts(I32_bit
);
158 KASSERT(0 <= index
&& index
<= 3);
160 irqbits
= icreg(INTCTL_SUBSRCPND
) &
161 ~icreg(INTCTL_INTSUBMSK
) & (0x07 << (3*index
));
163 for (irqno
= 3*index
; irqbits
; ++irqno
) {
164 if ((irqbits
& (1<<irqno
)) == 0)
167 /* clear pending bit */
168 irqbits
&= ~(1<<irqno
);
169 icreg(INTCTL_SUBSRCPND
) = (1 << irqno
);
171 /* allow nested interrupts. SPL is already set
172 * correctly by main handler. */
173 restore_interrupts(save
);
175 i
= S3C2410_SUBIRQ_MIN
+ irqno
;
176 (* handler
[i
].func
)(handler
[i
].cookie
);
178 disable_interrupts(I32_bit
);
185 static const uint8_t subirq_to_main
[] = {
200 s3c24x0_intr_establish(int irqno
, int level
, int type
,
201 int (* func
) (void *), void *cookie
)
205 if (irqno
< 0 || irqno
>= ICU_LEN
||
206 type
< IST_NONE
|| IST_EDGE_BOTH
< type
)
207 panic("intr_establish: bogus irq or type");
209 save
= disable_interrupts(I32_bit
);
211 handler
[irqno
].cookie
= cookie
;
212 handler
[irqno
].func
= func
;
213 handler
[irqno
].level
= level
;
215 if (irqno
>= S3C2410_SUBIRQ_MIN
) {
216 /* cascaded interrupts. */
218 int i
= (irqno
- S3C2410_SUBIRQ_MIN
);
220 main_irqno
= subirq_to_main
[i
];
222 /* establish main irq if first time
223 * be careful that cookie shouldn't be 0 */
224 if (handler
[main_irqno
].func
!= cascade_irq_handler
)
225 s3c24x0_intr_establish(main_irqno
, level
, type
,
226 cascade_irq_handler
, (void *)((i
/3) + 1));
228 /* unmask it in submask register */
229 icreg(INTCTL_INTSUBMSK
) &= ~(1<<i
);
231 restore_interrupts(save
);
232 return &handler
[irqno
];
235 s3c2xx0_update_intr_masks(irqno
, level
);
238 * set trigger type for external interrupts 0..3
240 if (irqno
<= S3C24X0_INT_EXT(3)) {
242 * Update external interrupt control
244 s3c2410_setup_extint(irqno
, type
);
247 s3c2xx0_setipl(curcpl());
249 restore_interrupts(save
);
251 return &handler
[irqno
];
256 init_interrupt_masks(void)
260 for (i
=0; i
< NIPL
; ++i
)
261 s3c2xx0_imask
[i
] = 0;
263 #ifdef __HAVE_FAST_SOFTINTS
264 s3c24x0_soft_imask
[IPL_NONE
] = SI_TO_IRQBIT(SI_SOFTSERIAL
) |
265 SI_TO_IRQBIT(SI_SOFTNET
) | SI_TO_IRQBIT(SI_SOFTCLOCK
) |
266 SI_TO_IRQBIT(SI_SOFT
);
268 s3c24x0_soft_imask
[IPL_SOFT
] = SI_TO_IRQBIT(SI_SOFTSERIAL
) |
269 SI_TO_IRQBIT(SI_SOFTNET
) | SI_TO_IRQBIT(SI_SOFTCLOCK
);
272 * splsoftclock() is the only interface that users of the
273 * generic software interrupt facility have to block their
274 * soft intrs, so splsoftclock() must also block IPL_SOFT.
276 s3c24x0_soft_imask
[IPL_SOFTCLOCK
] = SI_TO_IRQBIT(SI_SOFTSERIAL
) |
277 SI_TO_IRQBIT(SI_SOFTNET
);
280 * splsoftnet() must also block splsoftclock(), since we don't
281 * want timer-driven network events to occur while we're
282 * processing incoming packets.
284 s3c24x0_soft_imask
[IPL_SOFTNET
] = SI_TO_IRQBIT(SI_SOFTSERIAL
);
286 for (i
= IPL_BIO
; i
< IPL_SOFTSERIAL
; ++i
)
287 s3c24x0_soft_imask
[i
] = SI_TO_IRQBIT(SI_SOFTSERIAL
);
292 s3c2410_intr_init(struct s3c24x0_softc
*sc
)
294 intctl_base
= (vaddr_t
) bus_space_vaddr(sc
->sc_sx
.sc_iot
,
295 sc
->sc_sx
.sc_intctl_ioh
);
297 s3c2xx0_intr_mask_reg
= (uint32_t *)(intctl_base
+ INTCTL_INTMSK
);
299 /* clear all pending interrupt */
300 icreg(INTCTL_SRCPND
) = ~0;
301 icreg(INTCTL_INTPND
) = ~0;
303 /* mask all sub interrupts */
304 icreg(INTCTL_INTSUBMSK
) = 0x7ff;
306 init_interrupt_masks();
308 s3c2xx0_intr_init(handler
, ICU_LEN
);
314 * mask/unmask sub interrupts
317 s3c2410_mask_subinterrupts(int bits
)
319 int psw
= disable_interrupts(IF32_bits
);
320 icreg(INTCTL_INTSUBMSK
) |= bits
;
321 restore_interrupts(psw
);
325 s3c2410_unmask_subinterrupts(int bits
)
327 int psw
= disable_interrupts(IF32_bits
);
328 icreg(INTCTL_INTSUBMSK
) &= ~bits
;
329 restore_interrupts(psw
);
333 * Update external interrupt control
335 static const u_char s3c24x0_ist
[] = {
336 EXTINTR_LOW
, /* NONE */
337 EXTINTR_FALLING
, /* PULSE */
338 EXTINTR_FALLING
, /* EDGE */
339 EXTINTR_LOW
, /* LEVEL */
346 s3c2410_setup_extint(int extint
, int type
)
351 int regidx
= extint
/8; /* GPIO_EXTINT[0:2] */
354 trig
= s3c24x0_ist
[type
];
356 save
= disable_interrupts(I32_bit
);
358 reg
= bus_space_read_4(s3c2xx0_softc
->sc_iot
,
359 s3c2xx0_softc
->sc_gpio_ioh
,
360 GPIO_EXTINT(regidx
));
362 reg
= reg
& ~(0x07 << (4*i
));
363 reg
|= trig
<< (4*i
);
365 bus_space_write_4(s3c2xx0_softc
->sc_iot
, s3c2xx0_softc
->sc_gpio_ioh
,
366 GPIO_EXTINT(regidx
), reg
);
368 restore_interrupts(save
);