1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright (C) 2020, Jiaxun Yang <jiaxun.yang@flygoat.com> 4 * Loongson HTPIC IRQ support 5 */ 6 7 #include <linux/init.h> 8 #include <linux/of_address.h> 9 #include <linux/of_irq.h> 10 #include <linux/irqchip.h> 11 #include <linux/irqchip/chained_irq.h> 12 #include <linux/irq.h> 13 #include <linux/io.h> 14 #include <linux/syscore_ops.h> 15 16 #include <asm/i8259.h> 17 18 #define HTPIC_MAX_PARENT_IRQ 4 19 #define HTINT_NUM_VECTORS 8 20 #define HTINT_EN_OFF 0x20 21 22 struct loongson_htpic { 23 void __iomem *base; 24 struct irq_domain *domain; 25 }; 26 27 static struct loongson_htpic *htpic; 28 29 static void htpic_irq_dispatch(struct irq_desc *desc) 30 { 31 struct loongson_htpic *priv = irq_desc_get_handler_data(desc); 32 struct irq_chip *chip = irq_desc_get_chip(desc); 33 uint32_t pending; 34 35 chained_irq_enter(chip, desc); 36 pending = readl(priv->base); 37 /* Ack all IRQs at once, otherwise IRQ flood might happen */ 38 writel(pending, priv->base); 39 40 if (!pending) 41 spurious_interrupt(); 42 43 while (pending) { 44 int bit = __ffs(pending); 45 46 if (unlikely(bit > 15)) { 47 spurious_interrupt(); 48 break; 49 } 50 51 generic_handle_irq(irq_linear_revmap(priv->domain, bit)); 52 pending &= ~BIT(bit); 53 } 54 chained_irq_exit(chip, desc); 55 } 56 57 static void htpic_reg_init(void) 58 { 59 int i; 60 61 for (i = 0; i < HTINT_NUM_VECTORS; i++) { 62 uint32_t val; 63 64 /* Disable all HT Vectors */ 65 writel(0x0, htpic->base + HTINT_EN_OFF + i * 0x4); 66 val = readl(htpic->base + i * 0x4); 67 /* Ack all possible pending IRQs */ 68 writel(GENMASK(31, 0), htpic->base + i * 0x4); 69 } 70 71 /* Enable 16 vectors for PIC */ 72 writel(0xffff, htpic->base + HTINT_EN_OFF); 73 } 74 75 static void htpic_resume(void) 76 { 77 htpic_reg_init(); 78 } 79 80 struct syscore_ops htpic_syscore_ops = { 81 .resume = htpic_resume, 82 }; 83 84 int __init htpic_of_init(struct device_node *node, struct device_node *parent) 85 { 86 unsigned int parent_irq[4]; 87 int i, err; 88 int num_parents = 0; 89 90 if (htpic) { 91 pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n"); 92 return -ENODEV; 93 } 94 95 htpic = kzalloc(sizeof(*htpic), GFP_KERNEL); 96 if (!htpic) { 97 err = -ENOMEM; 98 goto out_free; 99 } 100 101 htpic->base = of_iomap(node, 0); 102 if (!htpic->base) { 103 err = -ENODEV; 104 goto out_free; 105 } 106 107 htpic->domain = __init_i8259_irqs(node); 108 if (!htpic->domain) { 109 pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n"); 110 err = -ENOMEM; 111 goto out_iounmap; 112 } 113 114 /* Interrupt may come from any of the 4 interrupt line */ 115 for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) { 116 parent_irq[i] = irq_of_parse_and_map(node, i); 117 if (parent_irq[i] <= 0) 118 break; 119 120 num_parents++; 121 } 122 123 if (!num_parents) { 124 pr_err("loongson-htpic: Failed to get parent irqs\n"); 125 err = -ENODEV; 126 goto out_remove_domain; 127 } 128 129 htpic_reg_init(); 130 131 for (i = 0; i < num_parents; i++) { 132 irq_set_chained_handler_and_data(parent_irq[i], 133 htpic_irq_dispatch, htpic); 134 } 135 136 register_syscore_ops(&htpic_syscore_ops); 137 138 return 0; 139 140 out_remove_domain: 141 irq_domain_remove(htpic->domain); 142 out_iounmap: 143 iounmap(htpic->base); 144 out_free: 145 kfree(htpic); 146 return err; 147 } 148 149 IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init); 150