Commit | Line | Data |
---|---|---|
2299c49d SH |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2008 Ralf Baechle (ralf@linux-mips.org) | |
7 | * Copyright (C) 2012 MIPS Technologies, Inc. All rights reserved. | |
8 | */ | |
39b8d525 RB |
9 | #include <linux/bitmap.h> |
10 | #include <linux/init.h> | |
18743d27 AB |
11 | #include <linux/interrupt.h> |
12 | #include <linux/sched.h> | |
631330f5 | 13 | #include <linux/smp.h> |
ca4d3e67 | 14 | #include <linux/irq.h> |
dfa762e1 | 15 | #include <linux/clocksource.h> |
39b8d525 RB |
16 | |
17 | #include <asm/io.h> | |
18 | #include <asm/gic.h> | |
98b67c37 SH |
19 | #include <asm/setup.h> |
20 | #include <asm/traps.h> | |
39b8d525 RB |
21 | #include <linux/hardirq.h> |
22 | #include <asm-generic/bitops/find.h> | |
23 | ||
28ea2151 | 24 | unsigned int gic_frequency; |
ff86714f | 25 | unsigned int gic_present; |
0b271f56 | 26 | unsigned long _gic_base; |
98b67c37 | 27 | |
822350bc | 28 | struct gic_pcpu_mask { |
fbd55241 | 29 | DECLARE_BITMAP(pcpu_mask, GIC_MAX_INTRS); |
822350bc JD |
30 | }; |
31 | ||
32 | struct gic_pending_regs { | |
fbd55241 | 33 | DECLARE_BITMAP(pending, GIC_MAX_INTRS); |
822350bc JD |
34 | }; |
35 | ||
36 | struct gic_intrmask_regs { | |
fbd55241 | 37 | DECLARE_BITMAP(intrmask, GIC_MAX_INTRS); |
822350bc JD |
38 | }; |
39 | ||
0b271f56 | 40 | static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; |
39b8d525 RB |
41 | static struct gic_pending_regs pending_regs[NR_CPUS]; |
42 | static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; | |
95150ae8 | 43 | static DEFINE_SPINLOCK(gic_lock); |
c49581a4 | 44 | static struct irq_domain *gic_irq_domain; |
fbd55241 | 45 | static int gic_shared_intrs; |
e9de688d | 46 | static int gic_vpes; |
3263d085 | 47 | static unsigned int gic_cpu_pin; |
4a6a3ea3 | 48 | static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller; |
39b8d525 | 49 | |
18743d27 AB |
50 | static void __gic_irq_dispatch(void); |
51 | ||
0ab2b7d0 | 52 | #if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC) |
dfa762e1 SH |
53 | cycle_t gic_read_count(void) |
54 | { | |
55 | unsigned int hi, hi2, lo; | |
56 | ||
57 | do { | |
58 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi); | |
59 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo); | |
60 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2); | |
61 | } while (hi2 != hi); | |
62 | ||
63 | return (((cycle_t) hi) << 32) + lo; | |
64 | } | |
0ab2b7d0 RG |
65 | |
66 | void gic_write_compare(cycle_t cnt) | |
67 | { | |
68 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), | |
69 | (int)(cnt >> 32)); | |
70 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), | |
71 | (int)(cnt & 0xffffffff)); | |
72 | } | |
73 | ||
414408d0 PB |
74 | void gic_write_cpu_compare(cycle_t cnt, int cpu) |
75 | { | |
76 | unsigned long flags; | |
77 | ||
78 | local_irq_save(flags); | |
79 | ||
80 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), cpu); | |
81 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI), | |
82 | (int)(cnt >> 32)); | |
83 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO), | |
84 | (int)(cnt & 0xffffffff)); | |
85 | ||
86 | local_irq_restore(flags); | |
87 | } | |
88 | ||
0ab2b7d0 RG |
89 | cycle_t gic_read_compare(void) |
90 | { | |
91 | unsigned int hi, lo; | |
92 | ||
93 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi); | |
94 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo); | |
95 | ||
96 | return (((cycle_t) hi) << 32) + lo; | |
97 | } | |
dfa762e1 SH |
98 | #endif |
99 | ||
e9de688d AB |
100 | static bool gic_local_irq_is_routable(int intr) |
101 | { | |
102 | u32 vpe_ctl; | |
103 | ||
104 | /* All local interrupts are routable in EIC mode. */ | |
105 | if (cpu_has_veic) | |
106 | return true; | |
107 | ||
108 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_CTL), vpe_ctl); | |
109 | switch (intr) { | |
110 | case GIC_LOCAL_INT_TIMER: | |
111 | return vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK; | |
112 | case GIC_LOCAL_INT_PERFCTR: | |
113 | return vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK; | |
114 | case GIC_LOCAL_INT_FDC: | |
115 | return vpe_ctl & GIC_VPE_CTL_FDC_RTBL_MSK; | |
116 | case GIC_LOCAL_INT_SWINT0: | |
117 | case GIC_LOCAL_INT_SWINT1: | |
118 | return vpe_ctl & GIC_VPE_CTL_SWINT_RTBL_MSK; | |
119 | default: | |
120 | return true; | |
121 | } | |
122 | } | |
123 | ||
98b67c37 SH |
124 | unsigned int gic_get_timer_pending(void) |
125 | { | |
126 | unsigned int vpe_pending; | |
127 | ||
e9de688d | 128 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), vpe_pending); |
635c9907 | 129 | return vpe_pending & GIC_VPE_PEND_TIMER_MSK; |
98b67c37 SH |
130 | } |
131 | ||
3263d085 | 132 | static void gic_bind_eic_interrupt(int irq, int set) |
98b67c37 SH |
133 | { |
134 | /* Convert irq vector # to hw int # */ | |
135 | irq -= GIC_PIN_TO_VEC_OFFSET; | |
136 | ||
137 | /* Set irq to use shadow set */ | |
138 | GICWRITE(GIC_REG_ADDR(VPE_LOCAL, GIC_VPE_EIC_SS(irq)), set); | |
139 | } | |
140 | ||
39b8d525 RB |
141 | void gic_send_ipi(unsigned int intr) |
142 | { | |
39b8d525 | 143 | GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr); |
39b8d525 RB |
144 | } |
145 | ||
e9de688d AB |
146 | int gic_get_c0_compare_int(void) |
147 | { | |
148 | if (!gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) | |
149 | return MIPS_CPU_IRQ_BASE + cp0_compare_irq; | |
150 | return irq_create_mapping(gic_irq_domain, | |
151 | GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_TIMER)); | |
152 | } | |
153 | ||
154 | int gic_get_c0_perfcount_int(void) | |
155 | { | |
156 | if (!gic_local_irq_is_routable(GIC_LOCAL_INT_PERFCTR)) { | |
157 | /* Is the erformance counter shared with the timer? */ | |
158 | if (cp0_perfcount_irq < 0) | |
159 | return -1; | |
160 | return MIPS_CPU_IRQ_BASE + cp0_perfcount_irq; | |
161 | } | |
162 | return irq_create_mapping(gic_irq_domain, | |
163 | GIC_LOCAL_TO_HWIRQ(GIC_LOCAL_INT_PERFCTR)); | |
164 | } | |
165 | ||
3263d085 | 166 | static unsigned int gic_get_int(void) |
39b8d525 RB |
167 | { |
168 | unsigned int i; | |
169 | unsigned long *pending, *intrmask, *pcpu_mask; | |
170 | unsigned long *pending_abs, *intrmask_abs; | |
171 | ||
172 | /* Get per-cpu bitmaps */ | |
173 | pending = pending_regs[smp_processor_id()].pending; | |
174 | intrmask = intrmask_regs[smp_processor_id()].intrmask; | |
175 | pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask; | |
176 | ||
177 | pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
178 | GIC_SH_PEND_31_0_OFS); | |
179 | intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
180 | GIC_SH_MASK_31_0_OFS); | |
181 | ||
fbd55241 | 182 | for (i = 0; i < BITS_TO_LONGS(gic_shared_intrs); i++) { |
39b8d525 RB |
183 | GICREAD(*pending_abs, pending[i]); |
184 | GICREAD(*intrmask_abs, intrmask[i]); | |
185 | pending_abs++; | |
186 | intrmask_abs++; | |
187 | } | |
188 | ||
fbd55241 AB |
189 | bitmap_and(pending, pending, intrmask, gic_shared_intrs); |
190 | bitmap_and(pending, pending, pcpu_mask, gic_shared_intrs); | |
39b8d525 | 191 | |
3263d085 | 192 | return find_first_bit(pending, gic_shared_intrs); |
39b8d525 RB |
193 | } |
194 | ||
161d049e | 195 | static void gic_mask_irq(struct irq_data *d) |
39b8d525 | 196 | { |
e9de688d | 197 | GIC_CLR_INTR_MASK(GIC_HWIRQ_TO_SHARED(d->hwirq)); |
39b8d525 RB |
198 | } |
199 | ||
161d049e | 200 | static void gic_unmask_irq(struct irq_data *d) |
39b8d525 | 201 | { |
e9de688d | 202 | GIC_SET_INTR_MASK(GIC_HWIRQ_TO_SHARED(d->hwirq)); |
39b8d525 RB |
203 | } |
204 | ||
5561c9e4 AB |
205 | static void gic_ack_irq(struct irq_data *d) |
206 | { | |
e9de688d | 207 | unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); |
c49581a4 | 208 | |
4a6a3ea3 | 209 | GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), irq); |
5561c9e4 AB |
210 | } |
211 | ||
95150ae8 AB |
212 | static int gic_set_type(struct irq_data *d, unsigned int type) |
213 | { | |
e9de688d | 214 | unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); |
95150ae8 AB |
215 | unsigned long flags; |
216 | bool is_edge; | |
217 | ||
218 | spin_lock_irqsave(&gic_lock, flags); | |
219 | switch (type & IRQ_TYPE_SENSE_MASK) { | |
220 | case IRQ_TYPE_EDGE_FALLING: | |
221 | GIC_SET_POLARITY(irq, GIC_POL_NEG); | |
222 | GIC_SET_TRIGGER(irq, GIC_TRIG_EDGE); | |
223 | GIC_SET_DUAL(irq, GIC_TRIG_DUAL_DISABLE); | |
224 | is_edge = true; | |
225 | break; | |
226 | case IRQ_TYPE_EDGE_RISING: | |
227 | GIC_SET_POLARITY(irq, GIC_POL_POS); | |
228 | GIC_SET_TRIGGER(irq, GIC_TRIG_EDGE); | |
229 | GIC_SET_DUAL(irq, GIC_TRIG_DUAL_DISABLE); | |
230 | is_edge = true; | |
231 | break; | |
232 | case IRQ_TYPE_EDGE_BOTH: | |
233 | /* polarity is irrelevant in this case */ | |
234 | GIC_SET_TRIGGER(irq, GIC_TRIG_EDGE); | |
235 | GIC_SET_DUAL(irq, GIC_TRIG_DUAL_ENABLE); | |
236 | is_edge = true; | |
237 | break; | |
238 | case IRQ_TYPE_LEVEL_LOW: | |
239 | GIC_SET_POLARITY(irq, GIC_POL_NEG); | |
240 | GIC_SET_TRIGGER(irq, GIC_TRIG_LEVEL); | |
241 | GIC_SET_DUAL(irq, GIC_TRIG_DUAL_DISABLE); | |
242 | is_edge = false; | |
243 | break; | |
244 | case IRQ_TYPE_LEVEL_HIGH: | |
245 | default: | |
246 | GIC_SET_POLARITY(irq, GIC_POL_POS); | |
247 | GIC_SET_TRIGGER(irq, GIC_TRIG_LEVEL); | |
248 | GIC_SET_DUAL(irq, GIC_TRIG_DUAL_DISABLE); | |
249 | is_edge = false; | |
250 | break; | |
251 | } | |
252 | ||
253 | if (is_edge) { | |
4a6a3ea3 AB |
254 | __irq_set_chip_handler_name_locked(d->irq, |
255 | &gic_edge_irq_controller, | |
256 | handle_edge_irq, NULL); | |
95150ae8 | 257 | } else { |
4a6a3ea3 AB |
258 | __irq_set_chip_handler_name_locked(d->irq, |
259 | &gic_level_irq_controller, | |
260 | handle_level_irq, NULL); | |
95150ae8 AB |
261 | } |
262 | spin_unlock_irqrestore(&gic_lock, flags); | |
39b8d525 | 263 | |
95150ae8 AB |
264 | return 0; |
265 | } | |
266 | ||
267 | #ifdef CONFIG_SMP | |
161d049e TG |
268 | static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, |
269 | bool force) | |
39b8d525 | 270 | { |
e9de688d | 271 | unsigned int irq = GIC_HWIRQ_TO_SHARED(d->hwirq); |
39b8d525 RB |
272 | cpumask_t tmp = CPU_MASK_NONE; |
273 | unsigned long flags; | |
274 | int i; | |
275 | ||
0de26520 | 276 | cpumask_and(&tmp, cpumask, cpu_online_mask); |
39b8d525 | 277 | if (cpus_empty(tmp)) |
14d160ab | 278 | return -EINVAL; |
39b8d525 RB |
279 | |
280 | /* Assumption : cpumask refers to a single CPU */ | |
281 | spin_lock_irqsave(&gic_lock, flags); | |
39b8d525 | 282 | |
c214c035 TW |
283 | /* Re-route this IRQ */ |
284 | GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp)); | |
285 | ||
286 | /* Update the pcpu_masks */ | |
287 | for (i = 0; i < NR_CPUS; i++) | |
288 | clear_bit(irq, pcpu_masks[i].pcpu_mask); | |
289 | set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); | |
39b8d525 | 290 | |
161d049e | 291 | cpumask_copy(d->affinity, cpumask); |
39b8d525 RB |
292 | spin_unlock_irqrestore(&gic_lock, flags); |
293 | ||
161d049e | 294 | return IRQ_SET_MASK_OK_NOCOPY; |
39b8d525 RB |
295 | } |
296 | #endif | |
297 | ||
4a6a3ea3 AB |
298 | static struct irq_chip gic_level_irq_controller = { |
299 | .name = "MIPS GIC", | |
300 | .irq_mask = gic_mask_irq, | |
301 | .irq_unmask = gic_unmask_irq, | |
302 | .irq_set_type = gic_set_type, | |
303 | #ifdef CONFIG_SMP | |
304 | .irq_set_affinity = gic_set_affinity, | |
305 | #endif | |
306 | }; | |
307 | ||
308 | static struct irq_chip gic_edge_irq_controller = { | |
161d049e | 309 | .name = "MIPS GIC", |
5561c9e4 | 310 | .irq_ack = gic_ack_irq, |
161d049e | 311 | .irq_mask = gic_mask_irq, |
161d049e | 312 | .irq_unmask = gic_unmask_irq, |
95150ae8 | 313 | .irq_set_type = gic_set_type, |
39b8d525 | 314 | #ifdef CONFIG_SMP |
161d049e | 315 | .irq_set_affinity = gic_set_affinity, |
39b8d525 RB |
316 | #endif |
317 | }; | |
318 | ||
e9de688d AB |
319 | static unsigned int gic_get_local_int(void) |
320 | { | |
321 | unsigned long pending, masked; | |
322 | ||
323 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending); | |
324 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_MASK), masked); | |
325 | ||
326 | bitmap_and(&pending, &pending, &masked, GIC_NUM_LOCAL_INTRS); | |
327 | ||
328 | return find_first_bit(&pending, GIC_NUM_LOCAL_INTRS); | |
329 | } | |
330 | ||
331 | static void gic_mask_local_irq(struct irq_data *d) | |
332 | { | |
333 | int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); | |
334 | ||
335 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_RMASK), 1 << intr); | |
336 | } | |
337 | ||
338 | static void gic_unmask_local_irq(struct irq_data *d) | |
339 | { | |
340 | int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); | |
341 | ||
342 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), 1 << intr); | |
343 | } | |
344 | ||
345 | static struct irq_chip gic_local_irq_controller = { | |
346 | .name = "MIPS GIC Local", | |
347 | .irq_mask = gic_mask_local_irq, | |
348 | .irq_unmask = gic_unmask_local_irq, | |
349 | }; | |
350 | ||
351 | static void gic_mask_local_irq_all_vpes(struct irq_data *d) | |
352 | { | |
353 | int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); | |
354 | int i; | |
355 | unsigned long flags; | |
356 | ||
357 | spin_lock_irqsave(&gic_lock, flags); | |
358 | for (i = 0; i < gic_vpes; i++) { | |
359 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
360 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << intr); | |
361 | } | |
362 | spin_unlock_irqrestore(&gic_lock, flags); | |
363 | } | |
364 | ||
365 | static void gic_unmask_local_irq_all_vpes(struct irq_data *d) | |
366 | { | |
367 | int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); | |
368 | int i; | |
369 | unsigned long flags; | |
370 | ||
371 | spin_lock_irqsave(&gic_lock, flags); | |
372 | for (i = 0; i < gic_vpes; i++) { | |
373 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
374 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), 1 << intr); | |
375 | } | |
376 | spin_unlock_irqrestore(&gic_lock, flags); | |
377 | } | |
378 | ||
379 | static struct irq_chip gic_all_vpes_local_irq_controller = { | |
380 | .name = "MIPS GIC Local", | |
381 | .irq_mask = gic_mask_local_irq_all_vpes, | |
382 | .irq_unmask = gic_unmask_local_irq_all_vpes, | |
383 | }; | |
384 | ||
18743d27 | 385 | static void __gic_irq_dispatch(void) |
39b8d525 | 386 | { |
18743d27 | 387 | unsigned int intr, virq; |
6096e114 | 388 | |
e9de688d AB |
389 | while ((intr = gic_get_local_int()) != GIC_NUM_LOCAL_INTRS) { |
390 | virq = irq_linear_revmap(gic_irq_domain, | |
391 | GIC_LOCAL_TO_HWIRQ(intr)); | |
392 | do_IRQ(virq); | |
393 | } | |
394 | ||
fbd55241 | 395 | while ((intr = gic_get_int()) != gic_shared_intrs) { |
e9de688d AB |
396 | virq = irq_linear_revmap(gic_irq_domain, |
397 | GIC_SHARED_TO_HWIRQ(intr)); | |
18743d27 | 398 | do_IRQ(virq); |
39b8d525 | 399 | } |
18743d27 | 400 | } |
39b8d525 | 401 | |
18743d27 AB |
402 | static void gic_irq_dispatch(unsigned int irq, struct irq_desc *desc) |
403 | { | |
404 | __gic_irq_dispatch(); | |
405 | } | |
406 | ||
407 | #ifdef CONFIG_MIPS_GIC_IPI | |
408 | static int gic_resched_int_base; | |
409 | static int gic_call_int_base; | |
410 | ||
411 | unsigned int plat_ipi_resched_int_xlate(unsigned int cpu) | |
412 | { | |
413 | return gic_resched_int_base + cpu; | |
414 | } | |
39b8d525 | 415 | |
18743d27 AB |
416 | unsigned int plat_ipi_call_int_xlate(unsigned int cpu) |
417 | { | |
418 | return gic_call_int_base + cpu; | |
419 | } | |
39b8d525 | 420 | |
18743d27 AB |
421 | static irqreturn_t ipi_resched_interrupt(int irq, void *dev_id) |
422 | { | |
423 | scheduler_ipi(); | |
424 | ||
425 | return IRQ_HANDLED; | |
426 | } | |
427 | ||
428 | static irqreturn_t ipi_call_interrupt(int irq, void *dev_id) | |
429 | { | |
430 | smp_call_function_interrupt(); | |
431 | ||
432 | return IRQ_HANDLED; | |
433 | } | |
b0a88ae5 | 434 | |
18743d27 AB |
435 | static struct irqaction irq_resched = { |
436 | .handler = ipi_resched_interrupt, | |
437 | .flags = IRQF_PERCPU, | |
438 | .name = "IPI resched" | |
439 | }; | |
440 | ||
441 | static struct irqaction irq_call = { | |
442 | .handler = ipi_call_interrupt, | |
443 | .flags = IRQF_PERCPU, | |
444 | .name = "IPI call" | |
445 | }; | |
446 | ||
447 | static __init void gic_ipi_init_one(unsigned int intr, int cpu, | |
448 | struct irqaction *action) | |
449 | { | |
e9de688d AB |
450 | int virq = irq_create_mapping(gic_irq_domain, |
451 | GIC_SHARED_TO_HWIRQ(intr)); | |
18743d27 AB |
452 | int i; |
453 | ||
454 | GIC_SH_MAP_TO_VPE_SMASK(intr, cpu); | |
c49581a4 AB |
455 | for (i = 0; i < NR_CPUS; i++) |
456 | clear_bit(intr, pcpu_masks[i].pcpu_mask); | |
b0a88ae5 JD |
457 | set_bit(intr, pcpu_masks[cpu].pcpu_mask); |
458 | ||
18743d27 AB |
459 | irq_set_irq_type(virq, IRQ_TYPE_EDGE_RISING); |
460 | ||
461 | irq_set_handler(virq, handle_percpu_irq); | |
462 | setup_irq(virq, action); | |
39b8d525 RB |
463 | } |
464 | ||
18743d27 | 465 | static __init void gic_ipi_init(void) |
39b8d525 | 466 | { |
18743d27 AB |
467 | int i; |
468 | ||
469 | /* Use last 2 * NR_CPUS interrupts as IPIs */ | |
fbd55241 | 470 | gic_resched_int_base = gic_shared_intrs - nr_cpu_ids; |
18743d27 AB |
471 | gic_call_int_base = gic_resched_int_base - nr_cpu_ids; |
472 | ||
473 | for (i = 0; i < nr_cpu_ids; i++) { | |
474 | gic_ipi_init_one(gic_call_int_base + i, i, &irq_call); | |
475 | gic_ipi_init_one(gic_resched_int_base + i, i, &irq_resched); | |
476 | } | |
477 | } | |
478 | #else | |
479 | static inline void gic_ipi_init(void) | |
480 | { | |
481 | } | |
482 | #endif | |
483 | ||
e9de688d | 484 | static void __init gic_basic_init(void) |
18743d27 AB |
485 | { |
486 | unsigned int i; | |
98b67c37 SH |
487 | |
488 | board_bind_eic_interrupt = &gic_bind_eic_interrupt; | |
39b8d525 RB |
489 | |
490 | /* Setup defaults */ | |
fbd55241 | 491 | for (i = 0; i < gic_shared_intrs; i++) { |
39b8d525 RB |
492 | GIC_SET_POLARITY(i, GIC_POL_POS); |
493 | GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL); | |
7098f748 | 494 | GIC_CLR_INTR_MASK(i); |
39b8d525 RB |
495 | } |
496 | ||
e9de688d AB |
497 | for (i = 0; i < gic_vpes; i++) { |
498 | unsigned int j; | |
499 | ||
500 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
501 | for (j = 0; j < GIC_NUM_LOCAL_INTRS; j++) { | |
502 | if (!gic_local_irq_is_routable(j)) | |
503 | continue; | |
504 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << j); | |
505 | } | |
506 | } | |
39b8d525 RB |
507 | } |
508 | ||
e9de688d AB |
509 | static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, |
510 | irq_hw_number_t hw) | |
c49581a4 | 511 | { |
e9de688d AB |
512 | int intr = GIC_HWIRQ_TO_LOCAL(hw); |
513 | int ret = 0; | |
514 | int i; | |
515 | unsigned long flags; | |
516 | ||
517 | if (!gic_local_irq_is_routable(intr)) | |
518 | return -EPERM; | |
519 | ||
520 | /* | |
521 | * HACK: These are all really percpu interrupts, but the rest | |
522 | * of the MIPS kernel code does not use the percpu IRQ API for | |
523 | * the CP0 timer and performance counter interrupts. | |
524 | */ | |
525 | if (intr != GIC_LOCAL_INT_TIMER && intr != GIC_LOCAL_INT_PERFCTR) { | |
526 | irq_set_chip_and_handler(virq, | |
527 | &gic_local_irq_controller, | |
528 | handle_percpu_devid_irq); | |
529 | irq_set_percpu_devid(virq); | |
530 | } else { | |
531 | irq_set_chip_and_handler(virq, | |
532 | &gic_all_vpes_local_irq_controller, | |
533 | handle_percpu_irq); | |
534 | } | |
535 | ||
536 | spin_lock_irqsave(&gic_lock, flags); | |
537 | for (i = 0; i < gic_vpes; i++) { | |
538 | u32 val = GIC_MAP_TO_PIN_MSK | gic_cpu_pin; | |
539 | ||
540 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
541 | ||
542 | switch (intr) { | |
543 | case GIC_LOCAL_INT_WD: | |
544 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_WD_MAP), val); | |
545 | break; | |
546 | case GIC_LOCAL_INT_COMPARE: | |
547 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP), val); | |
548 | break; | |
549 | case GIC_LOCAL_INT_TIMER: | |
550 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), val); | |
551 | break; | |
552 | case GIC_LOCAL_INT_PERFCTR: | |
553 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), val); | |
554 | break; | |
555 | case GIC_LOCAL_INT_SWINT0: | |
556 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_SWINT0_MAP), val); | |
557 | break; | |
558 | case GIC_LOCAL_INT_SWINT1: | |
559 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_SWINT1_MAP), val); | |
560 | break; | |
561 | case GIC_LOCAL_INT_FDC: | |
562 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_FDC_MAP), val); | |
563 | break; | |
564 | default: | |
565 | pr_err("Invalid local IRQ %d\n", intr); | |
566 | ret = -EINVAL; | |
567 | break; | |
568 | } | |
569 | } | |
570 | spin_unlock_irqrestore(&gic_lock, flags); | |
571 | ||
572 | return ret; | |
573 | } | |
574 | ||
575 | static int gic_shared_irq_domain_map(struct irq_domain *d, unsigned int virq, | |
576 | irq_hw_number_t hw) | |
577 | { | |
578 | int intr = GIC_HWIRQ_TO_SHARED(hw); | |
c49581a4 AB |
579 | unsigned long flags; |
580 | ||
4a6a3ea3 AB |
581 | irq_set_chip_and_handler(virq, &gic_level_irq_controller, |
582 | handle_level_irq); | |
c49581a4 AB |
583 | |
584 | spin_lock_irqsave(&gic_lock, flags); | |
e9de688d | 585 | GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), |
18743d27 | 586 | GIC_MAP_TO_PIN_MSK | gic_cpu_pin); |
c49581a4 | 587 | /* Map to VPE 0 by default */ |
e9de688d AB |
588 | GIC_SH_MAP_TO_VPE_SMASK(intr, 0); |
589 | set_bit(intr, pcpu_masks[0].pcpu_mask); | |
c49581a4 AB |
590 | spin_unlock_irqrestore(&gic_lock, flags); |
591 | ||
592 | return 0; | |
593 | } | |
594 | ||
e9de688d AB |
595 | static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, |
596 | irq_hw_number_t hw) | |
597 | { | |
598 | if (GIC_HWIRQ_TO_LOCAL(hw) < GIC_NUM_LOCAL_INTRS) | |
599 | return gic_local_irq_domain_map(d, virq, hw); | |
600 | return gic_shared_irq_domain_map(d, virq, hw); | |
601 | } | |
602 | ||
c49581a4 AB |
603 | static struct irq_domain_ops gic_irq_domain_ops = { |
604 | .map = gic_irq_domain_map, | |
605 | .xlate = irq_domain_xlate_twocell, | |
606 | }; | |
607 | ||
39b8d525 | 608 | void __init gic_init(unsigned long gic_base_addr, |
18743d27 | 609 | unsigned long gic_addrspace_size, unsigned int cpu_vec, |
39b8d525 RB |
610 | unsigned int irqbase) |
611 | { | |
612 | unsigned int gicconfig; | |
613 | ||
614 | _gic_base = (unsigned long) ioremap_nocache(gic_base_addr, | |
615 | gic_addrspace_size); | |
39b8d525 RB |
616 | |
617 | GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); | |
fbd55241 | 618 | gic_shared_intrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >> |
39b8d525 | 619 | GIC_SH_CONFIG_NUMINTRS_SHF; |
fbd55241 | 620 | gic_shared_intrs = ((gic_shared_intrs + 1) * 8); |
39b8d525 | 621 | |
e9de688d | 622 | gic_vpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >> |
39b8d525 | 623 | GIC_SH_CONFIG_NUMVPES_SHF; |
e9de688d | 624 | gic_vpes = gic_vpes + 1; |
39b8d525 | 625 | |
18743d27 AB |
626 | if (cpu_has_veic) { |
627 | /* Always use vector 1 in EIC mode */ | |
628 | gic_cpu_pin = 0; | |
629 | set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET, | |
630 | __gic_irq_dispatch); | |
631 | } else { | |
632 | gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET; | |
633 | irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec, | |
634 | gic_irq_dispatch); | |
635 | } | |
636 | ||
e9de688d AB |
637 | gic_irq_domain = irq_domain_add_simple(NULL, GIC_NUM_LOCAL_INTRS + |
638 | gic_shared_intrs, irqbase, | |
c49581a4 AB |
639 | &gic_irq_domain_ops, NULL); |
640 | if (!gic_irq_domain) | |
641 | panic("Failed to add GIC IRQ domain"); | |
0b271f56 | 642 | |
e9de688d | 643 | gic_basic_init(); |
18743d27 AB |
644 | |
645 | gic_ipi_init(); | |
39b8d525 | 646 | } |