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(addr: priv->base); |
37 | /* Ack all IRQs at once, otherwise IRQ flood might happen */ |
38 | writel(val: pending, addr: 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(domain: priv->domain, hwirq: 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 | /* Disable all HT Vectors */ |
63 | writel(val: 0x0, addr: htpic->base + HTINT_EN_OFF + i * 0x4); |
64 | /* Read back to force write */ |
65 | (void) readl(addr: htpic->base + i * 0x4); |
66 | /* Ack all possible pending IRQs */ |
67 | writel(GENMASK(31, 0), addr: htpic->base + i * 0x4); |
68 | } |
69 | |
70 | /* Enable 16 vectors for PIC */ |
71 | writel(val: 0xffff, addr: htpic->base + HTINT_EN_OFF); |
72 | } |
73 | |
74 | static void htpic_resume(void) |
75 | { |
76 | htpic_reg_init(); |
77 | } |
78 | |
79 | struct syscore_ops htpic_syscore_ops = { |
80 | .resume = htpic_resume, |
81 | }; |
82 | |
83 | static 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(size: sizeof(*htpic), GFP_KERNEL); |
95 | if (!htpic) |
96 | return -ENOMEM; |
97 | |
98 | htpic->base = of_iomap(node, index: 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, index: 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(irq: parent_irq[i], |
130 | handle: htpic_irq_dispatch, data: htpic); |
131 | } |
132 | |
133 | register_syscore_ops(ops: &htpic_syscore_ops); |
134 | |
135 | return 0; |
136 | |
137 | out_remove_domain: |
138 | irq_domain_remove(host: htpic->domain); |
139 | out_iounmap: |
140 | iounmap(addr: htpic->base); |
141 | out_free: |
142 | kfree(objp: htpic); |
143 | return err; |
144 | } |
145 | |
146 | IRQCHIP_DECLARE(loongson_htpic, "loongson,htpic-1.0" , htpic_of_init); |
147 | |