19e543e22SJiaxun Yang // SPDX-License-Identifier: GPL-2.0 29e543e22SJiaxun Yang /* 39e543e22SJiaxun Yang * Copyright (C) 2019, Jiaxun Yang <jiaxun.yang@flygoat.com> 49e543e22SJiaxun Yang * Loongson-1 platform IRQ support 59e543e22SJiaxun Yang */ 69e543e22SJiaxun Yang 79e543e22SJiaxun Yang #include <linux/errno.h> 89e543e22SJiaxun Yang #include <linux/init.h> 99e543e22SJiaxun Yang #include <linux/types.h> 109e543e22SJiaxun Yang #include <linux/interrupt.h> 119e543e22SJiaxun Yang #include <linux/ioport.h> 129e543e22SJiaxun Yang #include <linux/irqchip.h> 139e543e22SJiaxun Yang #include <linux/of_address.h> 149e543e22SJiaxun Yang #include <linux/of_irq.h> 159e543e22SJiaxun Yang #include <linux/io.h> 169e543e22SJiaxun Yang #include <linux/irqchip/chained_irq.h> 179e543e22SJiaxun Yang 189e543e22SJiaxun Yang #define LS_REG_INTC_STATUS 0x00 199e543e22SJiaxun Yang #define LS_REG_INTC_EN 0x04 209e543e22SJiaxun Yang #define LS_REG_INTC_SET 0x08 219e543e22SJiaxun Yang #define LS_REG_INTC_CLR 0x0c 229e543e22SJiaxun Yang #define LS_REG_INTC_POL 0x10 239e543e22SJiaxun Yang #define LS_REG_INTC_EDGE 0x14 249e543e22SJiaxun Yang 259e543e22SJiaxun Yang /** 269e543e22SJiaxun Yang * struct ls1x_intc_priv - private ls1x-intc data. 279e543e22SJiaxun Yang * @domain: IRQ domain. 289e543e22SJiaxun Yang * @intc_base: IO Base of intc registers. 299e543e22SJiaxun Yang */ 309e543e22SJiaxun Yang 319e543e22SJiaxun Yang struct ls1x_intc_priv { 329e543e22SJiaxun Yang struct irq_domain *domain; 339e543e22SJiaxun Yang void __iomem *intc_base; 349e543e22SJiaxun Yang }; 359e543e22SJiaxun Yang 369e543e22SJiaxun Yang 379e543e22SJiaxun Yang static void ls1x_chained_handle_irq(struct irq_desc *desc) 389e543e22SJiaxun Yang { 399e543e22SJiaxun Yang struct ls1x_intc_priv *priv = irq_desc_get_handler_data(desc); 409e543e22SJiaxun Yang struct irq_chip *chip = irq_desc_get_chip(desc); 419e543e22SJiaxun Yang u32 pending; 429e543e22SJiaxun Yang 439e543e22SJiaxun Yang chained_irq_enter(chip, desc); 449e543e22SJiaxun Yang pending = readl(priv->intc_base + LS_REG_INTC_STATUS) & 459e543e22SJiaxun Yang readl(priv->intc_base + LS_REG_INTC_EN); 469e543e22SJiaxun Yang 479e543e22SJiaxun Yang if (!pending) 489e543e22SJiaxun Yang spurious_interrupt(); 499e543e22SJiaxun Yang 509e543e22SJiaxun Yang while (pending) { 519e543e22SJiaxun Yang int bit = __ffs(pending); 529e543e22SJiaxun Yang 53*046a6ee2SMarc Zyngier generic_handle_domain_irq(priv->domain, bit); 549e543e22SJiaxun Yang pending &= ~BIT(bit); 559e543e22SJiaxun Yang } 569e543e22SJiaxun Yang 579e543e22SJiaxun Yang chained_irq_exit(chip, desc); 589e543e22SJiaxun Yang } 599e543e22SJiaxun Yang 609e543e22SJiaxun Yang static void ls_intc_set_bit(struct irq_chip_generic *gc, 619e543e22SJiaxun Yang unsigned int offset, 629e543e22SJiaxun Yang u32 mask, bool set) 639e543e22SJiaxun Yang { 649e543e22SJiaxun Yang if (set) 659e543e22SJiaxun Yang writel(readl(gc->reg_base + offset) | mask, 669e543e22SJiaxun Yang gc->reg_base + offset); 679e543e22SJiaxun Yang else 689e543e22SJiaxun Yang writel(readl(gc->reg_base + offset) & ~mask, 699e543e22SJiaxun Yang gc->reg_base + offset); 709e543e22SJiaxun Yang } 719e543e22SJiaxun Yang 729e543e22SJiaxun Yang static int ls_intc_set_type(struct irq_data *data, unsigned int type) 739e543e22SJiaxun Yang { 749e543e22SJiaxun Yang struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 759e543e22SJiaxun Yang u32 mask = data->mask; 769e543e22SJiaxun Yang 779e543e22SJiaxun Yang switch (type) { 789e543e22SJiaxun Yang case IRQ_TYPE_LEVEL_HIGH: 799e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); 809e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); 819e543e22SJiaxun Yang break; 829e543e22SJiaxun Yang case IRQ_TYPE_LEVEL_LOW: 839e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, false); 849e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); 859e543e22SJiaxun Yang break; 869e543e22SJiaxun Yang case IRQ_TYPE_EDGE_RISING: 879e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); 889e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, true); 899e543e22SJiaxun Yang break; 909e543e22SJiaxun Yang case IRQ_TYPE_EDGE_FALLING: 919e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_EDGE, mask, true); 929e543e22SJiaxun Yang ls_intc_set_bit(gc, LS_REG_INTC_POL, mask, false); 939e543e22SJiaxun Yang break; 949e543e22SJiaxun Yang default: 959e543e22SJiaxun Yang return -EINVAL; 969e543e22SJiaxun Yang } 979e543e22SJiaxun Yang 989e543e22SJiaxun Yang irqd_set_trigger_type(data, type); 999e543e22SJiaxun Yang return irq_setup_alt_chip(data, type); 1009e543e22SJiaxun Yang } 1019e543e22SJiaxun Yang 1029e543e22SJiaxun Yang 1039e543e22SJiaxun Yang static int __init ls1x_intc_of_init(struct device_node *node, 1049e543e22SJiaxun Yang struct device_node *parent) 1059e543e22SJiaxun Yang { 1069e543e22SJiaxun Yang struct irq_chip_generic *gc; 1079e543e22SJiaxun Yang struct irq_chip_type *ct; 1089e543e22SJiaxun Yang struct ls1x_intc_priv *priv; 1099e543e22SJiaxun Yang int parent_irq, err = 0; 1109e543e22SJiaxun Yang 1119e543e22SJiaxun Yang priv = kzalloc(sizeof(*priv), GFP_KERNEL); 1129e543e22SJiaxun Yang if (!priv) 1139e543e22SJiaxun Yang return -ENOMEM; 1149e543e22SJiaxun Yang 1159e543e22SJiaxun Yang priv->intc_base = of_iomap(node, 0); 1169e543e22SJiaxun Yang if (!priv->intc_base) { 1179e543e22SJiaxun Yang err = -ENODEV; 1189e543e22SJiaxun Yang goto out_free_priv; 1199e543e22SJiaxun Yang } 1209e543e22SJiaxun Yang 1219e543e22SJiaxun Yang parent_irq = irq_of_parse_and_map(node, 0); 1229e543e22SJiaxun Yang if (!parent_irq) { 1239e543e22SJiaxun Yang pr_err("ls1x-irq: unable to get parent irq\n"); 1249e543e22SJiaxun Yang err = -ENODEV; 1259e543e22SJiaxun Yang goto out_iounmap; 1269e543e22SJiaxun Yang } 1279e543e22SJiaxun Yang 1289e543e22SJiaxun Yang /* Set up an IRQ domain */ 1299e543e22SJiaxun Yang priv->domain = irq_domain_add_linear(node, 32, &irq_generic_chip_ops, 1309e543e22SJiaxun Yang NULL); 1319e543e22SJiaxun Yang if (!priv->domain) { 1329e543e22SJiaxun Yang pr_err("ls1x-irq: cannot add IRQ domain\n"); 13395c5c618SDan Carpenter err = -ENOMEM; 1349e543e22SJiaxun Yang goto out_iounmap; 1359e543e22SJiaxun Yang } 1369e543e22SJiaxun Yang 1379e543e22SJiaxun Yang err = irq_alloc_domain_generic_chips(priv->domain, 32, 2, 1389e543e22SJiaxun Yang node->full_name, handle_level_irq, 1399e543e22SJiaxun Yang IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN, 0, 1409e543e22SJiaxun Yang IRQ_GC_INIT_MASK_CACHE); 1419e543e22SJiaxun Yang if (err) { 1429e543e22SJiaxun Yang pr_err("ls1x-irq: unable to register IRQ domain\n"); 1439e543e22SJiaxun Yang goto out_free_domain; 1449e543e22SJiaxun Yang } 1459e543e22SJiaxun Yang 1469e543e22SJiaxun Yang /* Mask all irqs */ 1479e543e22SJiaxun Yang writel(0x0, priv->intc_base + LS_REG_INTC_EN); 1489e543e22SJiaxun Yang 1499e543e22SJiaxun Yang /* Ack all irqs */ 1509e543e22SJiaxun Yang writel(0xffffffff, priv->intc_base + LS_REG_INTC_CLR); 1519e543e22SJiaxun Yang 1529e543e22SJiaxun Yang /* Set all irqs to high level triggered */ 1539e543e22SJiaxun Yang writel(0xffffffff, priv->intc_base + LS_REG_INTC_POL); 1549e543e22SJiaxun Yang 1559e543e22SJiaxun Yang gc = irq_get_domain_generic_chip(priv->domain, 0); 1569e543e22SJiaxun Yang 1579e543e22SJiaxun Yang gc->reg_base = priv->intc_base; 1589e543e22SJiaxun Yang 1599e543e22SJiaxun Yang ct = gc->chip_types; 1609e543e22SJiaxun Yang ct[0].type = IRQ_TYPE_LEVEL_MASK; 1619e543e22SJiaxun Yang ct[0].regs.mask = LS_REG_INTC_EN; 1629e543e22SJiaxun Yang ct[0].regs.ack = LS_REG_INTC_CLR; 1639e543e22SJiaxun Yang ct[0].chip.irq_unmask = irq_gc_mask_set_bit; 1649e543e22SJiaxun Yang ct[0].chip.irq_mask = irq_gc_mask_clr_bit; 1659e543e22SJiaxun Yang ct[0].chip.irq_ack = irq_gc_ack_set_bit; 1669e543e22SJiaxun Yang ct[0].chip.irq_set_type = ls_intc_set_type; 1679e543e22SJiaxun Yang ct[0].handler = handle_level_irq; 1689e543e22SJiaxun Yang 1699e543e22SJiaxun Yang ct[1].type = IRQ_TYPE_EDGE_BOTH; 1709e543e22SJiaxun Yang ct[1].regs.mask = LS_REG_INTC_EN; 1719e543e22SJiaxun Yang ct[1].regs.ack = LS_REG_INTC_CLR; 1729e543e22SJiaxun Yang ct[1].chip.irq_unmask = irq_gc_mask_set_bit; 1739e543e22SJiaxun Yang ct[1].chip.irq_mask = irq_gc_mask_clr_bit; 1749e543e22SJiaxun Yang ct[1].chip.irq_ack = irq_gc_ack_set_bit; 1759e543e22SJiaxun Yang ct[1].chip.irq_set_type = ls_intc_set_type; 1769e543e22SJiaxun Yang ct[1].handler = handle_edge_irq; 1779e543e22SJiaxun Yang 1789e543e22SJiaxun Yang irq_set_chained_handler_and_data(parent_irq, 1799e543e22SJiaxun Yang ls1x_chained_handle_irq, priv); 1809e543e22SJiaxun Yang 1819e543e22SJiaxun Yang return 0; 1829e543e22SJiaxun Yang 1839e543e22SJiaxun Yang out_free_domain: 1849e543e22SJiaxun Yang irq_domain_remove(priv->domain); 1859e543e22SJiaxun Yang out_iounmap: 1869e543e22SJiaxun Yang iounmap(priv->intc_base); 1879e543e22SJiaxun Yang out_free_priv: 1889e543e22SJiaxun Yang kfree(priv); 1899e543e22SJiaxun Yang 1909e543e22SJiaxun Yang return err; 1919e543e22SJiaxun Yang } 1929e543e22SJiaxun Yang 1939e543e22SJiaxun Yang IRQCHIP_DECLARE(ls1x_intc, "loongson,ls1x-intc", ls1x_intc_of_init); 194