162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 462306a36Sopenharmony_ci * Ingenic XBurst platform IRQ support 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <linux/errno.h> 862306a36Sopenharmony_ci#include <linux/init.h> 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/interrupt.h> 1162306a36Sopenharmony_ci#include <linux/ioport.h> 1262306a36Sopenharmony_ci#include <linux/irqchip.h> 1362306a36Sopenharmony_ci#include <linux/of_address.h> 1462306a36Sopenharmony_ci#include <linux/of_irq.h> 1562306a36Sopenharmony_ci#include <linux/timex.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/delay.h> 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#include <asm/io.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_cistruct ingenic_intc_data { 2262306a36Sopenharmony_ci void __iomem *base; 2362306a36Sopenharmony_ci struct irq_domain *domain; 2462306a36Sopenharmony_ci unsigned num_chips; 2562306a36Sopenharmony_ci}; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define JZ_REG_INTC_STATUS 0x00 2862306a36Sopenharmony_ci#define JZ_REG_INTC_MASK 0x04 2962306a36Sopenharmony_ci#define JZ_REG_INTC_SET_MASK 0x08 3062306a36Sopenharmony_ci#define JZ_REG_INTC_CLEAR_MASK 0x0c 3162306a36Sopenharmony_ci#define JZ_REG_INTC_PENDING 0x10 3262306a36Sopenharmony_ci#define CHIP_SIZE 0x20 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic irqreturn_t intc_cascade(int irq, void *data) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct ingenic_intc_data *intc = irq_get_handler_data(irq); 3762306a36Sopenharmony_ci struct irq_domain *domain = intc->domain; 3862306a36Sopenharmony_ci struct irq_chip_generic *gc; 3962306a36Sopenharmony_ci uint32_t pending; 4062306a36Sopenharmony_ci unsigned i; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci for (i = 0; i < intc->num_chips; i++) { 4362306a36Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, i * 32); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING); 4662306a36Sopenharmony_ci if (!pending) 4762306a36Sopenharmony_ci continue; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci while (pending) { 5062306a36Sopenharmony_ci int bit = __fls(pending); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci generic_handle_domain_irq(domain, bit + (i * 32)); 5362306a36Sopenharmony_ci pending &= ~BIT(bit); 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return IRQ_HANDLED; 5862306a36Sopenharmony_ci} 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic int __init ingenic_intc_of_init(struct device_node *node, 6162306a36Sopenharmony_ci unsigned num_chips) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci struct ingenic_intc_data *intc; 6462306a36Sopenharmony_ci struct irq_chip_generic *gc; 6562306a36Sopenharmony_ci struct irq_chip_type *ct; 6662306a36Sopenharmony_ci struct irq_domain *domain; 6762306a36Sopenharmony_ci int parent_irq, err = 0; 6862306a36Sopenharmony_ci unsigned i; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci intc = kzalloc(sizeof(*intc), GFP_KERNEL); 7162306a36Sopenharmony_ci if (!intc) { 7262306a36Sopenharmony_ci err = -ENOMEM; 7362306a36Sopenharmony_ci goto out_err; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci parent_irq = irq_of_parse_and_map(node, 0); 7762306a36Sopenharmony_ci if (!parent_irq) { 7862306a36Sopenharmony_ci err = -EINVAL; 7962306a36Sopenharmony_ci goto out_free; 8062306a36Sopenharmony_ci } 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci err = irq_set_handler_data(parent_irq, intc); 8362306a36Sopenharmony_ci if (err) 8462306a36Sopenharmony_ci goto out_unmap_irq; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci intc->num_chips = num_chips; 8762306a36Sopenharmony_ci intc->base = of_iomap(node, 0); 8862306a36Sopenharmony_ci if (!intc->base) { 8962306a36Sopenharmony_ci err = -ENODEV; 9062306a36Sopenharmony_ci goto out_unmap_irq; 9162306a36Sopenharmony_ci } 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci domain = irq_domain_add_linear(node, num_chips * 32, 9462306a36Sopenharmony_ci &irq_generic_chip_ops, NULL); 9562306a36Sopenharmony_ci if (!domain) { 9662306a36Sopenharmony_ci err = -ENOMEM; 9762306a36Sopenharmony_ci goto out_unmap_base; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci intc->domain = domain; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC", 10362306a36Sopenharmony_ci handle_level_irq, 0, 10462306a36Sopenharmony_ci IRQ_NOPROBE | IRQ_LEVEL, 0); 10562306a36Sopenharmony_ci if (err) 10662306a36Sopenharmony_ci goto out_domain_remove; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci for (i = 0; i < num_chips; i++) { 10962306a36Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, i * 32); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci gc->wake_enabled = IRQ_MSK(32); 11262306a36Sopenharmony_ci gc->reg_base = intc->base + (i * CHIP_SIZE); 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci ct = gc->chip_types; 11562306a36Sopenharmony_ci ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; 11662306a36Sopenharmony_ci ct->regs.disable = JZ_REG_INTC_SET_MASK; 11762306a36Sopenharmony_ci ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 11862306a36Sopenharmony_ci ct->chip.irq_mask = irq_gc_mask_disable_reg; 11962306a36Sopenharmony_ci ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; 12062306a36Sopenharmony_ci ct->chip.irq_set_wake = irq_gc_set_wake; 12162306a36Sopenharmony_ci ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci /* Mask all irqs */ 12462306a36Sopenharmony_ci irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK); 12562306a36Sopenharmony_ci } 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci if (request_irq(parent_irq, intc_cascade, IRQF_NO_SUSPEND, 12862306a36Sopenharmony_ci "SoC intc cascade interrupt", NULL)) 12962306a36Sopenharmony_ci pr_err("Failed to register SoC intc cascade interrupt\n"); 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ciout_domain_remove: 13362306a36Sopenharmony_ci irq_domain_remove(domain); 13462306a36Sopenharmony_ciout_unmap_base: 13562306a36Sopenharmony_ci iounmap(intc->base); 13662306a36Sopenharmony_ciout_unmap_irq: 13762306a36Sopenharmony_ci irq_dispose_mapping(parent_irq); 13862306a36Sopenharmony_ciout_free: 13962306a36Sopenharmony_ci kfree(intc); 14062306a36Sopenharmony_ciout_err: 14162306a36Sopenharmony_ci return err; 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic int __init intc_1chip_of_init(struct device_node *node, 14562306a36Sopenharmony_ci struct device_node *parent) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci return ingenic_intc_of_init(node, 1); 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init); 15062306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int __init intc_2chip_of_init(struct device_node *node, 15362306a36Sopenharmony_ci struct device_node *parent) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci return ingenic_intc_of_init(node, 2); 15662306a36Sopenharmony_ci} 15762306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init); 15862306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init); 15962306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init); 16062306a36Sopenharmony_ciIRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init); 161