1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com> 4 * Jianmin Lv <lvjianmin@loongson.cn> 5 * Loongson Local IO Interrupt Controller support 6 */ 7 8#include <linux/errno.h> 9#include <linux/init.h> 10#include <linux/types.h> 11#include <linux/interrupt.h> 12#include <linux/ioport.h> 13#include <linux/irqchip.h> 14#include <linux/of_address.h> 15#include <linux/of_irq.h> 16#include <linux/io.h> 17#include <linux/smp.h> 18#include <linux/irqchip/chained_irq.h> 19 20#include <loongson.h> 21 22#define LIOINTC_CHIP_IRQ 32 23#define LIOINTC_NUM_PARENT 4 24 25#define LIOINTC_INTC_CHIP_START 0x20 26#define LIOINTC_MEM_SIZE 0x80 27#define LIOINTC_REG_INTC_STATUS (LIOINTC_INTC_CHIP_START + 0x20) 28#define LIOINTC_REG_INTC_EN_STATUS (LIOINTC_INTC_CHIP_START + 0x04) 29#define LIOINTC_REG_INTC_ENABLE (LIOINTC_INTC_CHIP_START + 0x08) 30#define LIOINTC_REG_INTC_DISABLE (LIOINTC_INTC_CHIP_START + 0x0c) 31#define LIOINTC_REG_INTC_POL (LIOINTC_INTC_CHIP_START + 0x10) 32#define LIOINTC_REG_INTC_EDGE (LIOINTC_INTC_CHIP_START + 0x14) 33 34#define LIOINTC_SHIFT_INTx 4 35 36#define LIOINTC_ERRATA_IRQ 10 37 38struct liointc_handler_data { 39 struct liointc_priv *priv; 40 u32 parent_int_map; 41}; 42 43struct liointc_priv { 44 struct fwnode_handle *domain_handle; 45 struct irq_chip_generic *gc; 46 struct liointc_handler_data handler[LIOINTC_NUM_PARENT]; 47 u8 map_cache[LIOINTC_CHIP_IRQ]; 48 u32 int_pol; 49 u32 int_edge; 50 bool has_lpc_irq_errata; 51}; 52 53static void liointc_chained_handle_irq(struct irq_desc *desc) 54{ 55 struct liointc_handler_data *handler = irq_desc_get_handler_data(desc); 56 struct irq_chip *chip = irq_desc_get_chip(desc); 57 struct irq_chip_generic *gc = handler->priv->gc; 58 u32 pending, offset = cpu_logical_map(smp_processor_id()) * 8; 59 60 chained_irq_enter(chip, desc); 61 62 pending = readl(gc->reg_base + LIOINTC_REG_INTC_STATUS + offset); 63 64 if (!pending) { 65 /* Always blame LPC IRQ if we have that bug */ 66 if (handler->priv->has_lpc_irq_errata && 67 (handler->parent_int_map & gc->mask_cache & 68 BIT(LIOINTC_ERRATA_IRQ))) 69 pending = BIT(LIOINTC_ERRATA_IRQ); 70 else 71 spurious_interrupt(); 72 } 73 74 while (pending) { 75 int bit = __ffs(pending); 76 77 generic_handle_irq(irq_find_mapping(gc->domain, bit)); 78 pending &= ~BIT(bit); 79 } 80 81 chained_irq_exit(chip, desc); 82} 83 84static void liointc_set_bit(struct irq_chip_generic *gc, 85 unsigned int offset, 86 u32 mask, bool set) 87{ 88 if (set) 89 writel(readl(gc->reg_base + offset) | mask, 90 gc->reg_base + offset); 91 else 92 writel(readl(gc->reg_base + offset) & ~mask, 93 gc->reg_base + offset); 94} 95 96static int liointc_set_type(struct irq_data *data, unsigned int type) 97{ 98 struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); 99 u32 mask = data->mask; 100 unsigned long flags; 101 102 irq_gc_lock_irqsave(gc, flags); 103 switch (type) { 104 case IRQ_TYPE_LEVEL_HIGH: 105 liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); 106 liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); 107 break; 108 case IRQ_TYPE_LEVEL_LOW: 109 liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, false); 110 liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); 111 break; 112 case IRQ_TYPE_EDGE_RISING: 113 liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); 114 liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, true); 115 break; 116 case IRQ_TYPE_EDGE_FALLING: 117 liointc_set_bit(gc, LIOINTC_REG_INTC_EDGE, mask, true); 118 liointc_set_bit(gc, LIOINTC_REG_INTC_POL, mask, false); 119 break; 120 default: 121 irq_gc_unlock_irqrestore(gc, flags); 122 return -EINVAL; 123 } 124 irq_gc_unlock_irqrestore(gc, flags); 125 126 irqd_set_trigger_type(data, type); 127 return 0; 128} 129 130static void liointc_suspend(struct irq_chip_generic *gc) 131{ 132 struct liointc_priv *priv = gc->private; 133 134 priv->int_pol = readl(gc->reg_base + LIOINTC_REG_INTC_POL); 135 priv->int_edge = readl(gc->reg_base + LIOINTC_REG_INTC_EDGE); 136} 137 138static void liointc_resume(struct irq_chip_generic *gc) 139{ 140 struct liointc_priv *priv = gc->private; 141 unsigned long flags; 142 int i; 143 144 irq_gc_lock_irqsave(gc, flags); 145 /* Disable all at first */ 146 writel(0xffffffff, gc->reg_base + LIOINTC_REG_INTC_DISABLE); 147 /* Restore map cache */ 148 for (i = 0; i < LIOINTC_CHIP_IRQ; i++) 149 writeb(priv->map_cache[i], gc->reg_base + i); 150 writel(priv->int_pol, gc->reg_base + LIOINTC_REG_INTC_POL); 151 writel(priv->int_edge, gc->reg_base + LIOINTC_REG_INTC_EDGE); 152 /* Restore mask cache */ 153 writel(gc->mask_cache, gc->reg_base + LIOINTC_REG_INTC_ENABLE); 154 irq_gc_unlock_irqrestore(gc, flags); 155} 156 157static int parent_irq[LIOINTC_NUM_PARENT]; 158static u32 parent_int_map[LIOINTC_NUM_PARENT]; 159static const char *const parent_names[] = {"int0", "int1", "int2", "int3"}; 160 161static int liointc_init(phys_addr_t addr, unsigned long size, int revision, 162 struct fwnode_handle *domain_handle, struct device_node *node) 163{ 164 int i, err; 165 void __iomem *base; 166 struct irq_chip_type *ct; 167 struct irq_chip_generic *gc; 168 struct irq_domain *domain; 169 struct liointc_priv *priv; 170 171 priv = kzalloc(sizeof(*priv), GFP_KERNEL); 172 if (!priv) 173 return -ENOMEM; 174 175 base = ioremap(addr, size); 176 if (!base) 177 goto out_free_priv; 178 179 priv->domain_handle = domain_handle; 180 181 for (i = 0; i < LIOINTC_NUM_PARENT; i++) 182 priv->handler[i].parent_int_map = parent_int_map[i]; 183 184 /* Setup IRQ domain */ 185 domain = irq_domain_create_linear(domain_handle, LIOINTC_CHIP_IRQ, 186 &irq_generic_chip_ops, priv); 187 if (!domain) { 188 pr_err("loongson-liointc: cannot add IRQ domain\n"); 189 goto out_iounmap; 190 } 191 192 err = irq_alloc_domain_generic_chips(domain, LIOINTC_CHIP_IRQ, 1, 193 (node ? node->full_name : "LIOINTC"), 194 handle_level_irq, 0, IRQ_NOPROBE, 0); 195 if (err) { 196 pr_err("loongson-liointc: unable to register IRQ domain\n"); 197 goto out_free_domain; 198 } 199 200 201 /* Disable all IRQs */ 202 writel(0xffffffff, base + LIOINTC_REG_INTC_DISABLE); 203 /* Set to level triggered */ 204 writel(0x0, base + LIOINTC_REG_INTC_EDGE); 205 206 /* Generate parent INT part of map cache */ 207 for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 208 u32 pending = priv->handler[i].parent_int_map; 209 210 while (pending) { 211 int bit = __ffs(pending); 212 213 priv->map_cache[bit] = BIT(i) << LIOINTC_SHIFT_INTx; 214 pending &= ~BIT(bit); 215 } 216 } 217 218 for (i = 0; i < LIOINTC_CHIP_IRQ; i++) { 219 /* Generate core part of map cache */ 220 priv->map_cache[i] |= BIT(loongson_sysconf.boot_cpu_id); 221 writeb(priv->map_cache[i], base + i); 222 } 223 224 gc = irq_get_domain_generic_chip(domain, 0); 225 gc->private = priv; 226 gc->reg_base = base; 227 gc->domain = domain; 228 gc->suspend = liointc_suspend; 229 gc->resume = liointc_resume; 230 231 ct = gc->chip_types; 232 ct->regs.enable = LIOINTC_REG_INTC_ENABLE; 233 ct->regs.disable = LIOINTC_REG_INTC_DISABLE; 234 ct->chip.irq_unmask = irq_gc_unmask_enable_reg; 235 ct->chip.irq_mask = irq_gc_mask_disable_reg; 236 ct->chip.irq_mask_ack = irq_gc_mask_disable_reg; 237 ct->chip.irq_set_type = liointc_set_type; 238 239 gc->mask_cache = 0; 240 priv->gc = gc; 241 242 for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 243 if (parent_irq[i] <= 0) 244 continue; 245 246 priv->handler[i].priv = priv; 247 irq_set_chained_handler_and_data(parent_irq[i], 248 liointc_chained_handle_irq, &priv->handler[i]); 249 } 250 251 return 0; 252 253out_free_domain: 254 irq_domain_remove(domain); 255out_iounmap: 256 iounmap(base); 257out_free_priv: 258 kfree(priv); 259 260 return -EINVAL; 261} 262 263#ifdef CONFIG_OF 264 265static int __init liointc_of_init(struct device_node *node, 266 struct device_node *parent) 267{ 268 bool have_parent = FALSE; 269 int sz, i, index, revision, err = 0; 270 struct resource res; 271 272 if (!of_device_is_compatible(node, "loongson,liointc-2.0")) { 273 index = 0; 274 revision = 1; 275 } else { 276 index = of_property_match_string(node, "reg-names", "main"); 277 revision = 2; 278 } 279 280 if (of_address_to_resource(node, index, &res)) 281 return -EINVAL; 282 283 for (i = 0; i < LIOINTC_NUM_PARENT; i++) { 284 parent_irq[i] = of_irq_get_byname(node, parent_names[i]); 285 if (parent_irq[i] > 0) 286 have_parent = TRUE; 287 } 288 if (!have_parent) 289 return -ENODEV; 290 291 sz = of_property_read_variable_u32_array(node, 292 "loongson,parent_int_map", 293 &parent_int_map[0], 294 LIOINTC_NUM_PARENT, 295 LIOINTC_NUM_PARENT); 296 if (sz < 4) { 297 pr_err("loongson-liointc: No parent_int_map\n"); 298 return -ENODEV; 299 } 300 301 err = liointc_init(res.start, resource_size(&res), 302 revision, of_node_to_fwnode(node), node); 303 if (err < 0) 304 return err; 305 306 return 0; 307} 308 309IRQCHIP_DECLARE(loongson_liointc_1_0, "loongson,liointc-1.0", liointc_of_init); 310IRQCHIP_DECLARE(loongson_liointc_1_0a, "loongson,liointc-1.0a", liointc_of_init); 311 312#endif 313 314#ifdef CONFIG_ACPI 315 316struct irq_domain *liointc_acpi_init(struct irq_domain *parent, 317 struct acpi_madt_lio_pic *acpi_liointc) 318{ 319 int ret; 320 struct fwnode_handle *domain_handle; 321 322 if (!acpi_liointc) 323 return NULL; 324 325 parent_int_map[0] = acpi_liointc->cascade_map[0]; 326 parent_int_map[1] = acpi_liointc->cascade_map[1]; 327 328 parent_irq[0] = irq_create_mapping(parent, acpi_liointc->cascade[0]); 329 if (!cpu_has_extioi) 330 parent_irq[1] = irq_create_mapping(parent, acpi_liointc->cascade[1]); 331 332 domain_handle = irq_domain_alloc_fwnode((phys_addr_t *)acpi_liointc); 333 if (!domain_handle) { 334 pr_err("Unable to allocate domain handle\n"); 335 return NULL; 336 } 337 338 ret = liointc_init(acpi_liointc->address, acpi_liointc->size, 339 1, domain_handle, NULL); 340 if (ret < 0) 341 return NULL; 342 343 return irq_find_matching_fwnode(domain_handle, DOMAIN_BUS_ANY); 344} 345 346#endif 347