18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com> 48c2ecf20Sopenharmony_ci * Loongson-1 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/io.h> 168c2ecf20Sopenharmony_ci#include <linux/irqchip/chained_irq.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define LS_REG_INTC_STATUS 0x00 198c2ecf20Sopenharmony_ci#define LS_REG_INTC_EN 0x04 208c2ecf20Sopenharmony_ci#define LS_REG_INTC_SET 0x08 218c2ecf20Sopenharmony_ci#define LS_REG_INTC_CLR 0x0c 228c2ecf20Sopenharmony_ci#define LS_REG_INTC_POL 0x10 238c2ecf20Sopenharmony_ci#define LS_REG_INTC_EDGE 0x14 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/** 268c2ecf20Sopenharmony_ci * struct ls1x_intc_priv - private ls1x-intc data. 278c2ecf20Sopenharmony_ci * @domain: IRQ domain. 288c2ecf20Sopenharmony_ci * @intc_base: IO Base of intc registers. 298c2ecf20Sopenharmony_ci */ 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct ls1x_intc_priv { 328c2ecf20Sopenharmony_ci struct irq_domain *domain; 338c2ecf20Sopenharmony_ci void __iomem *intc_base; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void ls1x_chained_handle_irq(struct irq_desc *desc) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc); 408c2ecf20Sopenharmony_ci struct irq_chip *chip = irq_desc_get_chip(desc); 418c2ecf20Sopenharmony_ci u32 pending; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci chained_irq_enter(chip, desc); 448c2ecf20Sopenharmony_ci pending = readl(priv->intc_base + LS_REG_INTC_STATUS) & 458c2ecf20Sopenharmony_ci readl(priv->intc_base + LS_REG_INTC_EN); 468c2ecf20Sopenharmony_ci 478c2ecf20Sopenharmony_ci if (!pending) 488c2ecf20Sopenharmony_ci spurious_interrupt(); 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ci while (pending) { 518c2ecf20Sopenharmony_ci int bit = __ffs(pending); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci generic_handle_irq(irq_find_mapping(priv->domain, bit)); 548c2ecf20Sopenharmony_ci pending &= ~BIT(bit); 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci chained_irq_exit(chip, desc); 588c2ecf20Sopenharmony_ci} 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistatic void ls_intc_set_bit(struct irq_chip_generic *gc, 618c2ecf20Sopenharmony_ci unsigned int offset, 628c2ecf20Sopenharmony_ci u32 mask, bool set) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci if (set) 658c2ecf20Sopenharmony_ci writel(readl(gc->reg_base + offset) | mask, 668c2ecf20Sopenharmony_ci gc->reg_base + offset); 678c2ecf20Sopenharmony_ci else 688c2ecf20Sopenharmony_ci writel(readl(gc->reg_base + offset) & ~mask, 698c2ecf20Sopenharmony_ci gc->reg_base + offset); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int ls_intc_set_type(struct irq_data *data, unsigned int type) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 758c2ecf20Sopenharmony_ci u32 mask = data->mask; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci switch (type) { 788c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_HIGH: 798c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); 808c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); 818c2ecf20Sopenharmony_ci break; 828c2ecf20Sopenharmony_ci case IRQ_TYPE_LEVEL_LOW: 838c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); 848c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); 858c2ecf20Sopenharmony_ci break; 868c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_RISING: 878c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); 888c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); 898c2ecf20Sopenharmony_ci break; 908c2ecf20Sopenharmony_ci case IRQ_TYPE_EDGE_FALLING: 918c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); 928c2ecf20Sopenharmony_ci ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); 938c2ecf20Sopenharmony_ci break; 948c2ecf20Sopenharmony_ci default: 958c2ecf20Sopenharmony_ci return -EINVAL; 968c2ecf20Sopenharmony_ci } 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci irqd_set_trigger_type(data, type); 998c2ecf20Sopenharmony_ci return irq_setup_alt_chip(data, type); 1008c2ecf20Sopenharmony_ci} 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int __init ls1x_intc_of_init(struct device_node *node, 1048c2ecf20Sopenharmony_ci struct device_node *parent) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct irq_chip_generic *gc; 1078c2ecf20Sopenharmony_ci struct irq_chip_type *ct; 1088c2ecf20Sopenharmony_ci struct ls1x_intc_priv *priv; 1098c2ecf20Sopenharmony_ci int parent_irq, err = 0; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1128c2ecf20Sopenharmony_ci if (!priv) 1138c2ecf20Sopenharmony_ci return -ENOMEM; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci priv->intc_base = of_iomap(node, 0); 1168c2ecf20Sopenharmony_ci if (!priv->intc_base) { 1178c2ecf20Sopenharmony_ci err = -ENODEV; 1188c2ecf20Sopenharmony_ci goto out_free_priv; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci parent_irq = irq_of_parse_and_map(node, 0); 1228c2ecf20Sopenharmony_ci if (!parent_irq) { 1238c2ecf20Sopenharmony_ci pr_err("ls1x-irq: unable to get parent irq\n"); 1248c2ecf20Sopenharmony_ci err = -ENODEV; 1258c2ecf20Sopenharmony_ci goto out_iounmap; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Set up an IRQ domain */ 1298c2ecf20Sopenharmony_ci priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops, 1308c2ecf20Sopenharmony_ci NULL); 1318c2ecf20Sopenharmony_ci if (!priv->domain) { 1328c2ecf20Sopenharmony_ci pr_err("ls1x-irq: cannot add IRQ domain\n"); 1338c2ecf20Sopenharmony_ci err = -ENOMEM; 1348c2ecf20Sopenharmony_ci goto out_iounmap; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci err = irq_alloc_domain_generic_chips(priv->domain, 32, 2, 1388c2ecf20Sopenharmony_ci node->full_name, handle_level_irq, 1398c2ecf20Sopenharmony_ci IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0, 1408c2ecf20Sopenharmony_ci IRQ_GC_INIT_MASK_CACHE); 1418c2ecf20Sopenharmony_ci if (err) { 1428c2ecf20Sopenharmony_ci pr_err("ls1x-irq: unable to register IRQ domain\n"); 1438c2ecf20Sopenharmony_ci goto out_free_domain; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* Mask all irqs */ 1478c2ecf20Sopenharmony_ci writel(0x0, priv->intc_base + LS_REG_INTC_EN); 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci /* Ack all irqs */ 1508c2ecf20Sopenharmony_ci writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci /* Set all irqs to high level triggered */ 1538c2ecf20Sopenharmony_ci writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci gc = irq_get_domain_generic_chip(priv->domain, 0); 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci gc->reg_base = priv->intc_base; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci ct = gc->chip_types; 1608c2ecf20Sopenharmony_ci ct[0].type = IRQ_TYPE_LEVEL_MASK; 1618c2ecf20Sopenharmony_ci ct[0].regs.mask = LS_REG_INTC_EN; 1628c2ecf20Sopenharmony_ci ct[0].regs.ack = LS_REG_INTC_CLR; 1638c2ecf20Sopenharmony_ci ct[0].chip.irq_unmask = irq_gc_mask_set_bit; 1648c2ecf20Sopenharmony_ci ct[0].chip.irq_mask = irq_gc_mask_clr_bit; 1658c2ecf20Sopenharmony_ci ct[0].chip.irq_ack = irq_gc_ack_set_bit; 1668c2ecf20Sopenharmony_ci ct[0].chip.irq_set_type = ls_intc_set_type; 1678c2ecf20Sopenharmony_ci ct[0].handler = handle_level_irq; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci ct[1].type = IRQ_TYPE_EDGE_BOTH; 1708c2ecf20Sopenharmony_ci ct[1].regs.mask = LS_REG_INTC_EN; 1718c2ecf20Sopenharmony_ci ct[1].regs.ack = LS_REG_INTC_CLR; 1728c2ecf20Sopenharmony_ci ct[1].chip.irq_unmask = irq_gc_mask_set_bit; 1738c2ecf20Sopenharmony_ci ct[1].chip.irq_mask = irq_gc_mask_clr_bit; 1748c2ecf20Sopenharmony_ci ct[1].chip.irq_ack = irq_gc_ack_set_bit; 1758c2ecf20Sopenharmony_ci ct[1].chip.irq_set_type = ls_intc_set_type; 1768c2ecf20Sopenharmony_ci ct[1].handler = handle_edge_irq; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci irq_set_chained_handler_and_data(parent_irq, 1798c2ecf20Sopenharmony_ci ls1x_chained_handle_irq, priv); 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci return 0; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ciout_free_domain: 1848c2ecf20Sopenharmony_ci irq_domain_remove(priv->domain); 1858c2ecf20Sopenharmony_ciout_iounmap: 1868c2ecf20Sopenharmony_ci iounmap(priv->intc_base); 1878c2ecf20Sopenharmony_ciout_free_priv: 1888c2ecf20Sopenharmony_ci kfree(priv); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci return err; 1918c2ecf20Sopenharmony_ci} 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ciIRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init); 194