Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

irqchip: Add driver for Loongson-3 HyperTransport PIC controller

This controller appeared on Loongson-3 family of chips to receive
interrupts from PCH PIC.
It is a I8259 with optimized interrupt polling flow. We can poll
interrupt number from HT vector directly but still have to follow
standard I8259 routines to mask, unmask and EOI.

Signed-off-by: Jiaxun Yang <jiaxun.yang@flygoat.com>
Co-developed-by: Huacai Chen <chenhc@lemote.com>
Signed-off-by: Huacai Chen <chenhc@lemote.com>
Reviewed-by: Marc Zyngier <maz@kernel.org>
Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>

authored by

Jiaxun Yang and committed by
Thomas Bogendoerfer
a93f1d90 b6280c8b

+161
+1
arch/mips/include/asm/i8259.h
··· 36 36 extern void make_8259A_irq(unsigned int irq); 37 37 38 38 extern void init_i8259_irqs(void); 39 + extern struct irq_domain *__init_i8259_irqs(struct device_node *node); 39 40 40 41 /** 41 42 * i8159_set_poll() - Override the i8259 polling function
+10
drivers/irqchip/Kconfig
··· 522 522 help 523 523 Support for the Loongson Local I/O Interrupt Controller. 524 524 525 + config LOONGSON_HTPIC 526 + bool "Loongson3 HyperTransport PIC Controller" 527 + depends on MACH_LOONGSON64 528 + default y 529 + select IRQ_DOMAIN 530 + select GENERIC_IRQ_CHIP 531 + select I8259 532 + help 533 + Support for the Loongson-3 HyperTransport PIC Controller. 534 + 525 535 endmenu
+1
drivers/irqchip/Makefile
··· 106 106 obj-$(CONFIG_TI_SCI_INTR_IRQCHIP) += irq-ti-sci-intr.o 107 107 obj-$(CONFIG_TI_SCI_INTA_IRQCHIP) += irq-ti-sci-inta.o 108 108 obj-$(CONFIG_LOONGSON_LIOINTC) += irq-loongson-liointc.o 109 + obj-$(CONFIG_LOONGSON_HTPIC) += irq-loongson-htpic.o
+149
drivers/irqchip/irq-loongson-htpic.c
··· 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);