1dbb15226SJiaxun Yang // SPDX-License-Identifier: GPL-2.0 2dbb15226SJiaxun Yang /* 3dbb15226SJiaxun Yang * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com> 4dbb15226SJiaxun Yang * Loongson Local IO Interrupt Controller support 5dbb15226SJiaxun Yang */ 6dbb15226SJiaxun Yang 7dbb15226SJiaxun Yang #include <linux/errno.h> 8dbb15226SJiaxun Yang #include <linux/init.h> 9dbb15226SJiaxun Yang #include <linux/types.h> 10dbb15226SJiaxun Yang #include <linux/interrupt.h> 11dbb15226SJiaxun Yang #include <linux/ioport.h> 12dbb15226SJiaxun Yang #include <linux/irqchip.h> 13dbb15226SJiaxun Yang #include <linux/of_address.h> 14dbb15226SJiaxun Yang #include <linux/of_irq.h> 15dbb15226SJiaxun Yang #include <linux/io.h> 16dbb15226SJiaxun Yang #include <linux/smp.h> 17dbb15226SJiaxun Yang #include <linux/irqchip/chained_irq.h> 18dbb15226SJiaxun Yang 19*76e0c88dSQing Zhang #include <loongson.h> 20dbb15226SJiaxun Yang 21dbb15226SJiaxun Yang #define LIOINTC_CHIP_IRQ 32 22dbb15226SJiaxun Yang #define LIOINTC_NUM_PARENT 4 23dbb15226SJiaxun Yang 24dbb15226SJiaxun Yang #define LIOINTC_INTC_CHIP_START 0x20 25dbb15226SJiaxun Yang 26dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20) 27dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04) 28dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08) 29dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c) 30dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10) 31dbb15226SJiaxun Yang #define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14) 32dbb15226SJiaxun Yang 33dbb15226SJiaxun Yang #define LIOINTC_SHIFT_INTx 4 34dbb15226SJiaxun Yang 35be09ef09SJiaxun Yang #define LIOINTC_ERRATA_IRQ 10 36be09ef09SJiaxun Yang 37dbb15226SJiaxun Yang struct liointc_handler_data { 38dbb15226SJiaxun Yang struct liointc_priv *priv; 39dbb15226SJiaxun Yang u32 parent_int_map; 40dbb15226SJiaxun Yang }; 41dbb15226SJiaxun Yang 42dbb15226SJiaxun Yang struct liointc_priv { 43dbb15226SJiaxun Yang struct irq_chip_generic *gc; 44dbb15226SJiaxun Yang struct liointc_handler_data handler[LIOINTC_NUM_PARENT]; 45dbb15226SJiaxun Yang u8 map_cache[LIOINTC_CHIP_IRQ]; 46be09ef09SJiaxun Yang bool has_lpc_irq_errata; 47dbb15226SJiaxun Yang }; 48dbb15226SJiaxun Yang 49dbb15226SJiaxun Yang static void liointc_chained_handle_irq(struct irq_desc *desc) 50dbb15226SJiaxun Yang { 51dbb15226SJiaxun Yang struct liointc_handler_data *handler = irq_desc_get_handler_data(desc); 52dbb15226SJiaxun Yang struct irq_chip *chip = irq_desc_get_chip(desc); 53dbb15226SJiaxun Yang struct irq_chip_generic *gc = handler->priv->gc; 54dbb15226SJiaxun Yang u32 pending; 55dbb15226SJiaxun Yang 56dbb15226SJiaxun Yang chained_irq_enter(chip, desc); 57dbb15226SJiaxun Yang 58dbb15226SJiaxun Yang pending = readl(gc->reg_base + LIOINTC_REG_INTC_STATUS); 59dbb15226SJiaxun Yang 60be09ef09SJiaxun Yang if (!pending) { 61be09ef09SJiaxun Yang /* Always blame LPC IRQ if we have that bug */ 62be09ef09SJiaxun Yang if (handler->priv->has_lpc_irq_errata && 63c9c73a05SHuacai Chen (handler->parent_int_map & gc->mask_cache & 64be09ef09SJiaxun Yang BIT(LIOINTC_ERRATA_IRQ))) 65be09ef09SJiaxun Yang pending = BIT(LIOINTC_ERRATA_IRQ); 66be09ef09SJiaxun Yang else 67dbb15226SJiaxun Yang spurious_interrupt(); 68be09ef09SJiaxun Yang } 69dbb15226SJiaxun Yang 70dbb15226SJiaxun Yang while (pending) { 71dbb15226SJiaxun Yang int bit = __ffs(pending); 72dbb15226SJiaxun Yang 73dbb15226SJiaxun Yang generic_handle_irq(irq_find_mapping(gc->domain, bit)); 74dbb15226SJiaxun Yang pending &= ~BIT(bit); 75dbb15226SJiaxun Yang } 76dbb15226SJiaxun Yang 77dbb15226SJiaxun Yang chained_irq_exit(chip, desc); 78dbb15226SJiaxun Yang } 79dbb15226SJiaxun Yang 80dbb15226SJiaxun Yang static void liointc_set_bit(struct irq_chip_generic *gc, 81dbb15226SJiaxun Yang unsigned int offset, 82dbb15226SJiaxun Yang u32 mask, bool set) 83dbb15226SJiaxun Yang { 84dbb15226SJiaxun Yang if (set) 85dbb15226SJiaxun Yang writel(readl(gc->reg_base + offset) | mask, 86dbb15226SJiaxun Yang gc->reg_base + offset); 87dbb15226SJiaxun Yang else 88dbb15226SJiaxun Yang writel(readl(gc->reg_base + offset) & ~mask, 89dbb15226SJiaxun Yang gc->reg_base + offset); 90dbb15226SJiaxun Yang } 91dbb15226SJiaxun Yang 92dbb15226SJiaxun Yang static int liointc_set_type(struct irq_data *data, unsigned int type) 93dbb15226SJiaxun Yang { 94dbb15226SJiaxun Yang struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 95dbb15226SJiaxun Yang u32 mask = data->mask; 96dbb15226SJiaxun Yang unsigned long flags; 97dbb15226SJiaxun Yang 98dbb15226SJiaxun Yang irq_gc_lock_irqsave(gc, flags); 99dbb15226SJiaxun Yang switch (type) { 100dbb15226SJiaxun Yang case IRQ_TYPE_LEVEL_HIGH: 101dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); 102dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); 103dbb15226SJiaxun Yang break; 104dbb15226SJiaxun Yang case IRQ_TYPE_LEVEL_LOW: 105dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); 106dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); 107dbb15226SJiaxun Yang break; 108dbb15226SJiaxun Yang case IRQ_TYPE_EDGE_RISING: 109dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); 110dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); 111dbb15226SJiaxun Yang break; 112dbb15226SJiaxun Yang case IRQ_TYPE_EDGE_FALLING: 113dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); 114dbb15226SJiaxun Yang liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); 115dbb15226SJiaxun Yang break; 116dbb15226SJiaxun Yang default: 117fa03587cSTiezhu Yang irq_gc_unlock_irqrestore(gc, flags); 118dbb15226SJiaxun Yang return -EINVAL; 119dbb15226SJiaxun Yang } 120dbb15226SJiaxun Yang irq_gc_unlock_irqrestore(gc, flags); 121dbb15226SJiaxun Yang 122dbb15226SJiaxun Yang irqd_set_trigger_type(data, type); 123dbb15226SJiaxun Yang return 0; 124dbb15226SJiaxun Yang } 125dbb15226SJiaxun Yang 126dbb15226SJiaxun Yang static void liointc_resume(struct irq_chip_generic *gc) 127dbb15226SJiaxun Yang { 128dbb15226SJiaxun Yang struct liointc_priv *priv = gc->private; 129dbb15226SJiaxun Yang unsigned long flags; 130dbb15226SJiaxun Yang int i; 131dbb15226SJiaxun Yang 132dbb15226SJiaxun Yang irq_gc_lock_irqsave(gc, flags); 133dbb15226SJiaxun Yang /* Disable all at first */ 134dbb15226SJiaxun Yang writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE); 135c9c73a05SHuacai Chen /* Restore map cache */ 136dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_CHIP_IRQ; i++) 137dbb15226SJiaxun Yang writeb(priv->map_cache[i], gc->reg_base + i); 138c9c73a05SHuacai Chen /* Restore mask cache */ 139c9c73a05SHuacai Chen writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE); 140dbb15226SJiaxun Yang irq_gc_unlock_irqrestore(gc, flags); 141dbb15226SJiaxun Yang } 142dbb15226SJiaxun Yang 143dbb15226SJiaxun Yang static const char * const parent_names[] = {"int0", "int1", "int2", "int3"}; 144dbb15226SJiaxun Yang 1454cc99d03SHuacai Chen static int __init liointc_of_init(struct device_node *node, 146dbb15226SJiaxun Yang struct device_node *parent) 147dbb15226SJiaxun Yang { 148dbb15226SJiaxun Yang struct irq_chip_generic *gc; 149dbb15226SJiaxun Yang struct irq_domain *domain; 150dbb15226SJiaxun Yang struct irq_chip_type *ct; 151dbb15226SJiaxun Yang struct liointc_priv *priv; 152dbb15226SJiaxun Yang void __iomem *base; 153dbb15226SJiaxun Yang u32 of_parent_int_map[LIOINTC_NUM_PARENT]; 154dbb15226SJiaxun Yang int parent_irq[LIOINTC_NUM_PARENT]; 155dbb15226SJiaxun Yang bool have_parent = FALSE; 156dbb15226SJiaxun Yang int sz, i, err = 0; 157dbb15226SJiaxun Yang 158dbb15226SJiaxun Yang priv = kzalloc(sizeof(*priv), GFP_KERNEL); 159dbb15226SJiaxun Yang if (!priv) 160dbb15226SJiaxun Yang return -ENOMEM; 161dbb15226SJiaxun Yang 162dbb15226SJiaxun Yang base = of_iomap(node, 0); 163dbb15226SJiaxun Yang if (!base) { 164dbb15226SJiaxun Yang err = -ENODEV; 165dbb15226SJiaxun Yang goto out_free_priv; 166dbb15226SJiaxun Yang } 167dbb15226SJiaxun Yang 168dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 169dbb15226SJiaxun Yang parent_irq[i] = of_irq_get_byname(node, parent_names[i]); 170dbb15226SJiaxun Yang if (parent_irq[i] > 0) 171dbb15226SJiaxun Yang have_parent = TRUE; 172dbb15226SJiaxun Yang } 173dbb15226SJiaxun Yang if (!have_parent) { 174dbb15226SJiaxun Yang err = -ENODEV; 175dbb15226SJiaxun Yang goto out_iounmap; 176dbb15226SJiaxun Yang } 177dbb15226SJiaxun Yang 178dbb15226SJiaxun Yang sz = of_property_read_variable_u32_array(node, 179dbb15226SJiaxun Yang "loongson,parent_int_map", 180dbb15226SJiaxun Yang &of_parent_int_map[0], 181dbb15226SJiaxun Yang LIOINTC_NUM_PARENT, 182dbb15226SJiaxun Yang LIOINTC_NUM_PARENT); 183dbb15226SJiaxun Yang if (sz < 4) { 184dbb15226SJiaxun Yang pr_err("loongson-liointc: No parent_int_map\n"); 185dbb15226SJiaxun Yang err = -ENODEV; 186dbb15226SJiaxun Yang goto out_iounmap; 187dbb15226SJiaxun Yang } 188dbb15226SJiaxun Yang 189dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_NUM_PARENT; i++) 190dbb15226SJiaxun Yang priv->handler[i].parent_int_map = of_parent_int_map[i]; 191dbb15226SJiaxun Yang 192dbb15226SJiaxun Yang /* Setup IRQ domain */ 193dbb15226SJiaxun Yang domain = irq_domain_add_linear(node, 32, 194dbb15226SJiaxun Yang &irq_generic_chip_ops, priv); 195dbb15226SJiaxun Yang if (!domain) { 196dbb15226SJiaxun Yang pr_err("loongson-liointc: cannot add IRQ domain\n"); 197dbb15226SJiaxun Yang err = -EINVAL; 198dbb15226SJiaxun Yang goto out_iounmap; 199dbb15226SJiaxun Yang } 200dbb15226SJiaxun Yang 201dbb15226SJiaxun Yang err = irq_alloc_domain_generic_chips(domain, 32, 1, 202dbb15226SJiaxun Yang node->full_name, handle_level_irq, 203dbb15226SJiaxun Yang IRQ_NOPROBE, 0, 0); 204dbb15226SJiaxun Yang if (err) { 205dbb15226SJiaxun Yang pr_err("loongson-liointc: unable to register IRQ domain\n"); 206dbb15226SJiaxun Yang goto out_free_domain; 207dbb15226SJiaxun Yang } 208dbb15226SJiaxun Yang 209dbb15226SJiaxun Yang 210dbb15226SJiaxun Yang /* Disable all IRQs */ 211dbb15226SJiaxun Yang writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE); 212dbb15226SJiaxun Yang /* Set to level triggered */ 213dbb15226SJiaxun Yang writel(0x0, base + LIOINTC_REG_INTC_EDGE); 214dbb15226SJiaxun Yang 215dbb15226SJiaxun Yang /* Generate parent INT part of map cache */ 216dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 217dbb15226SJiaxun Yang u32 pending = priv->handler[i].parent_int_map; 218dbb15226SJiaxun Yang 219dbb15226SJiaxun Yang while (pending) { 220dbb15226SJiaxun Yang int bit = __ffs(pending); 221dbb15226SJiaxun Yang 222dbb15226SJiaxun Yang priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx; 223dbb15226SJiaxun Yang pending &= ~BIT(bit); 224dbb15226SJiaxun Yang } 225dbb15226SJiaxun Yang } 226dbb15226SJiaxun Yang 227dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_CHIP_IRQ; i++) { 228dbb15226SJiaxun Yang /* Generate core part of map cache */ 229dbb15226SJiaxun Yang priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id); 230dbb15226SJiaxun Yang writeb(priv->map_cache[i], base + i); 231dbb15226SJiaxun Yang } 232dbb15226SJiaxun Yang 233dbb15226SJiaxun Yang gc = irq_get_domain_generic_chip(domain, 0); 234dbb15226SJiaxun Yang gc->private = priv; 235dbb15226SJiaxun Yang gc->reg_base = base; 236dbb15226SJiaxun Yang gc->domain = domain; 237dbb15226SJiaxun Yang gc->resume = liointc_resume; 238dbb15226SJiaxun Yang 239dbb15226SJiaxun Yang ct = gc->chip_types; 240dbb15226SJiaxun Yang ct->regs.enable = LIOINTC_REG_INTC_ENABLE; 241dbb15226SJiaxun Yang ct->regs.disable = LIOINTC_REG_INTC_DISABLE; 242dbb15226SJiaxun Yang ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 243dbb15226SJiaxun Yang ct->chip.irq_mask = irq_gc_mask_disable_reg; 244dbb15226SJiaxun Yang ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; 245dbb15226SJiaxun Yang ct->chip.irq_set_type = liointc_set_type; 246dbb15226SJiaxun Yang 247c9c73a05SHuacai Chen gc->mask_cache = 0; 248dbb15226SJiaxun Yang priv->gc = gc; 249dbb15226SJiaxun Yang 250dbb15226SJiaxun Yang for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 251dbb15226SJiaxun Yang if (parent_irq[i] <= 0) 252dbb15226SJiaxun Yang continue; 253dbb15226SJiaxun Yang 254dbb15226SJiaxun Yang priv->handler[i].priv = priv; 255dbb15226SJiaxun Yang irq_set_chained_handler_and_data(parent_irq[i], 256dbb15226SJiaxun Yang liointc_chained_handle_irq, &priv->handler[i]); 257dbb15226SJiaxun Yang } 258dbb15226SJiaxun Yang 259dbb15226SJiaxun Yang return 0; 260dbb15226SJiaxun Yang 261dbb15226SJiaxun Yang out_free_domain: 262dbb15226SJiaxun Yang irq_domain_remove(domain); 263dbb15226SJiaxun Yang out_iounmap: 264dbb15226SJiaxun Yang iounmap(base); 265dbb15226SJiaxun Yang out_free_priv: 266dbb15226SJiaxun Yang kfree(priv); 267dbb15226SJiaxun Yang 268dbb15226SJiaxun Yang return err; 269dbb15226SJiaxun Yang } 270dbb15226SJiaxun Yang 271dbb15226SJiaxun Yang IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init); 272dbb15226SJiaxun Yang IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init); 273