diff --git a/drivers/irqchip/irq-vt8500.c b/drivers/irqchip/irq-vt8500.c index debca897c1ee..15134cb2fbe5 100644 --- a/drivers/irqchip/irq-vt8500.c +++ b/drivers/irqchip/irq-vt8500.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -66,6 +67,8 @@ struct vt8500_irq_data { /* Global variable for accessing io-mem addresses */ static struct vt8500_irq_data intc[VT8500_INTC_MAX]; static u32 active_cnt = 0; +/* Primary interrupt controller data */ +static struct vt8500_irq_data *primary_intc; static void vt8500_irq_ack(struct irq_data *d) { @@ -163,28 +166,38 @@ static const struct irq_domain_ops vt8500_irq_domain_ops = { .xlate = irq_domain_xlate_onecell, }; +static inline void vt8500_handle_irq_common(struct vt8500_irq_data *intc) +{ + unsigned long irqnr = readl_relaxed(intc->base) & 0x3F; + unsigned long stat; + + /* + * Highest Priority register default = 63, so check that this + * is a real interrupt by checking the status register + */ + if (irqnr == 63) { + stat = readl_relaxed(intc->base + VT8500_ICIS + 4); + if (!(stat & BIT(31))) + return; + } + + generic_handle_domain_irq(intc->domain, irqnr); +} + static void __exception_irq_entry vt8500_handle_irq(struct pt_regs *regs) { - u32 stat, i; - int irqnr; - void __iomem *base; + vt8500_handle_irq_common(primary_intc); +} - /* Loop through each active controller */ - for (i=0; ihost_data; - generic_handle_domain_irq(intc[i].domain, irqnr); - } + chained_irq_enter(chip, desc); + vt8500_handle_irq_common(intc); + chained_irq_exit(chip, desc); } static int __init vt8500_irq_init(struct device_node *node, @@ -212,8 +225,6 @@ static int __init vt8500_irq_init(struct device_node *node, goto out; } - set_handle_irq(vt8500_handle_irq); - vt8500_init_irq_hw(intc[active_cnt].base); pr_info("vt8500-irq: Added interrupt controller\n"); @@ -224,10 +235,14 @@ static int __init vt8500_irq_init(struct device_node *node, if (of_irq_count(node) != 0) { for (i = 0; i < of_irq_count(node); i++) { irq = irq_of_parse_and_map(node, i); - enable_irq(irq); + irq_set_chained_handler_and_data(irq, vt8500_handle_irq_chained, + &intc[active_cnt]); } pr_info("vt8500-irq: Enabled slave->parent interrupts\n"); + } else { + primary_intc = &intc[active_cnt]; + set_handle_irq(vt8500_handle_irq); } out: return 0;