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