18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Abilis Systems interrupt controller driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) Abilis Systems 2012 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author: Christian Ruppert <christian.ruppert@abilis.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/irqdomain.h> 128c2ecf20Sopenharmony_ci#include <linux/irq.h> 138c2ecf20Sopenharmony_ci#include <linux/irqchip.h> 148c2ecf20Sopenharmony_ci#include <linux/of_irq.h> 158c2ecf20Sopenharmony_ci#include <linux/of_address.h> 168c2ecf20Sopenharmony_ci#include <linux/of_platform.h> 178c2ecf20Sopenharmony_ci#include <linux/io.h> 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include <linux/bitops.h> 208c2ecf20Sopenharmony_ci 218c2ecf20Sopenharmony_ci#define AB_IRQCTL_INT_ENABLE 0x00 228c2ecf20Sopenharmony_ci#define AB_IRQCTL_INT_STATUS 0x04 238c2ecf20Sopenharmony_ci#define AB_IRQCTL_SRC_MODE 0x08 248c2ecf20Sopenharmony_ci#define AB_IRQCTL_SRC_POLARITY 0x0C 258c2ecf20Sopenharmony_ci#define AB_IRQCTL_INT_MODE 0x10 268c2ecf20Sopenharmony_ci#define AB_IRQCTL_INT_POLARITY 0x14 278c2ecf20Sopenharmony_ci#define AB_IRQCTL_INT_FORCE 0x18 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_ci#define AB_IRQCTL_MAXIRQ 32 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic inline void ab_irqctl_writereg(struct irq_chip_generic *gc, u32 reg, 328c2ecf20Sopenharmony_ci u32 val) 338c2ecf20Sopenharmony_ci{ 348c2ecf20Sopenharmony_ci irq_reg_writel(gc, val, reg); 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic inline u32 ab_irqctl_readreg(struct irq_chip_generic *gc, u32 reg) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci return irq_reg_readl(gc, reg); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic int tb10x_irq_set_type(struct irq_data *data, unsigned int flow_type) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 458c2ecf20Sopenharmony_ci uint32_t im, mod, pol; 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci im = data->mask; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci irq_gc_lock(gc); 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci mod = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_MODE) | im; 528c2ecf20Sopenharmony_ci pol = ab_irqctl_readreg(gc, AB_IRQCTL_SRC_POLARITY) | im; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci switch (flow_type & IRQF_TRIGGER_MASK) { 558c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 568c2ecf20Sopenharmony_ci pol ^= im; 578c2ecf20Sopenharmony_ci break; 588c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 598c2ecf20Sopenharmony_ci mod ^= im; 608c2ecf20Sopenharmony_ci break; 618c2ecf20Sopenharmony_ci case IRQ_TYPE_NONE: 628c2ecf20Sopenharmony_ci flow_type = IRQ_TYPE_LEVEL_LOW; 638c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 648c2ecf20Sopenharmony_ci mod ^= im; 658c2ecf20Sopenharmony_ci pol ^= im; 668c2ecf20Sopenharmony_ci break; 678c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 688c2ecf20Sopenharmony_ci break; 698c2ecf20Sopenharmony_ci default: 708c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 718c2ecf20Sopenharmony_ci pr_err("%s: Cannot assign multiple trigger modes to IRQ %d.\n", 728c2ecf20Sopenharmony_ci __func__, data->irq); 738c2ecf20Sopenharmony_ci return -EBADR; 748c2ecf20Sopenharmony_ci } 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci irqd_set_trigger_type(data, flow_type); 778c2ecf20Sopenharmony_ci irq_setup_alt_chip(data, flow_type); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_SRC_MODE, mod); 808c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_SRC_POLARITY, pol); 818c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, im); 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci irq_gc_unlock(gc); 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci return IRQ_SET_MASK_OK; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void tb10x_irq_cascade(struct irq_desc *desc) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct irq_domain *domain = irq_desc_get_handler_data(desc); 918c2ecf20Sopenharmony_ci unsigned int irq = irq_desc_get_irq(desc); 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(domain, irq)); 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic int __init of_tb10x_init_irq(struct device_node *ictl, 978c2ecf20Sopenharmony_ci struct device_node *parent) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci int i, ret, nrirqs = of_irq_count(ictl); 1008c2ecf20Sopenharmony_ci struct resource mem; 1018c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 1028c2ecf20Sopenharmony_ci struct irq_domain *domain; 1038c2ecf20Sopenharmony_ci void __iomem *reg_base; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci if (of_address_to_resource(ictl, 0, &mem)) { 1068c2ecf20Sopenharmony_ci pr_err("%pOFn: No registers declared in DeviceTree.\n", 1078c2ecf20Sopenharmony_ci ictl); 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci if (!request_mem_region(mem.start, resource_size(&mem), 1128c2ecf20Sopenharmony_ci ictl->full_name)) { 1138c2ecf20Sopenharmony_ci pr_err("%pOFn: Request mem region failed.\n", ictl); 1148c2ecf20Sopenharmony_ci return -EBUSY; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci reg_base = ioremap(mem.start, resource_size(&mem)); 1188c2ecf20Sopenharmony_ci if (!reg_base) { 1198c2ecf20Sopenharmony_ci ret = -EBUSY; 1208c2ecf20Sopenharmony_ci pr_err("%pOFn: ioremap failed.\n", ictl); 1218c2ecf20Sopenharmony_ci goto ioremap_fail; 1228c2ecf20Sopenharmony_ci } 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci domain = irq_domain_add_linear(ictl, AB_IRQCTL_MAXIRQ, 1258c2ecf20Sopenharmony_ci &irq_generic_chip_ops, NULL); 1268c2ecf20Sopenharmony_ci if (!domain) { 1278c2ecf20Sopenharmony_ci ret = -ENOMEM; 1288c2ecf20Sopenharmony_ci pr_err("%pOFn: Could not register interrupt domain.\n", 1298c2ecf20Sopenharmony_ci ictl); 1308c2ecf20Sopenharmony_ci goto irq_domain_add_fail; 1318c2ecf20Sopenharmony_ci } 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci ret = irq_alloc_domain_generic_chips(domain, AB_IRQCTL_MAXIRQ, 1348c2ecf20Sopenharmony_ci 2, ictl->name, handle_level_irq, 1358c2ecf20Sopenharmony_ci IRQ_NOREQUEST, IRQ_NOPROBE, 1368c2ecf20Sopenharmony_ci IRQ_GC_INIT_MASK_CACHE); 1378c2ecf20Sopenharmony_ci if (ret) { 1388c2ecf20Sopenharmony_ci pr_err("%pOFn: Could not allocate generic interrupt chip.\n", 1398c2ecf20Sopenharmony_ci ictl); 1408c2ecf20Sopenharmony_ci goto gc_alloc_fail; 1418c2ecf20Sopenharmony_ci } 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_ci gc = domain->gc->gc[0]; 1448c2ecf20Sopenharmony_ci gc->reg_base = reg_base; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; 1478c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; 1488c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; 1498c2ecf20Sopenharmony_ci gc->chip_types[0].chip.irq_set_type = tb10x_irq_set_type; 1508c2ecf20Sopenharmony_ci gc->chip_types[0].regs.mask = AB_IRQCTL_INT_ENABLE; 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; 1538c2ecf20Sopenharmony_ci gc->chip_types[1].chip.name = gc->chip_types[0].chip.name; 1548c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; 1558c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; 1568c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; 1578c2ecf20Sopenharmony_ci gc->chip_types[1].chip.irq_set_type = tb10x_irq_set_type; 1588c2ecf20Sopenharmony_ci gc->chip_types[1].regs.ack = AB_IRQCTL_INT_STATUS; 1598c2ecf20Sopenharmony_ci gc->chip_types[1].regs.mask = AB_IRQCTL_INT_ENABLE; 1608c2ecf20Sopenharmony_ci gc->chip_types[1].handler = handle_edge_irq; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci for (i = 0; i < nrirqs; i++) { 1638c2ecf20Sopenharmony_ci unsigned int irq = irq_of_parse_and_map(ictl, i); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(irq, tb10x_irq_cascade, 1668c2ecf20Sopenharmony_ci domain); 1678c2ecf20Sopenharmony_ci } 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_INT_ENABLE, 0); 1708c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_INT_MODE, 0); 1718c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_INT_POLARITY, 0); 1728c2ecf20Sopenharmony_ci ab_irqctl_writereg(gc, AB_IRQCTL_INT_STATUS, ~0UL); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return 0; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cigc_alloc_fail: 1778c2ecf20Sopenharmony_ci irq_domain_remove(domain); 1788c2ecf20Sopenharmony_ciirq_domain_add_fail: 1798c2ecf20Sopenharmony_ci iounmap(reg_base); 1808c2ecf20Sopenharmony_ciioremap_fail: 1818c2ecf20Sopenharmony_ci release_mem_region(mem.start, resource_size(&mem)); 1828c2ecf20Sopenharmony_ci return ret; 1838c2ecf20Sopenharmony_ci} 1848c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(tb10x_intc, "abilis,tb10x-ictl", of_tb10x_init_irq); 185