irq-loongson-htpic.c (3076B)
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 22struct loongson_htpic { 23 void __iomem *base; 24 struct irq_domain *domain; 25}; 26 27static struct loongson_htpic *htpic; 28 29static 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_domain_irq(priv->domain, bit); 52 pending &= ~BIT(bit); 53 } 54 chained_irq_exit(chip, desc); 55} 56 57static void htpic_reg_init(void) 58{ 59 int i; 60 61 for (i = 0; i < HTINT_NUM_VECTORS; i++) { 62 /* Disable all HT Vectors */ 63 writel(0x0, htpic->base + HTINT_EN_OFF + i * 0x4); 64 /* Read back to force write */ 65 (void) readl(htpic->base + i * 0x4); 66 /* Ack all possible pending IRQs */ 67 writel(GENMASK(31, 0), htpic->base + i * 0x4); 68 } 69 70 /* Enable 16 vectors for PIC */ 71 writel(0xffff, htpic->base + HTINT_EN_OFF); 72} 73 74static void htpic_resume(void) 75{ 76 htpic_reg_init(); 77} 78 79struct syscore_ops htpic_syscore_ops = { 80 .resume = htpic_resume, 81}; 82 83static int __init htpic_of_init(struct device_node *node, struct device_node *parent) 84{ 85 unsigned int parent_irq[4]; 86 int i, err; 87 int num_parents = 0; 88 89 if (htpic) { 90 pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n"); 91 return -ENODEV; 92 } 93 94 htpic = kzalloc(sizeof(*htpic), GFP_KERNEL); 95 if (!htpic) 96 return -ENOMEM; 97 98 htpic->base = of_iomap(node, 0); 99 if (!htpic->base) { 100 err = -ENODEV; 101 goto out_free; 102 } 103 104 htpic->domain = __init_i8259_irqs(node); 105 if (!htpic->domain) { 106 pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n"); 107 err = -ENOMEM; 108 goto out_iounmap; 109 } 110 111 /* Interrupt may come from any of the 4 interrupt line */ 112 for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) { 113 parent_irq[i] = irq_of_parse_and_map(node, i); 114 if (parent_irq[i] <= 0) 115 break; 116 117 num_parents++; 118 } 119 120 if (!num_parents) { 121 pr_err("loongson-htpic: Failed to get parent irqs\n"); 122 err = -ENODEV; 123 goto out_remove_domain; 124 } 125 126 htpic_reg_init(); 127 128 for (i = 0; i < num_parents; i++) { 129 irq_set_chained_handler_and_data(parent_irq[i], 130 htpic_irq_dispatch, htpic); 131 } 132 133 register_syscore_ops(&htpic_syscore_ops); 134 135 return 0; 136 137out_remove_domain: 138 irq_domain_remove(htpic->domain); 139out_iounmap: 140 iounmap(htpic->base); 141out_free: 142 kfree(htpic); 143 return err; 144} 145 146IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init);