diff options
author | 2025-03-08 22:04:20 +0800 | |
---|---|---|
committer | 2025-03-08 22:04:20 +0800 | |
commit | a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a (patch) | |
tree | 84f21bd0bf7071bc5fc7dd989e77d7ceb5476682 /arch/mips/sgi-ip27/ip27-irq.c | |
download | ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.tar.gz ohosKernel-a07bb8fd1299070229f0e8f3dcb57ffd5ef9870a.zip |
Initial commit: OpenHarmony-v4.0-ReleaseOpenHarmony-v4.0-Release
Diffstat (limited to 'arch/mips/sgi-ip27/ip27-irq.c')
-rw-r--r-- | arch/mips/sgi-ip27/ip27-irq.c | 314 |
1 files changed, 314 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c new file mode 100644 index 000000000..42df9fafa --- /dev/null +++ b/arch/mips/sgi-ip27/ip27-irq.c | |||
@@ -0,0 +1,314 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * ip27-irq.c: Highlevel interrupt handling for IP27 architecture. | ||
4 | * | ||
5 | * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org) | ||
6 | * Copyright (C) 1999, 2000 Silicon Graphics, Inc. | ||
7 | * Copyright (C) 1999 - 2001 Kanoj Sarcar | ||
8 | */ | ||
9 | |||
10 | #include <linux/interrupt.h> | ||
11 | #include <linux/irq.h> | ||
12 | #include <linux/ioport.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/bitops.h> | ||
15 | #include <linux/sched.h> | ||
16 | |||
17 | #include <asm/io.h> | ||
18 | #include <asm/irq_cpu.h> | ||
19 | #include <asm/sn/addrs.h> | ||
20 | #include <asm/sn/agent.h> | ||
21 | #include <asm/sn/arch.h> | ||
22 | #include <asm/sn/intr.h> | ||
23 | #include <asm/sn/irq_alloc.h> | ||
24 | |||
25 | struct hub_irq_data { | ||
26 | u64 *irq_mask[2]; | ||
27 | cpuid_t cpu; | ||
28 | }; | ||
29 | |||
30 | static DECLARE_BITMAP(hub_irq_map, IP27_HUB_IRQ_COUNT); | ||
31 | |||
32 | static DEFINE_PER_CPU(unsigned long [2], irq_enable_mask); | ||
33 | |||
34 | static inline int alloc_level(void) | ||
35 | { | ||
36 | int level; | ||
37 | |||
38 | again: | ||
39 | level = find_first_zero_bit(hub_irq_map, IP27_HUB_IRQ_COUNT); | ||
40 | if (level >= IP27_HUB_IRQ_COUNT) | ||
41 | return -ENOSPC; | ||
42 | |||
43 | if (test_and_set_bit(level, hub_irq_map)) | ||
44 | goto again; | ||
45 | |||
46 | return level; | ||
47 | } | ||
48 | |||
49 | static void enable_hub_irq(struct irq_data *d) | ||
50 | { | ||
51 | struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); | ||
52 | unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu); | ||
53 | |||
54 | set_bit(d->hwirq, mask); | ||
55 | __raw_writeq(mask[0], hd->irq_mask[0]); | ||
56 | __raw_writeq(mask[1], hd->irq_mask[1]); | ||
57 | } | ||
58 | |||
59 | static void disable_hub_irq(struct irq_data *d) | ||
60 | { | ||
61 | struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); | ||
62 | unsigned long *mask = per_cpu(irq_enable_mask, hd->cpu); | ||
63 | |||
64 | clear_bit(d->hwirq, mask); | ||
65 | __raw_writeq(mask[0], hd->irq_mask[0]); | ||
66 | __raw_writeq(mask[1], hd->irq_mask[1]); | ||
67 | } | ||
68 | |||
69 | static void setup_hub_mask(struct hub_irq_data *hd, const struct cpumask *mask) | ||
70 | { | ||
71 | nasid_t nasid; | ||
72 | int cpu; | ||
73 | |||
74 | cpu = cpumask_first_and(mask, cpu_online_mask); | ||
75 | if (cpu >= nr_cpu_ids) | ||
76 | cpu = cpumask_any(cpu_online_mask); | ||
77 | |||
78 | nasid = cpu_to_node(cpu); | ||
79 | hd->cpu = cpu; | ||
80 | if (!cputoslice(cpu)) { | ||
81 | hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_A); | ||
82 | hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_A); | ||
83 | } else { | ||
84 | hd->irq_mask[0] = REMOTE_HUB_PTR(nasid, PI_INT_MASK0_B); | ||
85 | hd->irq_mask[1] = REMOTE_HUB_PTR(nasid, PI_INT_MASK1_B); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | static int set_affinity_hub_irq(struct irq_data *d, const struct cpumask *mask, | ||
90 | bool force) | ||
91 | { | ||
92 | struct hub_irq_data *hd = irq_data_get_irq_chip_data(d); | ||
93 | |||
94 | if (!hd) | ||
95 | return -EINVAL; | ||
96 | |||
97 | if (irqd_is_started(d)) | ||
98 | disable_hub_irq(d); | ||
99 | |||
100 | setup_hub_mask(hd, mask); | ||
101 | |||
102 | if (irqd_is_started(d)) | ||
103 | enable_hub_irq(d); | ||
104 | |||
105 | irq_data_update_effective_affinity(d, cpumask_of(hd->cpu)); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | static struct irq_chip hub_irq_type = { | ||
111 | .name = "HUB", | ||
112 | .irq_mask = disable_hub_irq, | ||
113 | .irq_unmask = enable_hub_irq, | ||
114 | .irq_set_affinity = set_affinity_hub_irq, | ||
115 | }; | ||
116 | |||
117 | static int hub_domain_alloc(struct irq_domain *domain, unsigned int virq, | ||
118 | unsigned int nr_irqs, void *arg) | ||
119 | { | ||
120 | struct irq_alloc_info *info = arg; | ||
121 | struct hub_irq_data *hd; | ||
122 | struct hub_data *hub; | ||
123 | struct irq_desc *desc; | ||
124 | int swlevel; | ||
125 | |||
126 | if (nr_irqs > 1 || !info) | ||
127 | return -EINVAL; | ||
128 | |||
129 | hd = kzalloc(sizeof(*hd), GFP_KERNEL); | ||
130 | if (!hd) | ||
131 | return -ENOMEM; | ||
132 | |||
133 | swlevel = alloc_level(); | ||
134 | if (unlikely(swlevel < 0)) { | ||
135 | kfree(hd); | ||
136 | return -EAGAIN; | ||
137 | } | ||
138 | irq_domain_set_info(domain, virq, swlevel, &hub_irq_type, hd, | ||
139 | handle_level_irq, NULL, NULL); | ||
140 | |||
141 | /* use CPU connected to nearest hub */ | ||
142 | hub = hub_data(info->nasid); | ||
143 | setup_hub_mask(hd, &hub->h_cpus); | ||
144 | info->nasid = cpu_to_node(hd->cpu); | ||
145 | |||
146 | /* Make sure it's not already pending when we connect it. */ | ||
147 | REMOTE_HUB_CLR_INTR(info->nasid, swlevel); | ||
148 | |||
149 | desc = irq_to_desc(virq); | ||
150 | desc->irq_common_data.node = info->nasid; | ||
151 | cpumask_copy(desc->irq_common_data.affinity, &hub->h_cpus); | ||
152 | |||
153 | return 0; | ||
154 | } | ||
155 | |||
156 | static void hub_domain_free(struct irq_domain *domain, | ||
157 | unsigned int virq, unsigned int nr_irqs) | ||
158 | { | ||
159 | struct irq_data *irqd; | ||
160 | |||
161 | if (nr_irqs > 1) | ||
162 | return; | ||
163 | |||
164 | irqd = irq_domain_get_irq_data(domain, virq); | ||
165 | if (irqd && irqd->chip_data) | ||
166 | kfree(irqd->chip_data); | ||
167 | } | ||
168 | |||
169 | static const struct irq_domain_ops hub_domain_ops = { | ||
170 | .alloc = hub_domain_alloc, | ||
171 | .free = hub_domain_free, | ||
172 | }; | ||
173 | |||
174 | /* | ||
175 | * This code is unnecessarily complex, because we do | ||
176 | * intr enabling. Basically, once we grab the set of intrs we need | ||
177 | * to service, we must mask _all_ these interrupts; firstly, to make | ||
178 | * sure the same intr does not intr again, causing recursion that | ||
179 | * can lead to stack overflow. Secondly, we can not just mask the | ||
180 | * one intr we are do_IRQing, because the non-masked intrs in the | ||
181 | * first set might intr again, causing multiple servicings of the | ||
182 | * same intr. This effect is mostly seen for intercpu intrs. | ||
183 | * Kanoj 05.13.00 | ||
184 | */ | ||
185 | |||
186 | static void ip27_do_irq_mask0(struct irq_desc *desc) | ||
187 | { | ||
188 | cpuid_t cpu = smp_processor_id(); | ||
189 | unsigned long *mask = per_cpu(irq_enable_mask, cpu); | ||
190 | struct irq_domain *domain; | ||
191 | u64 pend0; | ||
192 | int irq; | ||
193 | |||
194 | /* copied from Irix intpend0() */ | ||
195 | pend0 = LOCAL_HUB_L(PI_INT_PEND0); | ||
196 | |||
197 | pend0 &= mask[0]; /* Pick intrs we should look at */ | ||
198 | if (!pend0) | ||
199 | return; | ||
200 | |||
201 | #ifdef CONFIG_SMP | ||
202 | if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) { | ||
203 | LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ); | ||
204 | scheduler_ipi(); | ||
205 | } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) { | ||
206 | LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ); | ||
207 | scheduler_ipi(); | ||
208 | } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) { | ||
209 | LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ); | ||
210 | generic_smp_call_function_interrupt(); | ||
211 | } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) { | ||
212 | LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ); | ||
213 | generic_smp_call_function_interrupt(); | ||
214 | } else | ||
215 | #endif | ||
216 | { | ||
217 | domain = irq_desc_get_handler_data(desc); | ||
218 | irq = irq_linear_revmap(domain, __ffs(pend0)); | ||
219 | if (irq) | ||
220 | generic_handle_irq(irq); | ||
221 | else | ||
222 | spurious_interrupt(); | ||
223 | } | ||
224 | |||
225 | LOCAL_HUB_L(PI_INT_PEND0); | ||
226 | } | ||
227 | |||
228 | static void ip27_do_irq_mask1(struct irq_desc *desc) | ||
229 | { | ||
230 | cpuid_t cpu = smp_processor_id(); | ||
231 | unsigned long *mask = per_cpu(irq_enable_mask, cpu); | ||
232 | struct irq_domain *domain; | ||
233 | u64 pend1; | ||
234 | int irq; | ||
235 | |||
236 | /* copied from Irix intpend0() */ | ||
237 | pend1 = LOCAL_HUB_L(PI_INT_PEND1); | ||
238 | |||
239 | pend1 &= mask[1]; /* Pick intrs we should look at */ | ||
240 | if (!pend1) | ||
241 | return; | ||
242 | |||
243 | domain = irq_desc_get_handler_data(desc); | ||
244 | irq = irq_linear_revmap(domain, __ffs(pend1) + 64); | ||
245 | if (irq) | ||
246 | generic_handle_irq(irq); | ||
247 | else | ||
248 | spurious_interrupt(); | ||
249 | |||
250 | LOCAL_HUB_L(PI_INT_PEND1); | ||
251 | } | ||
252 | |||
253 | void install_ipi(void) | ||
254 | { | ||
255 | int cpu = smp_processor_id(); | ||
256 | unsigned long *mask = per_cpu(irq_enable_mask, cpu); | ||
257 | int slice = LOCAL_HUB_L(PI_CPU_NUM); | ||
258 | int resched, call; | ||
259 | |||
260 | resched = CPU_RESCHED_A_IRQ + slice; | ||
261 | set_bit(resched, mask); | ||
262 | LOCAL_HUB_CLR_INTR(resched); | ||
263 | |||
264 | call = CPU_CALL_A_IRQ + slice; | ||
265 | set_bit(call, mask); | ||
266 | LOCAL_HUB_CLR_INTR(call); | ||
267 | |||
268 | if (slice == 0) { | ||
269 | LOCAL_HUB_S(PI_INT_MASK0_A, mask[0]); | ||
270 | LOCAL_HUB_S(PI_INT_MASK1_A, mask[1]); | ||
271 | } else { | ||
272 | LOCAL_HUB_S(PI_INT_MASK0_B, mask[0]); | ||
273 | LOCAL_HUB_S(PI_INT_MASK1_B, mask[1]); | ||
274 | } | ||
275 | } | ||
276 | |||
277 | void __init arch_init_irq(void) | ||
278 | { | ||
279 | struct irq_domain *domain; | ||
280 | struct fwnode_handle *fn; | ||
281 | int i; | ||
282 | |||
283 | mips_cpu_irq_init(); | ||
284 | |||
285 | /* | ||
286 | * Some interrupts are reserved by hardware or by software convention. | ||
287 | * Mark these as reserved right away so they won't be used accidentally | ||
288 | * later. | ||
289 | */ | ||
290 | for (i = 0; i <= CPU_CALL_B_IRQ; i++) | ||
291 | set_bit(i, hub_irq_map); | ||
292 | |||
293 | for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) | ||
294 | set_bit(i, hub_irq_map); | ||
295 | |||
296 | fn = irq_domain_alloc_named_fwnode("HUB"); | ||
297 | WARN_ON(fn == NULL); | ||
298 | if (!fn) | ||
299 | return; | ||
300 | domain = irq_domain_create_linear(fn, IP27_HUB_IRQ_COUNT, | ||
301 | &hub_domain_ops, NULL); | ||
302 | WARN_ON(domain == NULL); | ||
303 | if (!domain) | ||
304 | return; | ||
305 | |||
306 | irq_set_default_host(domain); | ||
307 | |||
308 | irq_set_percpu_devid(IP27_HUB_PEND0_IRQ); | ||
309 | irq_set_chained_handler_and_data(IP27_HUB_PEND0_IRQ, ip27_do_irq_mask0, | ||
310 | domain); | ||
311 | irq_set_percpu_devid(IP27_HUB_PEND1_IRQ); | ||
312 | irq_set_chained_handler_and_data(IP27_HUB_PEND1_IRQ, ip27_do_irq_mask1, | ||
313 | domain); | ||
314 | } | ||