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

Configure Feed

Select the types of activity you want to include in your feed.

at v6.19-rc6 150 lines 3.2 kB view raw
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 *data) 75{ 76 htpic_reg_init(); 77} 78 79static const struct syscore_ops htpic_syscore_ops = { 80 .resume = htpic_resume, 81}; 82 83static struct syscore htpic_syscore = { 84 .ops = &htpic_syscore_ops, 85}; 86 87static int __init htpic_of_init(struct device_node *node, struct device_node *parent) 88{ 89 unsigned int parent_irq[4]; 90 int i, err; 91 int num_parents = 0; 92 93 if (htpic) { 94 pr_err("loongson-htpic: Only one HTPIC is allowed in the system\n"); 95 return -ENODEV; 96 } 97 98 htpic = kzalloc(sizeof(*htpic), GFP_KERNEL); 99 if (!htpic) 100 return -ENOMEM; 101 102 htpic->base = of_iomap(node, 0); 103 if (!htpic->base) { 104 err = -ENODEV; 105 goto out_free; 106 } 107 108 htpic->domain = __init_i8259_irqs(node); 109 if (!htpic->domain) { 110 pr_err("loongson-htpic: Failed to initialize i8259 IRQs\n"); 111 err = -ENOMEM; 112 goto out_iounmap; 113 } 114 115 /* Interrupt may come from any of the 4 interrupt line */ 116 for (i = 0; i < HTPIC_MAX_PARENT_IRQ; i++) { 117 parent_irq[i] = irq_of_parse_and_map(node, i); 118 if (parent_irq[i] <= 0) 119 break; 120 121 num_parents++; 122 } 123 124 if (!num_parents) { 125 pr_err("loongson-htpic: Failed to get parent irqs\n"); 126 err = -ENODEV; 127 goto out_remove_domain; 128 } 129 130 htpic_reg_init(); 131 132 for (i = 0; i < num_parents; i++) { 133 irq_set_chained_handler_and_data(parent_irq[i], 134 htpic_irq_dispatch, htpic); 135 } 136 137 register_syscore(&htpic_syscore); 138 139 return 0; 140 141out_remove_domain: 142 irq_domain_remove(htpic->domain); 143out_iounmap: 144 iounmap(htpic->base); 145out_free: 146 kfree(htpic); 147 return err; 148} 149 150IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0", htpic_of_init);