Home | History | Annotate | Line # | Download | only in iq80310
iq80310_intr.c revision 1.17
      1 /*	$NetBSD: iq80310_intr.c,v 1.17 2002/08/17 16:42:23 briggs Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2001, 2002 Wasabi Systems, Inc.
      5  * All rights reserved.
      6  *
      7  * Written by Jason R. Thorpe for Wasabi Systems, Inc.
      8  *
      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  *	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
     23  *    written permission.
     24  *
     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.
     36  */
     37 
     38 /*
     39  * Interrupt support for the Intel IQ80310.
     40  */
     41 
     42 #include <sys/param.h>
     43 #include <sys/systm.h>
     44 #include <sys/malloc.h>
     45 
     46 #include <uvm/uvm_extern.h>
     47 
     48 #include <machine/bus.h>
     49 #include <machine/intr.h>
     50 
     51 #include <arm/cpufunc.h>
     52 
     53 #include <arm/xscale/i80200reg.h>
     54 #include <arm/xscale/i80200var.h>
     55 
     56 #include <evbarm/iq80310/iq80310reg.h>
     57 #include <evbarm/iq80310/iq80310var.h>
     58 #include <evbarm/iq80310/obiovar.h>
     59 
     60 /* Interrupt handler queues. */
     61 struct intrq intrq[NIRQ];
     62 
     63 /* Interrupts to mask at each level. */
     64 int iq80310_imask[NIPL];
     65 
     66 /* Current interrupt priority level. */
     67 __volatile int current_spl_level;
     68 
     69 /* Interrupts pending. */
     70 __volatile int iq80310_ipending;
     71 
     72 /* Software copy of the IRQs we have enabled. */
     73 uint32_t intr_enabled;
     74 
     75 /*
     76  * Map a software interrupt queue index (at the top of the word, and
     77  * highest priority softintr is encountered first in an ffs()).
     78  */
     79 #define	SI_TO_IRQBIT(si)	(1U << (31 - (si)))
     80 
     81 /*
     82  * Map a software interrupt queue to an interrupt priority level.
     83  */
     84 static const int si_to_ipl[SI_NQUEUES] = {
     85 	IPL_SOFT,		/* SI_SOFT */
     86 	IPL_SOFTCLOCK,		/* SI_SOFTCLOCK */
     87 	IPL_SOFTNET,		/* SI_SOFTNET */
     88 	IPL_SOFTSERIAL,		/* SI_SOFTSERIAL */
     89 };
     90 
     91 void	iq80310_intr_dispatch(struct clockframe *frame);
     92 
     93 static __inline uint32_t
     94 iq80310_intstat_read(void)
     95 {
     96 	uint32_t intstat;
     97 
     98 	intstat = CPLD_READ(IQ80310_XINT3_STATUS) & 0x1f;
     99 #if defined(IRQ_READ_XINT0)
    100 	if (IRQ_READ_XINT0)
    101 		intstat |= (CPLD_READ(IQ80310_XINT0_STATUS) & 0x7) << 5;
    102 #endif
    103 
    104 	/* XXX Why do we have to mask off? */
    105 	return (intstat & intr_enabled);
    106 }
    107 
    108 static __inline void
    109 iq80310_set_intrmask(void)
    110 {
    111 	uint32_t disabled;
    112 
    113 	intr_enabled |= IRQ_BITS_ALWAYS_ON;
    114 
    115 	/* The XINT_MASK register sets a bit to *disable*. */
    116 	disabled = (~intr_enabled) & IRQ_BITS;
    117 
    118 	CPLD_WRITE(IQ80310_XINT_MASK, disabled & 0x1f);
    119 }
    120 
    121 static __inline void
    122 iq80310_enable_irq(int irq)
    123 {
    124 
    125 	intr_enabled |= (1U << irq);
    126 	iq80310_set_intrmask();
    127 }
    128 
    129 static __inline void
    130 iq80310_disable_irq(int irq)
    131 {
    132 
    133 	intr_enabled &= ~(1U << irq);
    134 	iq80310_set_intrmask();
    135 }
    136 
    137 /*
    138  * NOTE: This routine must be called with interrupts disabled in the CPSR.
    139  */
    140 static void
    141 iq80310_intr_calculate_masks(void)
    142 {
    143 	struct intrq *iq;
    144 	struct intrhand *ih;
    145 	int irq, ipl;
    146 
    147 	/* First, figure out which IPLs each IRQ has. */
    148 	for (irq = 0; irq < NIRQ; irq++) {
    149 		int levels = 0;
    150 		iq = &intrq[irq];
    151 		iq80310_disable_irq(irq);
    152 		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
    153 		     ih = TAILQ_NEXT(ih, ih_list))
    154 			levels |= (1U << ih->ih_ipl);
    155 		iq->iq_levels = levels;
    156 	}
    157 
    158 	/* Next, figure out which IRQs are used by each IPL. */
    159 	for (ipl = 0; ipl < NIPL; ipl++) {
    160 		int irqs = 0;
    161 		for (irq = 0; irq < NIRQ; irq++) {
    162 			if (intrq[irq].iq_levels & (1U << ipl))
    163 				irqs |= (1U << irq);
    164 		}
    165 		iq80310_imask[ipl] = irqs;
    166 	}
    167 
    168 	iq80310_imask[IPL_NONE] = 0;
    169 
    170 	/*
    171 	 * Initialize the soft interrupt masks to block themselves.
    172 	 */
    173 	iq80310_imask[IPL_SOFT] = SI_TO_IRQBIT(SI_SOFT);
    174 	iq80310_imask[IPL_SOFTCLOCK] = SI_TO_IRQBIT(SI_SOFTCLOCK);
    175 	iq80310_imask[IPL_SOFTNET] = SI_TO_IRQBIT(SI_SOFTNET);
    176 	iq80310_imask[IPL_SOFTSERIAL] = SI_TO_IRQBIT(SI_SOFTSERIAL);
    177 
    178 	/*
    179 	 * splsoftclock() is the only interface that users of the
    180 	 * generic software interrupt facility have to block their
    181 	 * soft intrs, so splsoftclock() must also block IPL_SOFT.
    182 	 */
    183 	iq80310_imask[IPL_SOFTCLOCK] |= iq80310_imask[IPL_SOFT];
    184 
    185 	/*
    186 	 * splsoftnet() must also block splsoftclock(), since we don't
    187 	 * want timer-driven network events to occur while we're
    188 	 * processing incoming packets.
    189 	 */
    190 	iq80310_imask[IPL_SOFTNET] |= iq80310_imask[IPL_SOFTCLOCK];
    191 
    192 	/*
    193 	 * Enforce a heirarchy that gives "slow" device (or devices with
    194 	 * limited input buffer space/"real-time" requirements) a better
    195 	 * chance at not dropping data.
    196 	 */
    197 	iq80310_imask[IPL_BIO] |= iq80310_imask[IPL_SOFTNET];
    198 	iq80310_imask[IPL_NET] |= iq80310_imask[IPL_BIO];
    199 	iq80310_imask[IPL_SOFTSERIAL] |= iq80310_imask[IPL_NET];
    200 	iq80310_imask[IPL_TTY] |= iq80310_imask[IPL_SOFTSERIAL];
    201 
    202 	/*
    203 	 * splvm() blocks all interrupts that use the kernel memory
    204 	 * allocation facilities.
    205 	 */
    206 	iq80310_imask[IPL_IMP] |= iq80310_imask[IPL_TTY];
    207 
    208 	/*
    209 	 * Audio devices are not allowed to perform memory allocation
    210 	 * in their interrupt routines, and they have fairly "real-time"
    211 	 * requirements, so give them a high interrupt priority.
    212 	 */
    213 	iq80310_imask[IPL_AUDIO] |= iq80310_imask[IPL_IMP];
    214 
    215 	/*
    216 	 * splclock() must block anything that uses the scheduler.
    217 	 */
    218 	iq80310_imask[IPL_CLOCK] |= iq80310_imask[IPL_AUDIO];
    219 
    220 	/*
    221 	 * No separate statclock on the IQ80310.
    222 	 */
    223 	iq80310_imask[IPL_STATCLOCK] |= iq80310_imask[IPL_CLOCK];
    224 
    225 	/*
    226 	 * splhigh() must block "everything".
    227 	 */
    228 	iq80310_imask[IPL_HIGH] |= iq80310_imask[IPL_STATCLOCK];
    229 
    230 	/*
    231 	 * XXX We need serial drivers to run at the absolute highest priority
    232 	 * in order to avoid overruns, so serial > high.
    233 	 */
    234 	iq80310_imask[IPL_SERIAL] |= iq80310_imask[IPL_HIGH];
    235 
    236 	/*
    237 	 * Now compute which IRQs must be blocked when servicing any
    238 	 * given IRQ.
    239 	 */
    240 	for (irq = 0; irq < NIRQ; irq++) {
    241 		int irqs = (1U << irq);
    242 		iq = &intrq[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];
    248 		iq->iq_mask = irqs;
    249 	}
    250 }
    251 
    252 void
    253 iq80310_do_soft(void)
    254 {
    255 	static __cpu_simple_lock_t processing = __SIMPLELOCK_UNLOCKED;
    256 	int new, oldirqstate;
    257 
    258 	if (__cpu_simple_lock_try(&processing) == 0)
    259 		return;
    260 
    261 	new = current_spl_level;
    262 
    263 	oldirqstate = disable_interrupts(I32_bit);
    264 
    265 #define	DO_SOFTINT(si)							\
    266 	if ((iq80310_ipending & ~new) & SI_TO_IRQBIT(si)) {		\
    267 		iq80310_ipending &= ~SI_TO_IRQBIT(si);			\
    268 		current_spl_level |= iq80310_imask[si_to_ipl[(si)]];	\
    269 		restore_interrupts(oldirqstate);			\
    270 		softintr_dispatch(si);					\
    271 		oldirqstate = disable_interrupts(I32_bit);		\
    272 		current_spl_level = new;				\
    273 	}
    274 
    275 	DO_SOFTINT(SI_SOFTSERIAL);
    276 	DO_SOFTINT(SI_SOFTNET);
    277 	DO_SOFTINT(SI_SOFTCLOCK);
    278 	DO_SOFTINT(SI_SOFT);
    279 
    280 	__cpu_simple_unlock(&processing);
    281 
    282 	restore_interrupts(oldirqstate);
    283 }
    284 
    285 #if defined(EVBARM_SPL_NOINLINE)
    286 int
    287 _splraise(int ipl)
    288 {
    289 	int old;
    290 
    291 	old = current_spl_level;
    292 	current_spl_level |= iq80310_imask[ipl];
    293 
    294 	return (old);
    295 }
    296 
    297 __inline void
    298 splx(int new)
    299 {
    300 	int old;
    301 
    302 	old = current_spl_level;
    303 	current_spl_level = new;
    304 
    305 	/* If there are software interrupts to process, do it. */
    306 	if ((iq80310_ipending & ~IRQ_BITS) & ~new)
    307 		iq80310_do_soft();
    308 
    309 	/*
    310 	 * If there are pending hardware interrupts (i.e. the
    311 	 * external interrupt is disabled in the ICU), and all
    312 	 * hardware interrupts are being unblocked, then re-enable
    313 	 * the external hardware interrupt.
    314 	 *
    315 	 * XXX We have to wait for ALL hardware interrupts to
    316 	 * XXX be unblocked, because we currently lose if we
    317 	 * XXX get nested interrupts, and I don't know why yet.
    318 	 */
    319 	if ((new & IRQ_BITS) == 0 && (iq80310_ipending & IRQ_BITS))
    320 		i80200_intr_enable(INTCTL_IM | INTCTL_PM);
    321 }
    322 
    323 int
    324 _spllower(int ipl)
    325 {
    326 	int old = current_spl_level;
    327 
    328 	iq80310_splx(iq80310_imask[ipl]);
    329 	return (old);
    330 }
    331 
    332 #else
    333 
    334 #undef _splraise
    335 int
    336 _splraise(int ipl)
    337 {
    338 	return iq80310_splraise(ipl);
    339 }
    340 
    341 #undef splx
    342 __inline void
    343 splx(int new)
    344 {
    345 	return iq80310_splx(new);
    346 }
    347 
    348 #undef _spllower
    349 int
    350 _spllower(int ipl)
    351 {
    352 	return iq80310_spllower(ipl);
    353 }
    354 #endif
    355 
    356 void
    357 _setsoftintr(int si)
    358 {
    359 	int oldirqstate;
    360 
    361 	oldirqstate = disable_interrupts(I32_bit);
    362 	iq80310_ipending |= SI_TO_IRQBIT(si);
    363 	restore_interrupts(oldirqstate);
    364 
    365 	/* Process unmasked pending soft interrupts. */
    366 	if ((iq80310_ipending & ~IRQ_BITS) & ~current_spl_level)
    367 		iq80310_do_soft();
    368 }
    369 
    370 void
    371 iq80310_intr_init(void)
    372 {
    373 	struct intrq *iq;
    374 	int i;
    375 
    376 	/*
    377 	 * The Secondary PCI interrupts INTA, INTB, and INTC
    378 	 * area always enabled, since they cannot be masked
    379 	 * in the CPLD.
    380 	 */
    381 	intr_enabled |= IRQ_BITS_ALWAYS_ON;
    382 
    383 	for (i = 0; i < NIRQ; i++) {
    384 		iq = &intrq[i];
    385 		TAILQ_INIT(&iq->iq_list);
    386 
    387 		sprintf(iq->iq_name, "irq %d", i);
    388 		evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
    389 		    NULL, "iq80310", iq->iq_name);
    390 	}
    391 
    392 	iq80310_intr_calculate_masks();
    393 
    394 	/* Enable external interrupts on the i80200. */
    395 	i80200_extirq_dispatch = iq80310_intr_dispatch;
    396 	i80200_intr_enable(INTCTL_IM | INTCTL_PM);
    397 
    398 	/* Enable IRQs (don't yet use FIQs). */
    399 	enable_interrupts(I32_bit);
    400 }
    401 
    402 void *
    403 iq80310_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
    404 {
    405 	struct intrq *iq;
    406 	struct intrhand *ih;
    407 	u_int oldirqstate;
    408 
    409 	if (irq < 0 || irq > NIRQ)
    410 		panic("iq80310_intr_establish: IRQ %d out of range", irq);
    411 
    412 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
    413 	if (ih == NULL)
    414 		return (NULL);
    415 
    416 	ih->ih_func = func;
    417 	ih->ih_arg = arg;
    418 	ih->ih_ipl = ipl;
    419 	ih->ih_irq = irq;
    420 
    421 	iq = &intrq[irq];
    422 
    423 	/* All IQ80310 interrupts are level-triggered. */
    424 	iq->iq_ist = IST_LEVEL;
    425 
    426 	oldirqstate = disable_interrupts(I32_bit);
    427 
    428 	TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
    429 
    430 	iq80310_intr_calculate_masks();
    431 
    432 	restore_interrupts(oldirqstate);
    433 
    434 	return (ih);
    435 }
    436 
    437 void
    438 iq80310_intr_disestablish(void *cookie)
    439 {
    440 	struct intrhand *ih = cookie;
    441 	struct intrq *iq = &intrq[ih->ih_irq];
    442 	int oldirqstate;
    443 
    444 	oldirqstate = disable_interrupts(I32_bit);
    445 
    446 	TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
    447 
    448 	iq80310_intr_calculate_masks();
    449 
    450 	restore_interrupts(oldirqstate);
    451 }
    452 
    453 void
    454 iq80310_intr_dispatch(struct clockframe *frame)
    455 {
    456 	struct intrq *iq;
    457 	struct intrhand *ih;
    458 	int oldirqstate, pcpl, irq, ibit, hwpend, rv, stray;
    459 
    460 	stray = 1;
    461 
    462 	/* First, disable external IRQs. */
    463 	i80200_intr_disable(INTCTL_IM | INTCTL_PM);
    464 
    465 	pcpl = current_spl_level;
    466 
    467 	for (hwpend = iq80310_intstat_read(); hwpend != 0;) {
    468 		irq = ffs(hwpend) - 1;
    469 		ibit = (1U << irq);
    470 
    471 		stray = 0;
    472 
    473 		hwpend &= ~ibit;
    474 
    475 		if (pcpl & ibit) {
    476 			/*
    477 			 * IRQ is masked; mark it as pending and check
    478 			 * the next one.  Note: external IRQs are already
    479 			 * disabled.
    480 			 */
    481 			iq80310_ipending |= ibit;
    482 			continue;
    483 		}
    484 
    485 		iq80310_ipending &= ~ibit;
    486 		rv = 0;
    487 
    488 		iq = &intrq[irq];
    489 		iq->iq_ev.ev_count++;
    490 		uvmexp.intrs++;
    491 		current_spl_level |= iq->iq_mask;
    492 		oldirqstate = enable_interrupts(I32_bit);
    493 		for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
    494 		     ih = TAILQ_NEXT(ih, ih_list)) {
    495 			rv |= (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
    496 		}
    497 		restore_interrupts(oldirqstate);
    498 
    499 		current_spl_level = pcpl;
    500 
    501 #if 0 /* XXX */
    502 		if (rv == 0)
    503 			printf("Stray interrupt: IRQ %d\n", irq);
    504 #endif
    505 	}
    506 
    507 #if 0 /* XXX */
    508 	if (stray)
    509 		printf("Stray external interrupt\n");
    510 #endif
    511 
    512 	/* Check for pendings soft intrs. */
    513 	if ((iq80310_ipending & ~IRQ_BITS) & ~current_spl_level) {
    514 		oldirqstate = enable_interrupts(I32_bit);
    515 		iq80310_do_soft();
    516 		restore_interrupts(oldirqstate);
    517 	}
    518 
    519 	/*
    520 	 * If no hardware interrupts are masked, re-enable external
    521 	 * interrupts.
    522 	 */
    523 	if ((iq80310_ipending & IRQ_BITS) == 0)
    524 		i80200_intr_enable(INTCTL_IM | INTCTL_PM);
    525 }
    526