diff options
-rw-r--r-- | drivers/irqchip/irq-vt8500.c | 59 |
1 files changed, 37 insertions, 22 deletions
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 <linux/io.h> #include <linux/irq.h> #include <linux/irqchip.h> +#include <linux/irqchip/chained_irq.h> #include <linux/irqdomain.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -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; - - /* Loop through each active controller */ - for (i=0; i<active_cnt; i++) { - base = intc[i].base; - irqnr = readl_relaxed(base) & 0x3F; - /* - Highest Priority register default = 63, so check that this - is a real interrupt by checking the status register - */ - if (irqnr == 63) { - stat = readl_relaxed(base + VT8500_ICIS + 4); - if (!(stat & BIT(31))) - continue; - } + vt8500_handle_irq_common(primary_intc); +} - generic_handle_domain_irq(intc[i].domain, irqnr); - } +static void vt8500_handle_irq_chained(struct irq_desc *desc) +{ + struct irq_domain *d = irq_desc_get_handler_data(desc); + struct irq_chip *chip = irq_desc_get_chip(desc); + struct vt8500_irq_data *intc = d->host_data; + + 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; |