If I80321_HPI_ENABLED is defined, add support for handling interrupts from

devices hooked up to the HPI pin.

HPIs cannot be masked at the interrupt controller; they can only be masked
by disabling IRQs in the XScale core. To deal with this, we tweak the
interrupt frame so that IRQs are disabled when the interrupt dispatcher
returns due to a masked HPI interrupt. IRQs will be re-enabled by a
subsequent splx(9).

Fortunately the only instance where HPI is used is for the console UART
on a couple of boards, so this hack does not adversely affect performance.

Contributed by Wasabi Systems.
This commit is contained in:
scw 2006-11-08 23:45:41 +00:00
parent 249933b899
commit d3089a454e
2 changed files with 101 additions and 8 deletions

View File

@ -1,10 +1,10 @@
/* $NetBSD: i80321_icu.c,v 1.12 2006/05/17 05:15:26 mrg Exp $ */
/* $NetBSD: i80321_icu.c,v 1.13 2006/11/08 23:45:41 scw Exp $ */
/*
* Copyright (c) 2001, 2002 Wasabi Systems, Inc.
* Copyright (c) 2001, 2002, 2006 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
* Written by Jason R. Thorpe and Steve C. Woodford for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -36,7 +36,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: i80321_icu.c,v 1.12 2006/05/17 05:15:26 mrg Exp $");
__KERNEL_RCSID(0, "$NetBSD: i80321_icu.c,v 1.13 2006/11/08 23:45:41 scw Exp $");
#ifndef EVBARM_SPL_NOINLINE
#define EVBARM_SPL_NOINLINE
@ -462,12 +462,46 @@ i80321_intr_disestablish(void *cookie)
restore_interrupts(oldirqstate);
}
/*
* Hardware interrupt handler.
*
* If I80321_HPI_ENABLED is defined, this code attempts to deal with
* HPI interrupts as best it can.
*
* The problem is that HPIs cannot be masked at the interrupt controller;
* they can only be masked by disabling IRQs in the XScale core.
*
* So, if an HPI comes in and we determine that it should be masked at
* the current IPL then we mark it pending in the usual way and set
* I32_bit in the interrupt frame. This ensures that when we return from
* i80321_intr_dispatch(), IRQs will be disabled in the XScale core. (To
* ensure IRQs are enabled later, i80321_splx() has been modified to do
* just that when a pending HPI interrupt is unmasked.) Additionally,
* because HPIs are level-triggered, the registered handler for the HPI
* interrupt will also be invoked with IRQs disabled. If a masked HPI
* occurs at the same time as another unmasked higher priority interrupt,
* the higher priority handler will also be invoked with IRQs disabled.
* As a result, the system could end up executing a lot of code with IRQs
* completely disabled if the HPI's IPL is relatively low.
*
* At the present time, the only known use of HPI is for the console UART
* on a couple of boards. This is probably the least intrusive use of HPI
* as IPL_SERIAL is the highest priority IPL in the system anyway. The
* code has not been tested with HPI hooked up to a class of device which
* interrupts below IPL_SERIAL. Indeed, such a configuration is likely to
* perform very poorly if at all, even though the following code has been
* designed (hopefully) to cope with it.
*/
void
i80321_intr_dispatch(struct clockframe *frame)
{
struct intrq *iq;
struct intrhand *ih;
int oldirqstate, pcpl, irq, ibit, hwpend;
#ifdef I80321_HPI_ENABLED
int oldpending;
#endif
pcpl = current_spl_level;
@ -480,7 +514,17 @@ i80321_intr_dispatch(struct clockframe *frame)
intr_enabled &= ~hwpend;
i80321_set_intrmask();
#ifdef I80321_HPI_ENABLED
oldirqstate = 0; /* XXX: quell gcc warning */
#endif
while (hwpend != 0) {
#ifdef I80321_HPI_ENABLED
/* Deal with HPI interrupt first */
if (__predict_false(hwpend & INT_HPIMASK))
irq = ICU_INT_HPI;
else
#endif
irq = ffs(hwpend) - 1;
ibit = (1U << irq);
@ -491,23 +535,58 @@ i80321_intr_dispatch(struct clockframe *frame)
* IRQ is masked; mark it as pending and check
* the next one. Note: the IRQ is already disabled.
*/
#ifdef I80321_HPI_ENABLED
if (__predict_false(irq == ICU_INT_HPI)) {
/*
* This is an HPI. We *must* disable
* IRQs in the interrupt frame until
* INT_HPIMASK is cleared by a later
* call to splx(). Otherwise the level-
* triggered interrupt will just keep
* coming back.
*/
frame->cf_if.if_spsr |= I32_bit;
}
#endif
i80321_ipending |= ibit;
continue;
}
#ifdef I80321_HPI_ENABLED
oldpending = i80321_ipending | ibit;
#endif
i80321_ipending &= ~ibit;
iq = &intrq[irq];
iq->iq_ev.ev_count++;
uvmexp.intrs++;
current_spl_level |= iq->iq_mask;
#ifdef I80321_HPI_ENABLED
/*
* Re-enable interrupts iff an HPI is not pending
*/
if (__predict_true((oldpending & INT_HPIMASK) == 0))
#endif
oldirqstate = enable_interrupts(I32_bit);
for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
ih = TAILQ_NEXT(ih, ih_list)) {
(void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
}
#ifdef I80321_HPI_ENABLED
if (__predict_true((oldpending & INT_HPIMASK) == 0))
#endif
restore_interrupts(oldirqstate);
#ifdef I80321_HPI_ENABLED
else if (irq == ICU_INT_HPI) {
/*
* We've just handled the HPI. Make sure IRQs
* are enabled in the interrupt frame.
* Here's hoping the handler really did clear
* down the source...
*/
frame->cf_if.if_spsr &= ~I32_bit;
}
#endif
current_spl_level = pcpl;
/* Re-enable this interrupt now that's it's cleared. */
@ -523,8 +602,16 @@ i80321_intr_dispatch(struct clockframe *frame)
/* Check for pendings soft intrs. */
if ((i80321_ipending & INT_SWMASK) & ~current_spl_level) {
#ifdef I80321_HPI_ENABLED
/* XXX: This is only necessary if HPI is < IPL_SOFT* */
if (__predict_true((i80321_ipending & INT_HPIMASK) == 0))
#endif
oldirqstate = enable_interrupts(I32_bit);
i80321_do_pending();
#ifdef I80321_HPI_ENABLED
/* XXX: This is only necessary if HPI is < IPL_NET* */
if (__predict_true((i80321_ipending & INT_HPIMASK) == 0))
#endif
restore_interrupts(oldirqstate);
}
}

