diff options
Diffstat (limited to 'drivers/irqchip/irq-orion.c')
| -rw-r--r-- | drivers/irqchip/irq-orion.c | 77 |
1 files changed, 45 insertions, 32 deletions
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c index e51d40031884..dddbc05917c0 100644 --- a/drivers/irqchip/irq-orion.c +++ b/drivers/irqchip/irq-orion.c @@ -10,14 +10,13 @@ #include <linux/io.h> #include <linux/irq.h> +#include <linux/irqchip.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_irq.h> #include <asm/exception.h> #include <asm/mach/irq.h> -#include "irqchip.h" - /* * Orion SoC main interrupt controller */ @@ -30,7 +29,7 @@ static struct irq_domain *orion_irq_domain; -static asmlinkage void +static void __exception_irq_entry orion_handle_irq(struct pt_regs *regs) { struct irq_domain_chip_generic *dgc = orion_irq_domain->gc; @@ -42,10 +41,9 @@ __exception_irq_entry orion_handle_irq(struct pt_regs *regs) u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; - u32 irq = irq_find_mapping(orion_irq_domain, - gc->irq_base + hwirq); - handle_IRQ(irq, regs); + u32 hwirq = __fls(stat); + generic_handle_domain_irq(orion_irq_domain, + gc->irq_base + hwirq); stat &= ~(1 << hwirq); } } @@ -59,21 +57,20 @@ static int __init orion_irq_init(struct device_node *np, struct resource r; /* count number of irq chips by valid reg addresses */ - while (of_address_to_resource(np, num_chips, &r) == 0) - num_chips++; + num_chips = of_address_count(np); - orion_irq_domain = irq_domain_add_linear(np, + orion_irq_domain = irq_domain_create_linear(of_fwnode_handle(np), num_chips * ORION_IRQS_PER_CHIP, &irq_generic_chip_ops, NULL); if (!orion_irq_domain) - panic("%s: unable to add irq domain\n", np->name); + panic("%pOFn: unable to add irq domain\n", np); ret = irq_alloc_domain_generic_chips(orion_irq_domain, - ORION_IRQS_PER_CHIP, 1, np->name, + ORION_IRQS_PER_CHIP, 1, np->full_name, handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); if (ret) - panic("%s: unable to alloc irq domain gc\n", np->name); + panic("%pOFn: unable to alloc irq domain gc\n", np); for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) { struct irq_chip_generic *gc = @@ -82,12 +79,12 @@ static int __init orion_irq_init(struct device_node *np, of_address_to_resource(np, n, &r); if (!request_mem_region(r.start, resource_size(&r), np->name)) - panic("%s: unable to request mem region %d", - np->name, n); + panic("%pOFn: unable to request mem region %d", + np, n); gc->reg_base = ioremap(r.start, resource_size(&r)); if (!gc->reg_base) - panic("%s: unable to map resource %d", np->name, n); + panic("%pOFn: unable to map resource %d", np, n); gc->chip_types[0].regs.mask = ORION_IRQ_MASK; gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; @@ -108,21 +105,35 @@ IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init); #define ORION_BRIDGE_IRQ_CAUSE 0x00 #define ORION_BRIDGE_IRQ_MASK 0x04 -static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc) +static void orion_bridge_irq_handler(struct irq_desc *desc) { - struct irq_domain *d = irq_get_handler_data(irq); - struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, irq); + struct irq_domain *d = irq_desc_get_handler_data(desc); + + struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) & gc->mask_cache; while (stat) { - u32 hwirq = ffs(stat) - 1; + u32 hwirq = __fls(stat); - generic_handle_irq(irq_find_mapping(d, gc->irq_base + hwirq)); + generic_handle_domain_irq(d, gc->irq_base + hwirq); stat &= ~(1 << hwirq); } } +/* + * Bridge IRQ_CAUSE is asserted regardless of IRQ_MASK register. + * To avoid interrupt events on stale irqs, we clear them before unmask. + */ +static unsigned int orion_bridge_irq_startup(struct irq_data *d) +{ + struct irq_chip_type *ct = irq_data_get_chip_type(d); + + ct->chip.irq_ack(d); + ct->chip.irq_unmask(d); + return 0; +} + static int __init orion_bridge_irq_init(struct device_node *np, struct device_node *parent) { @@ -135,23 +146,23 @@ static int __init orion_bridge_irq_init(struct device_node *np, /* get optional number of interrupts provided */ of_property_read_u32(np, "marvell,#interrupts", &nrirqs); - domain = irq_domain_add_linear(np, nrirqs, - &irq_generic_chip_ops, NULL); + domain = irq_domain_create_linear(of_fwnode_handle(np), nrirqs, + &irq_generic_chip_ops, NULL); if (!domain) { - pr_err("%s: unable to add irq domain\n", np->name); + pr_err("%pOFn: unable to add irq domain\n", np); return -ENOMEM; } ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, - handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); + handle_edge_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); if (ret) { - pr_err("%s: unable to alloc irq domain gc\n", np->name); + pr_err("%pOFn: unable to alloc irq domain gc\n", np); return ret; } ret = of_address_to_resource(np, 0, &r); if (ret) { - pr_err("%s: unable to get resource\n", np->name); + pr_err("%pOFn: unable to get resource\n", np); return ret; } @@ -163,28 +174,30 @@ static int __init orion_bridge_irq_init(struct device_node *np, /* Map the parent interrupt for the chained handler */ irq = irq_of_parse_and_map(np, 0); if (irq <= 0) { - pr_err("%s: unable to parse irq\n", np->name); + pr_err("%pOFn: unable to parse irq\n", np); return -EINVAL; } gc = irq_get_domain_generic_chip(domain, 0); gc->reg_base = ioremap(r.start, resource_size(&r)); if (!gc->reg_base) { - pr_err("%s: unable to map resource\n", np->name); + pr_err("%pOFn: unable to map resource\n", np); return -ENOMEM; } gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE; gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK; + gc->chip_types[0].chip.irq_startup = orion_bridge_irq_startup; gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit; gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; - /* mask all interrupts */ + /* mask and clear all interrupts */ writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK); + writel(0, gc->reg_base + ORION_BRIDGE_IRQ_CAUSE); - irq_set_handler_data(irq, domain); - irq_set_chained_handler(irq, orion_bridge_irq_handler); + irq_set_chained_handler_and_data(irq, orion_bridge_irq_handler, + domain); return 0; } |
