diff --git a/drivers/irqchip/irq-gic-phytium-2500.c b/drivers/irqchip/irq-gic-phytium-2500.c index 1843c563f50b3b56f629a978d85055b89c973292..100817c9c4a29ee854ec7fffee7e3ab6f5198516 100644 --- a/drivers/irqchip/irq-gic-phytium-2500.c +++ b/drivers/irqchip/irq-gic-phytium-2500.c @@ -1526,7 +1526,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, if (!gic_dist_supports_lpis()) return -EPERM; irq_domain_set_info(d, irq, hw, chip, d->host_data, - handle_fasteoi_irq, NULL, NULL); + handle_fasteoi_edge_irq, NULL, NULL); break; default: diff --git a/drivers/irqchip/irq-gic-v3.c b/drivers/irqchip/irq-gic-v3.c index 8b2a45fbc9f7ac7086b1ae2814e4ab1bd96f0e93..9bf324aeabc7051c74166b09f46d63f0c077b3ae 100644 --- a/drivers/irqchip/irq-gic-v3.c +++ b/drivers/irqchip/irq-gic-v3.c @@ -1475,7 +1475,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq, if (!gic_dist_supports_lpis()) return -EPERM; irq_domain_set_info(d, irq, hw, chip, d->host_data, - handle_fasteoi_irq, NULL, NULL); + handle_fasteoi_edge_irq, NULL, NULL); break; default: diff --git a/include/linux/irq.h b/include/linux/irq.h index 1108e9cd52a1a48156254159e681cc4e86cb59a5..1d26052347b9f8005b213fe14e6a88f86c4abbdf 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -654,6 +654,7 @@ static inline int irq_set_parent(int irq, int parent_irq) */ extern void handle_level_irq(struct irq_desc *desc); extern void handle_fasteoi_irq(struct irq_desc *desc); +extern void handle_fasteoi_edge_irq(struct irq_desc *desc); extern void handle_percpu_devid_fasteoi_ipi(struct irq_desc *desc); extern void handle_edge_irq(struct irq_desc *desc); extern void handle_edge_eoi_irq(struct irq_desc *desc); diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index e7d284261d450f38909165ad9fc3cf48dfc0ef9e..999d215a22d8a9d97477b308ab7819b1e6771bd2 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -680,6 +680,73 @@ static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip) } } +/** + * handle_fasteoi_edge_irq - irq handler for transparent controllers + * edge type IRQ. + * @desc: the interrupt description structure for this irq + */ +void handle_fasteoi_edge_irq(struct irq_desc *desc) +{ + struct irq_chip *chip = desc->irq_data.chip; + + raw_spin_lock(&desc->lock); + + if (!irq_may_run(desc)) { + desc->istate |= IRQS_PENDING; + mask_irq(desc); + goto out; + } + + desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING); + + /* + * If its disabled or no action available + * then mask it and get out of here: + */ + if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) { + desc->istate |= IRQS_PENDING; + mask_irq(desc); + goto out; + } + + kstat_incr_irqs_this_cpu(desc); + + if (desc->istate & IRQS_ONESHOT) + mask_irq(desc); + + do { + if (unlikely(!desc->action)) { + mask_irq(desc); + goto out; + } + + /* + * When another irq arrived while we were handling + * one, we could have masked the irq. + * Reenable it, if it was not disabled in meantime. + */ + if (unlikely(desc->istate & IRQS_PENDING)) { + if (!irqd_irq_disabled(&desc->irq_data) && + irqd_irq_masked(&desc->irq_data)) + unmask_irq(desc); + } + + handle_irq_event(desc); + + } while ((desc->istate & IRQS_PENDING) && + !irqd_irq_disabled(&desc->irq_data)); + + cond_unmask_eoi_irq(desc, chip); + + raw_spin_unlock(&desc->lock); + return; +out: + if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED)) + chip->irq_eoi(&desc->irq_data); + raw_spin_unlock(&desc->lock); +} +EXPORT_SYMBOL_GPL(handle_fasteoi_edge_irq); + /** * handle_fasteoi_irq - irq handler for transparent controllers * @desc: the interrupt description structure for this irq