Expand PMF_FN_* macros.
[netbsd-mini2440.git] / sys / arch / arm / omap / omap_intr.c
blobb8fa8394edad93ba8c9901a4b2353fc4d5cf07a6
1 /* $NetBSD: omap_intr.c,v 1.5 2008/04/27 18:58:45 matt Exp $ */
3 /*
4 * Based on arch/arm/xscale/pxa2x0_intr.c
6 * Copyright (c) 2002 Genetec Corporation. All rights reserved.
7 * Written by Hiroyuki Bessho for Genetec Corporation.
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 * Genetec Corporation.
21 * 4. The name of Genetec Corporation may not be used to endorse or
22 * promote products derived from this software without specific prior
23 * written permission.
25 * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``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 GENETEC CORPORATION
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.
39 * IRQ handler for the Texas Instruments OMAP processors. The OMAPs
40 * have two cascaded interrupt controllers. They have similarities, but
41 * are not identical.
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: omap_intr.c,v 1.5 2008/04/27 18:58:45 matt Exp $");
47 #include <sys/param.h>
48 #include <sys/systm.h>
49 #include <sys/malloc.h>
51 #include <machine/bus.h>
52 #include <machine/intr.h>
53 #include <machine/lock.h>
55 #include <arm/omap/omap_reg.h>
56 #include <arm/omap/omap_tipb.h>
59 * Array of arrays to give us the per bank masks for each level. The OMAP
60 * interrupt controller blocks an interrupt when the corresponding bit is set
61 * in the mask register. Initialize our masks to have all interrupts blocked
62 * for all levels.
64 uint32_t omap_spl_masks[NIPL][OMAP_NBANKS] =
65 { [ 0 ... NIPL-1 ] = { [ 0 ... OMAP_NBANKS-1 ] = ~0 } };
68 * And OR in the following global interrupt masks at all levels. This is
69 * used to hold off interrupts that omap_irq_handler will soon service,
70 * since servicing is performed in random order wrt spl levels.
73 uint32_t omap_global_masks[OMAP_NBANKS];
75 static int stray_interrupt(void *);
76 static void init_interrupt_masks(void);
77 static void omap_update_intr_masks(int, int);
78 static void omapintc_set_name(int, const char *, int);
80 typedef int (* omap_irq_handler_t)(void *);
83 * interrupt dispatch table.
85 #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
86 struct intrhand {
87 TAILQ_ENTRY(intrhand) ih_list; /* link on intrq list */
88 int (*ih_func)(void *); /* handler */
89 void *ih_arg; /* arg for handler */
91 #endif
93 static struct irq_handler {
94 struct evcnt ev;
95 char irq_num_str[8];
96 const char *name;
97 #ifdef MULTIPLE_HANDLERS_ON_ONE_IRQ
98 TAILQ_HEAD(,intrhand) list;
99 #else
100 omap_irq_handler_t func;
101 #endif
102 void *cookie; /* NULL for stack frame */
103 /* struct evbnt ev; */
104 } handler[OMAP_NIRQ];
106 static int extirq_level[OMAP_NIRQ];
109 omapintc_match(device_t parent, cfdata_t cf, void *aux)
111 return (1);
114 void
115 omapintc_attach(device_t parent, device_t self, void *args)
117 int i;
118 aprint_normal(": Interrupt Controller\n");
119 aprint_naive("\n");
121 /* Make sure we have interrupts globally off. */
122 disable_interrupts(I32_bit);
125 * Initialize the controller hardware.
127 /* Turn off the first level's global mask. */
128 write_icu(OMAP_INT_L1_BASE, OMAP_INTL1_GMR, 0);
129 /* Turn off the second level's global mask. */
130 write_icu(OMAP_INT_L2_BASE, OMAP_INTL2_CONTROL, 0);
131 /* Allow the second level to automatically idle. */
132 write_icu(OMAP_INT_L2_BASE, OMAP_INTL2_OCP_CFG,
133 OMAP_INTL2_OCP_CFG_SMART_IDLE | OMAP_INTL2_OCP_CFG_AUTOIDLE);
135 * Now set all of the Interrupt Level Registers. Set the triggering
136 * as appropriate for that interrupt, but otherwise, set them all to
137 * low priority and to be an IRQ (not a FIQ).
139 static const uint32_t ilr_def
140 = ((OMAP_INTB_ILR_PRIO_LOWEST << OMAP_INTB_ILR_PRIO_SHIFT)
141 | OMAP_INTB_ILR_IRQ);
142 for (i=0; i<OMAP_NIRQ; i++) {
143 const omap_intr_info_t *inf = &omap_intr_info[i];
144 volatile uint32_t *ILR = (volatile uint32_t *)inf->ILR;
146 switch (inf->trig) {
147 case INVALID:
148 break;
149 case TRIG_LEVEL:
150 *ILR = ilr_def | OMAP_INTB_ILR_LEVEL;
151 break;
152 case TRIG_EDGE:
153 case TRIG_LEVEL_OR_EDGE:
154 *ILR = ilr_def | OMAP_INTB_ILR_EDGE;
155 break;
156 default:
157 panic("Bad trigger (%d) on irq %d\n", inf->trig, i);
158 break;
162 /* Set all interrupts to be stray and to have event counters. */
163 omapintc_set_name(OMAP_INT_L2_IRQ, "IRQ from L2", false);
164 omapintc_set_name(OMAP_INT_L2_FIQ, "FIQ from L2", false);
165 #ifdef __HAVE_FAST_SOFTINTS
166 omapintc_set_name(omap_si_to_irq[SI_SOFTCLOCK], "SOFTCLOCK", false);
167 omapintc_set_name(omap_si_to_irq[SI_SOFTBIO], "SOFTBIO", false);
168 omapintc_set_name(omap_si_to_irq[SI_SOFTNET], "SOFTNET", false);
169 omapintc_set_name(omap_si_to_irq[SI_SOFTSERIAL], "SOFTSERIAL", false);
170 #endif
171 for(i = 0; i < __arraycount(handler); ++i) {
172 handler[i].func = stray_interrupt;
173 handler[i].cookie = (void *)(intptr_t) i;
174 extirq_level[i] = IPL_SERIAL;
175 sprintf(handler[i].irq_num_str, "#%d", i);
176 if (handler[i].name == NULL)
177 omapintc_set_name(i, handler[i].irq_num_str, false);
180 /* Initialize our table of masks. */
181 init_interrupt_masks();
184 * Now that we have the masks for all the levels set up, write the
185 * masks to the hardware.
187 omap_splx(IPL_SERIAL);
189 /* Everything is all set. Enable the interrupts. */
190 enable_interrupts(I32_bit);
193 static void
194 dispatch_irq(int irqno, struct clockframe *frame)
196 if (extirq_level[irqno] != curcpl())
197 splx(extirq_level[irqno]);
199 #ifndef MULTIPLE_HANDLERS_ON_ONE_IRQ
200 (* handler[irqno].func)(handler[irqno].cookie == 0
201 ? frame : handler[irqno].cookie );
202 #else
203 /* process all handlers for this interrupt. */
204 #error Having multiple handlers per IRQ is not yet supported.
205 #endif
209 * called from irq_entry.
211 void
212 omap_irq_handler(void *arg)
214 struct clockframe *frame = arg;
215 int saved_spl_level;
216 int unmaskedpend[OMAP_NBANKS];
217 int bank;
218 int level2 = 0;
220 saved_spl_level = curcpl();
222 for (bank = 0; bank < OMAP_NBANKS; bank++) {
223 int masked = read_icu(omap_intr_bank_bases[bank],
224 OMAP_INTB_MIR);
225 int pend = read_icu(omap_intr_bank_bases[bank],
226 OMAP_INTB_ITR);
228 * Save away pending unmasked interrupts for handling in
229 * a moment. Mask those interrupts, will unmask after
230 * serviced.
233 unmaskedpend[bank] = pend & ~masked;
234 write_icu(omap_intr_bank_bases[bank], OMAP_INTB_ITR,
235 ~unmaskedpend[bank]);
236 write_icu(omap_intr_bank_bases[bank], OMAP_INTB_MIR,
237 masked | unmaskedpend[bank]);
238 omap_global_masks[bank] |= unmaskedpend[bank];
241 * If any interrupt is pending for the Level-2 controller
242 * then we need a Level-2 new IRQ agreement to receive
243 * another one.
246 if (bank && pend)
247 level2 = 1;
250 /* Let the interrupt controllers process the next interrupt.
251 Acknowledge Level-1 IRQs. */
252 write_icu(OMAP_INT_L1_BASE, OMAP_INTL1_CONTROL,
253 OMAP_INTL1_CONTROL_NEW_IRQ_AGR);
255 if (level2) {
256 /* Acknowledge Level-2 IRQs. */
257 write_icu(OMAP_INT_L2_BASE, OMAP_INTL2_CONTROL,
258 OMAP_INTL2_CONTROL_NEW_IRQ_AGR);
262 * Invoke handlers for interrupts found pending and unmasked
263 * above. Unmask the interrupts for each bank after servicing.
266 for (bank = 0; bank < OMAP_NBANKS; bank++) {
267 int irqno;
268 int oldirqstate;
269 int pendtodo = unmaskedpend[bank];
271 if (!unmaskedpend[bank])
272 continue;
274 while ((irqno = ffs(pendtodo) - 1) != -1) {
275 irqno += bank * OMAP_BANK_WIDTH;
277 if (omap_intr_info[irqno].trig == INVALID)
278 printf("Bad IRQ %d.\n", irqno);
280 handler[irqno].ev.ev_count++;
282 if (irqno != OMAP_INT_L2_IRQ) {
283 oldirqstate = enable_interrupts(I32_bit);
284 dispatch_irq(irqno, frame);
285 restore_interrupts(oldirqstate);
288 pendtodo &= ~omap_intr_info[irqno].mask;
291 omap_global_masks[bank] &= ~unmaskedpend[bank];
292 write_icu(omap_intr_bank_bases[bank], OMAP_INTB_MIR,
293 read_icu(omap_intr_bank_bases[bank],
294 OMAP_INTB_MIR) &
295 ~unmaskedpend[bank]);
298 /* Restore spl to what it was when this interrupt happened. */
299 splx(saved_spl_level);
302 static int
303 stray_interrupt(void *cookie)
305 int irqno = (int)cookie;
306 printf("stray interrupt number %d: \"%s\".\n",
307 irqno, handler[irqno].name);
309 if (OMAP_IRQ_MIN <= irqno && irqno < OMAP_NIRQ){
310 /* Keep it from happening again. */
311 omap_update_intr_masks(irqno, IPL_NONE);
314 return 0;
317 static inline void
318 level_block_irq(int lvl, const omap_intr_info_t *inf)
320 omap_spl_masks[lvl][inf->bank_num] |= inf->mask;
322 static inline void
323 level_allow_irq(int lvl, const omap_intr_info_t *inf)
325 omap_spl_masks[lvl][inf->bank_num] &= ~inf->mask;
327 static inline void
328 level_mask_level(int dst_level, int src_level)
330 int i;
331 for (i = 0; i < OMAP_NBANKS; i++) {
332 omap_spl_masks[dst_level][i]
333 |= omap_spl_masks[src_level][i];
338 * Interrupt Mask Handling
341 static void
342 omap_update_intr_masks(int irqno, int level)
344 const omap_intr_info_t *irq_inf =&omap_intr_info[irqno];
345 int i;
346 int psw = disable_interrupts(I32_bit);
348 for(i = 0; i < level; ++i)
349 /* Enable interrupt at lower level */
350 level_allow_irq(i, irq_inf);
352 for( ; i < NIPL-1; ++i)
353 /* Disable interrupt at upper level */
354 level_block_irq(i, irq_inf);
357 * There is no way for interrupts to be enabled at upper levels, but
358 * not at lower levels and vice-versa. This means that this function
359 * doesn't have to enforce it.
362 /* Refresh the hardware's masks in case the current level's changed. */
363 omap_splx(curcpl());
365 restore_interrupts(psw);
369 static void
370 init_interrupt_masks(void)
372 const omap_intr_info_t
373 #ifdef __HAVE_FAST_SOFTINTS
374 *softclock_inf =&omap_intr_info[omap_si_to_irq[SI_SOFTCLOCK]],
375 *softbio_inf =&omap_intr_info[omap_si_to_irq[SI_SOFTBIO]],
376 *softnet_inf =&omap_intr_info[omap_si_to_irq[SI_SOFTNET]],
377 *softserial_inf=&omap_intr_info[omap_si_to_irq[SI_SOFTSERIAL]],
378 #endif
379 *l2_inf =&omap_intr_info[OMAP_INT_L2_IRQ];
380 int i;
382 #ifdef __HAVE_FAST_SOFTINTS
384 * We just blocked all the interrupts in all the masks. Now we just
385 * go through and modify the masks to allow the software interrupts as
386 * documented in the spl(9) man page.
388 for (i = IPL_NONE; i < IPL_SOFTCLOCK; ++i)
389 level_allow_irq(i, softclock_inf);
390 for (i = IPL_NONE; i < IPL_SOFTBIO; ++i)
391 level_allow_irq(i, softbio_inf);
392 for (i = IPL_NONE; i < IPL_SOFTNET; ++i)
393 level_allow_irq(i, softnet_inf);
394 for (i = IPL_NONE; i < IPL_SOFTSERIAL; ++i)
395 level_allow_irq(i, softserial_inf);
396 #endif
399 * We block level 2 interrupts down in the level 2 controller, so we
400 * can allow the level 1 interrupt controller to service the level 2
401 * interrupts at all levels.
403 for (i = IPL_NONE; i < IPL_SERIAL; ++i)
404 level_allow_irq(i, l2_inf);
407 #undef splx
408 void
409 splx(int ipl)
411 omap_splx(ipl);
414 #undef _splraise
416 _splraise(int ipl)
418 return omap_splraise(ipl);
421 #undef _spllower
423 _spllower(int ipl)
426 return omap_spllower(ipl);
429 #ifdef __HAVE_FAST_SOFTINTS
430 #undef _setsoftintr
431 void
432 _setsoftintr(int si)
435 return omap_setsoftintr(si);
437 #endif
439 void *
440 omap_intr_establish(int irqno, int level, const char *name,
441 int (*func)(void *), void *cookie)
443 const omap_intr_info_t *info;
444 int psw;
445 if (irqno < OMAP_IRQ_MIN || irqno >= OMAP_NIRQ
446 || irqno == OMAP_INT_L2_IRQ
447 || omap_intr_info[irqno].trig == INVALID)
448 panic("%s(): bogus irq number %d", __func__, irqno);
450 #ifndef MULTIPLE_HANDLERS_ON_ONE_IRQ
451 if (handler[irqno].func != stray_interrupt)
452 panic("Shared interrupts are not supported (irqno=%d)\n",
453 irqno);
454 #endif
456 psw = disable_interrupts(I32_bit);
459 * Name the evcnt what they passed us. Note this will zero the
460 * count.
462 omapintc_set_name(irqno, name, true);
464 /* Clear any existing interrupt by writing a zero into its ITR bit. */
465 info = &omap_intr_info[irqno];
466 write_icu(info->bank_base, OMAP_INTB_ITR, ~info->mask);
468 handler[irqno].cookie = cookie;
469 handler[irqno].func = func;
470 extirq_level[irqno] = level;
471 omap_update_intr_masks(irqno, level);
473 restore_interrupts(psw);
475 return (&handler[irqno]);
478 void
479 omap_intr_disestablish(void *v)
481 struct irq_handler *irq_handler = v;
482 int irqno = ((unsigned)v - (unsigned)&handler[0])/sizeof(handler[0]);
484 if (irqno < OMAP_IRQ_MIN || irqno >= OMAP_NIRQ
485 || irqno == OMAP_INT_L2_IRQ
486 || omap_intr_info[irqno].trig == INVALID)
487 panic("%s(): bogus irq number %d", __func__, irqno);
489 int psw = disable_interrupts(I32_bit);
492 * Put the evcnt name back to our number string. Note this will zero
493 * the count.
495 omapintc_set_name(irqno, handler[irqno].irq_num_str, true);
497 irq_handler->cookie = (void *)(intptr_t) irqno;
498 irq_handler->func = stray_interrupt;
499 extirq_level[irqno] = IPL_SERIAL;
500 omap_update_intr_masks(irqno, IPL_NONE);
502 restore_interrupts(psw);
505 static void
506 omapintc_set_name(int irqno, const char *name, int detach_first)
508 const char *group;
510 if (irqno < OMAP_INT_L1_NIRQ)
511 group = "omap_intr L1";
512 else
513 group = "omap_intr L2";
515 if (detach_first)
516 evcnt_detach(&handler[irqno].ev);
518 handler[irqno].name = name;
520 evcnt_attach_dynamic(&handler[irqno].ev, EVCNT_TYPE_INTR, NULL,
521 group, handler[irqno].name);