162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Marvell Orion SoCs IRQ chip driver. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com> 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This file is licensed under the terms of the GNU General Public 762306a36Sopenharmony_ci * License version 2. This program is licensed "as is" without any 862306a36Sopenharmony_ci * warranty of any kind, whether express or implied. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/io.h> 1262306a36Sopenharmony_ci#include <linux/irq.h> 1362306a36Sopenharmony_ci#include <linux/irqchip.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/of_address.h> 1662306a36Sopenharmony_ci#include <linux/of_irq.h> 1762306a36Sopenharmony_ci#include <asm/exception.h> 1862306a36Sopenharmony_ci#include <asm/mach/irq.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci/* 2162306a36Sopenharmony_ci * Orion SoC main interrupt controller 2262306a36Sopenharmony_ci */ 2362306a36Sopenharmony_ci#define ORION_IRQS_PER_CHIP 32 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#define ORION_IRQ_CAUSE 0x00 2662306a36Sopenharmony_ci#define ORION_IRQ_MASK 0x04 2762306a36Sopenharmony_ci#define ORION_IRQ_FIQ_MASK 0x08 2862306a36Sopenharmony_ci#define ORION_IRQ_ENDP_MASK 0x0c 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic struct irq_domain *orion_irq_domain; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistatic void 3362306a36Sopenharmony_ci__exception_irq_entry orion_handle_irq(struct pt_regs *regs) 3462306a36Sopenharmony_ci{ 3562306a36Sopenharmony_ci struct irq_domain_chip_generic *dgc = orion_irq_domain->gc; 3662306a36Sopenharmony_ci int n, base = 0; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci for (n = 0; n < dgc->num_chips; n++, base += ORION_IRQS_PER_CHIP) { 3962306a36Sopenharmony_ci struct irq_chip_generic *gc = 4062306a36Sopenharmony_ci irq_get_domain_generic_chip(orion_irq_domain, base); 4162306a36Sopenharmony_ci u32 stat = readl_relaxed(gc->reg_base + ORION_IRQ_CAUSE) & 4262306a36Sopenharmony_ci gc->mask_cache; 4362306a36Sopenharmony_ci while (stat) { 4462306a36Sopenharmony_ci u32 hwirq = __fls(stat); 4562306a36Sopenharmony_ci generic_handle_domain_irq(orion_irq_domain, 4662306a36Sopenharmony_ci gc->irq_base + hwirq); 4762306a36Sopenharmony_ci stat &= ~(1 << hwirq); 4862306a36Sopenharmony_ci } 4962306a36Sopenharmony_ci } 5062306a36Sopenharmony_ci} 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic int __init orion_irq_init(struct device_node *np, 5362306a36Sopenharmony_ci struct device_node *parent) 5462306a36Sopenharmony_ci{ 5562306a36Sopenharmony_ci unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 5662306a36Sopenharmony_ci int n, ret, base, num_chips = 0; 5762306a36Sopenharmony_ci struct resource r; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci /* count number of irq chips by valid reg addresses */ 6062306a36Sopenharmony_ci num_chips = of_address_count(np); 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci orion_irq_domain = irq_domain_add_linear(np, 6362306a36Sopenharmony_ci num_chips * ORION_IRQS_PER_CHIP, 6462306a36Sopenharmony_ci &irq_generic_chip_ops, NULL); 6562306a36Sopenharmony_ci if (!orion_irq_domain) 6662306a36Sopenharmony_ci panic("%pOFn: unable to add irq domain\n", np); 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci ret = irq_alloc_domain_generic_chips(orion_irq_domain, 6962306a36Sopenharmony_ci ORION_IRQS_PER_CHIP, 1, np->full_name, 7062306a36Sopenharmony_ci handle_level_irq, clr, 0, 7162306a36Sopenharmony_ci IRQ_GC_INIT_MASK_CACHE); 7262306a36Sopenharmony_ci if (ret) 7362306a36Sopenharmony_ci panic("%pOFn: unable to alloc irq domain gc\n", np); 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci for (n = 0, base = 0; n < num_chips; n++, base += ORION_IRQS_PER_CHIP) { 7662306a36Sopenharmony_ci struct irq_chip_generic *gc = 7762306a36Sopenharmony_ci irq_get_domain_generic_chip(orion_irq_domain, base); 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci of_address_to_resource(np, n, &r); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci if (!request_mem_region(r.start, resource_size(&r), np->name)) 8262306a36Sopenharmony_ci panic("%pOFn: unable to request mem region %d", 8362306a36Sopenharmony_ci np, n); 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci gc->reg_base = ioremap(r.start, resource_size(&r)); 8662306a36Sopenharmony_ci if (!gc->reg_base) 8762306a36Sopenharmony_ci panic("%pOFn: unable to map resource %d", np, n); 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci gc->chip_types[0].regs.mask = ORION_IRQ_MASK; 9062306a36Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 9162306a36Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci /* mask all interrupts */ 9462306a36Sopenharmony_ci writel(0, gc->reg_base + ORION_IRQ_MASK); 9562306a36Sopenharmony_ci } 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci set_handle_irq(orion_handle_irq); 9862306a36Sopenharmony_ci return 0; 9962306a36Sopenharmony_ci} 10062306a36Sopenharmony_ciIRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Orion SoC bridge interrupt controller 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_ci#define ORION_BRIDGE_IRQ_CAUSE 0x00 10662306a36Sopenharmony_ci#define ORION_BRIDGE_IRQ_MASK 0x04 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic void orion_bridge_irq_handler(struct irq_desc *desc) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct irq_domain *d = irq_desc_get_handler_data(desc); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci struct irq_chip_generic *gc = irq_get_domain_generic_chip(d, 0); 11362306a36Sopenharmony_ci u32 stat = readl_relaxed(gc->reg_base + ORION_BRIDGE_IRQ_CAUSE) & 11462306a36Sopenharmony_ci gc->mask_cache; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci while (stat) { 11762306a36Sopenharmony_ci u32 hwirq = __fls(stat); 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci generic_handle_domain_irq(d, gc->irq_base + hwirq); 12062306a36Sopenharmony_ci stat &= ~(1 << hwirq); 12162306a36Sopenharmony_ci } 12262306a36Sopenharmony_ci} 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci/* 12562306a36Sopenharmony_ci * Bridge IRQ_CAUSE is asserted regardless of IRQ_MASK register. 12662306a36Sopenharmony_ci * To avoid interrupt events on stale irqs, we clear them before unmask. 12762306a36Sopenharmony_ci */ 12862306a36Sopenharmony_cistatic unsigned int orion_bridge_irq_startup(struct irq_data *d) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct irq_chip_type *ct = irq_data_get_chip_type(d); 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci ct->chip.irq_ack(d); 13362306a36Sopenharmony_ci ct->chip.irq_unmask(d); 13462306a36Sopenharmony_ci return 0; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_cistatic int __init orion_bridge_irq_init(struct device_node *np, 13862306a36Sopenharmony_ci struct device_node *parent) 13962306a36Sopenharmony_ci{ 14062306a36Sopenharmony_ci unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; 14162306a36Sopenharmony_ci struct resource r; 14262306a36Sopenharmony_ci struct irq_domain *domain; 14362306a36Sopenharmony_ci struct irq_chip_generic *gc; 14462306a36Sopenharmony_ci int ret, irq, nrirqs = 32; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci /* get optional number of interrupts provided */ 14762306a36Sopenharmony_ci of_property_read_u32(np, "marvell,#interrupts", &nrirqs); 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci domain = irq_domain_add_linear(np, nrirqs, 15062306a36Sopenharmony_ci &irq_generic_chip_ops, NULL); 15162306a36Sopenharmony_ci if (!domain) { 15262306a36Sopenharmony_ci pr_err("%pOFn: unable to add irq domain\n", np); 15362306a36Sopenharmony_ci return -ENOMEM; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name, 15762306a36Sopenharmony_ci handle_edge_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE); 15862306a36Sopenharmony_ci if (ret) { 15962306a36Sopenharmony_ci pr_err("%pOFn: unable to alloc irq domain gc\n", np); 16062306a36Sopenharmony_ci return ret; 16162306a36Sopenharmony_ci } 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci ret = of_address_to_resource(np, 0, &r); 16462306a36Sopenharmony_ci if (ret) { 16562306a36Sopenharmony_ci pr_err("%pOFn: unable to get resource\n", np); 16662306a36Sopenharmony_ci return ret; 16762306a36Sopenharmony_ci } 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci if (!request_mem_region(r.start, resource_size(&r), np->name)) { 17062306a36Sopenharmony_ci pr_err("%s: unable to request mem region\n", np->name); 17162306a36Sopenharmony_ci return -ENOMEM; 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Map the parent interrupt for the chained handler */ 17562306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, 0); 17662306a36Sopenharmony_ci if (irq <= 0) { 17762306a36Sopenharmony_ci pr_err("%pOFn: unable to parse irq\n", np); 17862306a36Sopenharmony_ci return -EINVAL; 17962306a36Sopenharmony_ci } 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, 0); 18262306a36Sopenharmony_ci gc->reg_base = ioremap(r.start, resource_size(&r)); 18362306a36Sopenharmony_ci if (!gc->reg_base) { 18462306a36Sopenharmony_ci pr_err("%pOFn: unable to map resource\n", np); 18562306a36Sopenharmony_ci return -ENOMEM; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE; 18962306a36Sopenharmony_ci gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK; 19062306a36Sopenharmony_ci gc->chip_types[0].chip.irq_startup = orion_bridge_irq_startup; 19162306a36Sopenharmony_ci gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit; 19262306a36Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 19362306a36Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* mask and clear all interrupts */ 19662306a36Sopenharmony_ci writel(0, gc->reg_base + ORION_BRIDGE_IRQ_MASK); 19762306a36Sopenharmony_ci writel(0, gc->reg_base + ORION_BRIDGE_IRQ_CAUSE); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci irq_set_chained_handler_and_data(irq, orion_bridge_irq_handler, 20062306a36Sopenharmony_ci domain); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ciIRQCHIP_DECLARE(orion_bridge_intc, 20562306a36Sopenharmony_ci "marvell,orion-bridge-intc", orion_bridge_irq_init); 206