[POWERPC] 4xx: make UIC use generic level irq handler
This patch makes PowerPC 4xx UIC use generic level irq handler instead of a custom handle_uic_irq() function. We ack only edge irqs in mask_ack callback, since acking a level irq on UIC has no effect if the interrupt is still asserted by the device, even if the interrupt is already masked. So, to really de-assert the interrupt we need to de-assert the external source first *and* ack it on UIC then. The handle_level_irq() function masks and ack's the interrupt with mask_ack callback prior to calling the actual ISR and unmasks it at the end. So, to use it with UIC interrupts we need to ack level irqs in the unmask callback instead, after the ISR has de-asserted the external interrupt source. Even if we ack the interrupt that we didn't handle (unmask/ack it at the end of the handler, while next irq is already pending) it will not de-assert the irq, untill we de-assert its exteral source. Signed-off-by: Valentine Barshak <vbarshak@ru.mvista.com> Signed-off-by: Josh Boyer <jwboyer@linux.vnet.ibm.com>
This commit is contained in:
parent
309ae1a363
commit
c80905637e
|
@ -60,14 +60,19 @@ struct uic {
|
||||||
|
|
||||||
static void uic_unmask_irq(unsigned int virq)
|
static void uic_unmask_irq(unsigned int virq)
|
||||||
{
|
{
|
||||||
|
struct irq_desc *desc = get_irq_desc(virq);
|
||||||
struct uic *uic = get_irq_chip_data(virq);
|
struct uic *uic = get_irq_chip_data(virq);
|
||||||
unsigned int src = uic_irq_to_hw(virq);
|
unsigned int src = uic_irq_to_hw(virq);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
u32 er;
|
u32 er, sr;
|
||||||
|
|
||||||
|
sr = 1 << (31-src);
|
||||||
spin_lock_irqsave(&uic->lock, flags);
|
spin_lock_irqsave(&uic->lock, flags);
|
||||||
|
/* ack level-triggered interrupts here */
|
||||||
|
if (desc->status & IRQ_LEVEL)
|
||||||
|
mtdcr(uic->dcrbase + UIC_SR, sr);
|
||||||
er = mfdcr(uic->dcrbase + UIC_ER);
|
er = mfdcr(uic->dcrbase + UIC_ER);
|
||||||
er |= 1 << (31 - src);
|
er |= sr;
|
||||||
mtdcr(uic->dcrbase + UIC_ER, er);
|
mtdcr(uic->dcrbase + UIC_ER, er);
|
||||||
spin_unlock_irqrestore(&uic->lock, flags);
|
spin_unlock_irqrestore(&uic->lock, flags);
|
||||||
}
|
}
|
||||||
|
@ -99,6 +104,7 @@ static void uic_ack_irq(unsigned int virq)
|
||||||
|
|
||||||
static void uic_mask_ack_irq(unsigned int virq)
|
static void uic_mask_ack_irq(unsigned int virq)
|
||||||
{
|
{
|
||||||
|
struct irq_desc *desc = get_irq_desc(virq);
|
||||||
struct uic *uic = get_irq_chip_data(virq);
|
struct uic *uic = get_irq_chip_data(virq);
|
||||||
unsigned int src = uic_irq_to_hw(virq);
|
unsigned int src = uic_irq_to_hw(virq);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -109,7 +115,16 @@ static void uic_mask_ack_irq(unsigned int virq)
|
||||||
er = mfdcr(uic->dcrbase + UIC_ER);
|
er = mfdcr(uic->dcrbase + UIC_ER);
|
||||||
er &= ~sr;
|
er &= ~sr;
|
||||||
mtdcr(uic->dcrbase + UIC_ER, er);
|
mtdcr(uic->dcrbase + UIC_ER, er);
|
||||||
mtdcr(uic->dcrbase + UIC_SR, sr);
|
/* On the UIC, acking (i.e. clearing the SR bit)
|
||||||
|
* a level irq will have no effect if the interrupt
|
||||||
|
* is still asserted by the device, even if
|
||||||
|
* the interrupt is already masked. Therefore
|
||||||
|
* we only ack the egde interrupts here, while
|
||||||
|
* level interrupts are ack'ed after the actual
|
||||||
|
* isr call in the uic_unmask_irq()
|
||||||
|
*/
|
||||||
|
if (!(desc->status & IRQ_LEVEL))
|
||||||
|
mtdcr(uic->dcrbase + UIC_SR, sr);
|
||||||
spin_unlock_irqrestore(&uic->lock, flags);
|
spin_unlock_irqrestore(&uic->lock, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,64 +188,6 @@ static struct irq_chip uic_irq_chip = {
|
||||||
.set_type = uic_set_irq_type,
|
.set_type = uic_set_irq_type,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* handle_uic_irq - irq flow handler for UIC
|
|
||||||
* @irq: the interrupt number
|
|
||||||
* @desc: the interrupt description structure for this irq
|
|
||||||
*
|
|
||||||
* This is modified version of the generic handle_level_irq() suitable
|
|
||||||
* for the UIC. On the UIC, acking (i.e. clearing the SR bit) a level
|
|
||||||
* irq will have no effect if the interrupt is still asserted by the
|
|
||||||
* device, even if the interrupt is already masked. Therefore, unlike
|
|
||||||
* the standard handle_level_irq(), we must ack the interrupt *after*
|
|
||||||
* invoking the ISR (which should have de-asserted the interrupt in
|
|
||||||
* the external source). For edge interrupts we ack at the beginning
|
|
||||||
* instead of the end, to keep the window in which we can miss an
|
|
||||||
* interrupt as small as possible.
|
|
||||||
*/
|
|
||||||
void fastcall handle_uic_irq(unsigned int irq, struct irq_desc *desc)
|
|
||||||
{
|
|
||||||
unsigned int cpu = smp_processor_id();
|
|
||||||
struct irqaction *action;
|
|
||||||
irqreturn_t action_ret;
|
|
||||||
|
|
||||||
spin_lock(&desc->lock);
|
|
||||||
if (desc->status & IRQ_LEVEL)
|
|
||||||
desc->chip->mask(irq);
|
|
||||||
else
|
|
||||||
desc->chip->mask_ack(irq);
|
|
||||||
|
|
||||||
if (unlikely(desc->status & IRQ_INPROGRESS))
|
|
||||||
goto out_unlock;
|
|
||||||
desc->status &= ~(IRQ_REPLAY | IRQ_WAITING);
|
|
||||||
kstat_cpu(cpu).irqs[irq]++;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If its disabled or no action available
|
|
||||||
* keep it masked and get out of here
|
|
||||||
*/
|
|
||||||
action = desc->action;
|
|
||||||
if (unlikely(!action || (desc->status & IRQ_DISABLED))) {
|
|
||||||
desc->status |= IRQ_PENDING;
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
desc->status |= IRQ_INPROGRESS;
|
|
||||||
desc->status &= ~IRQ_PENDING;
|
|
||||||
spin_unlock(&desc->lock);
|
|
||||||
|
|
||||||
action_ret = handle_IRQ_event(irq, action);
|
|
||||||
|
|
||||||
spin_lock(&desc->lock);
|
|
||||||
desc->status &= ~IRQ_INPROGRESS;
|
|
||||||
if (desc->status & IRQ_LEVEL)
|
|
||||||
desc->chip->ack(irq);
|
|
||||||
if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
|
|
||||||
desc->chip->unmask(irq);
|
|
||||||
out_unlock:
|
|
||||||
spin_unlock(&desc->lock);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int uic_host_map(struct irq_host *h, unsigned int virq,
|
static int uic_host_map(struct irq_host *h, unsigned int virq,
|
||||||
irq_hw_number_t hw)
|
irq_hw_number_t hw)
|
||||||
{
|
{
|
||||||
|
@ -239,7 +196,7 @@ static int uic_host_map(struct irq_host *h, unsigned int virq,
|
||||||
set_irq_chip_data(virq, uic);
|
set_irq_chip_data(virq, uic);
|
||||||
/* Despite the name, handle_level_irq() works for both level
|
/* Despite the name, handle_level_irq() works for both level
|
||||||
* and edge irqs on UIC. FIXME: check this is correct */
|
* and edge irqs on UIC. FIXME: check this is correct */
|
||||||
set_irq_chip_and_handler(virq, &uic_irq_chip, handle_uic_irq);
|
set_irq_chip_and_handler(virq, &uic_irq_chip, handle_level_irq);
|
||||||
|
|
||||||
/* Set default irq type */
|
/* Set default irq type */
|
||||||
set_irq_type(virq, IRQ_TYPE_NONE);
|
set_irq_type(virq, IRQ_TYPE_NONE);
|
||||||
|
|
Loading…
Reference in a new issue