18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de> 48c2ecf20Sopenharmony_ci * Ingenic XBurst platform IRQ support 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/errno.h> 88c2ecf20Sopenharmony_ci#include <linux/init.h> 98c2ecf20Sopenharmony_ci#include <linux/types.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/ioport.h> 128c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 138c2ecf20Sopenharmony_ci#include <linux/of_address.h> 148c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 158c2ecf20Sopenharmony_ci#include <linux/timex.h> 168c2ecf20Sopenharmony_ci#include <linux/slab.h> 178c2ecf20Sopenharmony_ci#include <linux/delay.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <asm/io.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_cistruct ingenic_intc_data { 228c2ecf20Sopenharmony_ci void __iomem *base; 238c2ecf20Sopenharmony_ci struct irq_domain *domain; 248c2ecf20Sopenharmony_ci unsigned num_chips; 258c2ecf20Sopenharmony_ci}; 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define JZ_REG_INTC_STATUS 0x00 288c2ecf20Sopenharmony_ci#define JZ_REG_INTC_MASK 0x04 298c2ecf20Sopenharmony_ci#define JZ_REG_INTC_SET_MASK 0x08 308c2ecf20Sopenharmony_ci#define JZ_REG_INTC_CLEAR_MASK 0x0c 318c2ecf20Sopenharmony_ci#define JZ_REG_INTC_PENDING 0x10 328c2ecf20Sopenharmony_ci#define CHIP_SIZE 0x20 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistatic irqreturn_t intc_cascade(int irq, void *data) 358c2ecf20Sopenharmony_ci{ 368c2ecf20Sopenharmony_ci struct ingenic_intc_data *intc = irq_get_handler_data(irq); 378c2ecf20Sopenharmony_ci struct irq_domain *domain = intc->domain; 388c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 398c2ecf20Sopenharmony_ci uint32_t pending; 408c2ecf20Sopenharmony_ci unsigned i; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci for (i = 0; i < intc->num_chips; i++) { 438c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, i * 32); 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_ci pending = irq_reg_readl(gc, JZ_REG_INTC_PENDING); 468c2ecf20Sopenharmony_ci if (!pending) 478c2ecf20Sopenharmony_ci continue; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci while (pending) { 508c2ecf20Sopenharmony_ci int bit = __fls(pending); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci irq = irq_linear_revmap(domain, bit + (i * 32)); 538c2ecf20Sopenharmony_ci generic_handle_irq(irq); 548c2ecf20Sopenharmony_ci pending &= ~BIT(bit); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci } 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci return IRQ_HANDLED; 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic int __init ingenic_intc_of_init(struct device_node *node, 628c2ecf20Sopenharmony_ci unsigned num_chips) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci struct ingenic_intc_data *intc; 658c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 668c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 678c2ecf20Sopenharmony_ci struct irq_domain *domain; 688c2ecf20Sopenharmony_ci int parent_irq, err = 0; 698c2ecf20Sopenharmony_ci unsigned i; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci intc = kzalloc(sizeof(*intc), GFP_KERNEL); 728c2ecf20Sopenharmony_ci if (!intc) { 738c2ecf20Sopenharmony_ci err = -ENOMEM; 748c2ecf20Sopenharmony_ci goto out_err; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci parent_irq = irq_of_parse_and_map(node, 0); 788c2ecf20Sopenharmony_ci if (!parent_irq) { 798c2ecf20Sopenharmony_ci err = -EINVAL; 808c2ecf20Sopenharmony_ci goto out_free; 818c2ecf20Sopenharmony_ci } 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci err = irq_set_handler_data(parent_irq, intc); 848c2ecf20Sopenharmony_ci if (err) 858c2ecf20Sopenharmony_ci goto out_unmap_irq; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci intc->num_chips = num_chips; 888c2ecf20Sopenharmony_ci intc->base = of_iomap(node, 0); 898c2ecf20Sopenharmony_ci if (!intc->base) { 908c2ecf20Sopenharmony_ci err = -ENODEV; 918c2ecf20Sopenharmony_ci goto out_unmap_irq; 928c2ecf20Sopenharmony_ci } 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci domain = irq_domain_add_linear(node, num_chips * 32, 958c2ecf20Sopenharmony_ci &irq_generic_chip_ops, NULL); 968c2ecf20Sopenharmony_ci if (!domain) { 978c2ecf20Sopenharmony_ci err = -ENOMEM; 988c2ecf20Sopenharmony_ci goto out_unmap_base; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci intc->domain = domain; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci err = irq_alloc_domain_generic_chips(domain, 32, 1, "INTC", 1048c2ecf20Sopenharmony_ci handle_level_irq, 0, 1058c2ecf20Sopenharmony_ci IRQ_NOPROBE | IRQ_LEVEL, 0); 1068c2ecf20Sopenharmony_ci if (err) 1078c2ecf20Sopenharmony_ci goto out_domain_remove; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci for (i = 0; i < num_chips; i++) { 1108c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(domain, i * 32); 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci gc->wake_enabled = IRQ_MSK(32); 1138c2ecf20Sopenharmony_ci gc->reg_base = intc->base + (i * CHIP_SIZE); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci ct = gc->chip_types; 1168c2ecf20Sopenharmony_ci ct->regs.enable = JZ_REG_INTC_CLEAR_MASK; 1178c2ecf20Sopenharmony_ci ct->regs.disable = JZ_REG_INTC_SET_MASK; 1188c2ecf20Sopenharmony_ci ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 1198c2ecf20Sopenharmony_ci ct->chip.irq_mask = irq_gc_mask_disable_reg; 1208c2ecf20Sopenharmony_ci ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; 1218c2ecf20Sopenharmony_ci ct->chip.irq_set_wake = irq_gc_set_wake; 1228c2ecf20Sopenharmony_ci ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci /* Mask all irqs */ 1258c2ecf20Sopenharmony_ci irq_reg_writel(gc, IRQ_MSK(32), JZ_REG_INTC_SET_MASK); 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (request_irq(parent_irq, intc_cascade, IRQF_NO_SUSPEND, 1298c2ecf20Sopenharmony_ci "SoC intc cascade interrupt", NULL)) 1308c2ecf20Sopenharmony_ci pr_err("Failed to register SoC intc cascade interrupt\n"); 1318c2ecf20Sopenharmony_ci return 0; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ciout_domain_remove: 1348c2ecf20Sopenharmony_ci irq_domain_remove(domain); 1358c2ecf20Sopenharmony_ciout_unmap_base: 1368c2ecf20Sopenharmony_ci iounmap(intc->base); 1378c2ecf20Sopenharmony_ciout_unmap_irq: 1388c2ecf20Sopenharmony_ci irq_dispose_mapping(parent_irq); 1398c2ecf20Sopenharmony_ciout_free: 1408c2ecf20Sopenharmony_ci kfree(intc); 1418c2ecf20Sopenharmony_ciout_err: 1428c2ecf20Sopenharmony_ci return err; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic int __init intc_1chip_of_init(struct device_node *node, 1468c2ecf20Sopenharmony_ci struct device_node *parent) 1478c2ecf20Sopenharmony_ci{ 1488c2ecf20Sopenharmony_ci return ingenic_intc_of_init(node, 1); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4740_intc, "ingenic,jz4740-intc", intc_1chip_of_init); 1518c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4725b_intc, "ingenic,jz4725b-intc", intc_1chip_of_init); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic int __init intc_2chip_of_init(struct device_node *node, 1548c2ecf20Sopenharmony_ci struct device_node *parent) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci return ingenic_intc_of_init(node, 2); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4760_intc, "ingenic,jz4760-intc", intc_2chip_of_init); 1598c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4770_intc, "ingenic,jz4770-intc", intc_2chip_of_init); 1608c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4775_intc, "ingenic,jz4775-intc", intc_2chip_of_init); 1618c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(jz4780_intc, "ingenic,jz4780-intc", intc_2chip_of_init); 162