View File

@ -1,10 +1,10 @@
/* $NetBSD: i80321_intr.h,v 1.8 2006/04/10 03:36:03 simonb Exp $ */
/* $NetBSD: i80321_intr.h,v 1.9 2006/11/08 23:45:41 scw Exp $ */
/*
* Copyright (c) 2001, 2002 Wasabi Systems, Inc.
* Copyright (c) 2001, 2002, 2006 Wasabi Systems, Inc.
* All rights reserved.
*
* Written by Jason R. Thorpe for Wasabi Systems, Inc.
* Written by Jason R. Thorpe and Steve C. Woodford for Wasabi Systems, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@ -63,6 +63,8 @@ i80321_set_intrmask(void)
((1U << ICU_INT_bit26) | (1U << ICU_INT_bit22) | \
(1U << ICU_INT_bit5) | (1U << ICU_INT_bit4))
#define INT_HPIMASK (1u << ICU_INT_HPI)
static inline void __attribute__((__unused__))
i80321_splx(int new)
{
@ -82,6 +84,10 @@ i80321_splx(int new)
oldirqstate = disable_interrupts(I32_bit);
intr_enabled |= hwpend;
i80321_set_intrmask();
#ifdef I80321_HPI_ENABLED
if (__predict_false(hwpend & INT_HPIMASK))
oldirqstate &= ~I32_bit;
#endif
restore_interrupts(oldirqstate);
}