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