]>
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> | |
631330f5 | 11 | #include <linux/smp.h> |
ca4d3e67 | 12 | #include <linux/irq.h> |
dfa762e1 | 13 | #include <linux/clocksource.h> |
39b8d525 RB |
14 | |
15 | #include <asm/io.h> | |
16 | #include <asm/gic.h> | |
98b67c37 SH |
17 | #include <asm/setup.h> |
18 | #include <asm/traps.h> | |
39b8d525 RB |
19 | #include <linux/hardirq.h> |
20 | #include <asm-generic/bitops/find.h> | |
21 | ||
28ea2151 | 22 | unsigned int gic_frequency; |
ff86714f | 23 | unsigned int gic_present; |
0b271f56 SH |
24 | unsigned long _gic_base; |
25 | unsigned int gic_irq_base; | |
26 | unsigned int gic_irq_flags[GIC_NUM_INTRS]; | |
39b8d525 | 27 | |
98b67c37 SH |
28 | /* The index into this array is the vector # of the interrupt. */ |
29 | struct gic_shared_intr_map gic_shared_intr_map[GIC_NUM_INTRS]; | |
30 | ||
822350bc JD |
31 | struct gic_pcpu_mask { |
32 | DECLARE_BITMAP(pcpu_mask, GIC_NUM_INTRS); | |
33 | }; | |
34 | ||
35 | struct gic_pending_regs { | |
36 | DECLARE_BITMAP(pending, GIC_NUM_INTRS); | |
37 | }; | |
38 | ||
39 | struct gic_intrmask_regs { | |
40 | DECLARE_BITMAP(intrmask, GIC_NUM_INTRS); | |
41 | }; | |
42 | ||
0b271f56 | 43 | static struct gic_pcpu_mask pcpu_masks[NR_CPUS]; |
39b8d525 RB |
44 | static struct gic_pending_regs pending_regs[NR_CPUS]; |
45 | static struct gic_intrmask_regs intrmask_regs[NR_CPUS]; | |
46 | ||
0ab2b7d0 | 47 | #if defined(CONFIG_CSRC_GIC) || defined(CONFIG_CEVT_GIC) |
dfa762e1 SH |
48 | cycle_t gic_read_count(void) |
49 | { | |
50 | unsigned int hi, hi2, lo; | |
51 | ||
52 | do { | |
53 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi); | |
54 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_31_00), lo); | |
55 | GICREAD(GIC_REG(SHARED, GIC_SH_COUNTER_63_32), hi2); | |
56 | } while (hi2 != hi); | |
57 | ||
58 | return (((cycle_t) hi) << 32) + lo; | |
59 | } | |
0ab2b7d0 RG |
60 | |
61 | void gic_write_compare(cycle_t cnt) | |
62 | { | |
63 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), | |
64 | (int)(cnt >> 32)); | |
65 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), | |
66 | (int)(cnt & 0xffffffff)); | |
67 | } | |
68 | ||
414408d0 PB |
69 | void gic_write_cpu_compare(cycle_t cnt, int cpu) |
70 | { | |
71 | unsigned long flags; | |
72 | ||
73 | local_irq_save(flags); | |
74 | ||
75 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), cpu); | |
76 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_HI), | |
77 | (int)(cnt >> 32)); | |
78 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_LO), | |
79 | (int)(cnt & 0xffffffff)); | |
80 | ||
81 | local_irq_restore(flags); | |
82 | } | |
83 | ||
0ab2b7d0 RG |
84 | cycle_t gic_read_compare(void) |
85 | { | |
86 | unsigned int hi, lo; | |
87 | ||
88 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_HI), hi); | |
89 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_COMPARE_LO), lo); | |
90 | ||
91 | return (((cycle_t) hi) << 32) + lo; | |
92 | } | |
dfa762e1 SH |
93 | #endif |
94 | ||
98b67c37 SH |
95 | unsigned int gic_get_timer_pending(void) |
96 | { | |
97 | unsigned int vpe_pending; | |
98 | ||
99 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), 0); | |
100 | GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_PEND), vpe_pending); | |
101 | return (vpe_pending & GIC_VPE_PEND_TIMER_MSK); | |
102 | } | |
103 | ||
104 | void gic_bind_eic_interrupt(int irq, int set) | |
105 | { | |
106 | /* Convert irq vector # to hw int # */ | |
107 | irq -= GIC_PIN_TO_VEC_OFFSET; | |
108 | ||
109 | /* Set irq to use shadow set */ | |
110 | GICWRITE(GIC_REG_ADDR(VPE_LOCAL, GIC_VPE_EIC_SS(irq)), set); | |
111 | } | |
112 | ||
39b8d525 RB |
113 | void gic_send_ipi(unsigned int intr) |
114 | { | |
39b8d525 | 115 | GICWRITE(GIC_REG(SHARED, GIC_SH_WEDGE), 0x80000000 | intr); |
39b8d525 RB |
116 | } |
117 | ||
98b67c37 SH |
118 | static void gic_eic_irq_dispatch(void) |
119 | { | |
120 | unsigned int cause = read_c0_cause(); | |
121 | int irq; | |
122 | ||
123 | irq = (cause & ST0_IM) >> STATUSB_IP2; | |
124 | if (irq == 0) | |
125 | irq = -1; | |
126 | ||
127 | if (irq >= 0) | |
128 | do_IRQ(gic_irq_base + irq); | |
129 | else | |
130 | spurious_interrupt(); | |
131 | } | |
132 | ||
7098f748 | 133 | static void __init vpe_local_setup(unsigned int numvpes) |
39b8d525 | 134 | { |
98b67c37 SH |
135 | unsigned long timer_intr = GIC_INT_TMR; |
136 | unsigned long perf_intr = GIC_INT_PERFCTR; | |
39b8d525 | 137 | unsigned int vpe_ctl; |
2299c49d | 138 | int i; |
39b8d525 | 139 | |
98b67c37 SH |
140 | if (cpu_has_veic) { |
141 | /* | |
142 | * GIC timer interrupt -> CPU HW Int X (vector X+2) -> | |
143 | * map to pin X+2-1 (since GIC adds 1) | |
144 | */ | |
145 | timer_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); | |
146 | /* | |
147 | * GIC perfcnt interrupt -> CPU HW Int X (vector X+2) -> | |
148 | * map to pin X+2-1 (since GIC adds 1) | |
149 | */ | |
150 | perf_intr += (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); | |
151 | } | |
152 | ||
39b8d525 RB |
153 | /* |
154 | * Setup the default performance counter timer interrupts | |
155 | * for all VPEs | |
156 | */ | |
157 | for (i = 0; i < numvpes; i++) { | |
158 | GICWRITE(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), i); | |
159 | ||
160 | /* Are Interrupts locally routable? */ | |
161 | GICREAD(GIC_REG(VPE_OTHER, GIC_VPE_CTL), vpe_ctl); | |
162 | if (vpe_ctl & GIC_VPE_CTL_TIMER_RTBL_MSK) | |
163 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), | |
98b67c37 SH |
164 | GIC_MAP_TO_PIN_MSK | timer_intr); |
165 | if (cpu_has_veic) { | |
166 | set_vi_handler(timer_intr + GIC_PIN_TO_VEC_OFFSET, | |
167 | gic_eic_irq_dispatch); | |
168 | gic_shared_intr_map[timer_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_TIMER_MSK; | |
169 | } | |
39b8d525 RB |
170 | |
171 | if (vpe_ctl & GIC_VPE_CTL_PERFCNT_RTBL_MSK) | |
172 | GICWRITE(GIC_REG(VPE_OTHER, GIC_VPE_PERFCTR_MAP), | |
98b67c37 SH |
173 | GIC_MAP_TO_PIN_MSK | perf_intr); |
174 | if (cpu_has_veic) { | |
175 | set_vi_handler(perf_intr + GIC_PIN_TO_VEC_OFFSET, gic_eic_irq_dispatch); | |
176 | gic_shared_intr_map[perf_intr + GIC_PIN_TO_VEC_OFFSET].local_intr_mask |= GIC_VPE_RMASK_PERFCNT_MSK; | |
177 | } | |
39b8d525 RB |
178 | } |
179 | } | |
180 | ||
0ab2b7d0 RG |
181 | unsigned int gic_compare_int(void) |
182 | { | |
183 | unsigned int pending; | |
184 | ||
185 | GICREAD(GIC_REG(VPE_LOCAL, GIC_VPE_PEND), pending); | |
186 | if (pending & GIC_VPE_PEND_CMP_MSK) | |
187 | return 1; | |
188 | else | |
189 | return 0; | |
190 | } | |
191 | ||
39b8d525 RB |
192 | unsigned int gic_get_int(void) |
193 | { | |
194 | unsigned int i; | |
195 | unsigned long *pending, *intrmask, *pcpu_mask; | |
196 | unsigned long *pending_abs, *intrmask_abs; | |
197 | ||
198 | /* Get per-cpu bitmaps */ | |
199 | pending = pending_regs[smp_processor_id()].pending; | |
200 | intrmask = intrmask_regs[smp_processor_id()].intrmask; | |
201 | pcpu_mask = pcpu_masks[smp_processor_id()].pcpu_mask; | |
202 | ||
203 | pending_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
204 | GIC_SH_PEND_31_0_OFS); | |
205 | intrmask_abs = (unsigned long *) GIC_REG_ABS_ADDR(SHARED, | |
206 | GIC_SH_MASK_31_0_OFS); | |
207 | ||
208 | for (i = 0; i < BITS_TO_LONGS(GIC_NUM_INTRS); i++) { | |
209 | GICREAD(*pending_abs, pending[i]); | |
210 | GICREAD(*intrmask_abs, intrmask[i]); | |
211 | pending_abs++; | |
212 | intrmask_abs++; | |
213 | } | |
214 | ||
215 | bitmap_and(pending, pending, intrmask, GIC_NUM_INTRS); | |
216 | bitmap_and(pending, pending, pcpu_mask, GIC_NUM_INTRS); | |
217 | ||
2299c49d | 218 | return find_first_bit(pending, GIC_NUM_INTRS); |
39b8d525 RB |
219 | } |
220 | ||
161d049e | 221 | static void gic_mask_irq(struct irq_data *d) |
39b8d525 | 222 | { |
2299c49d | 223 | GIC_CLR_INTR_MASK(d->irq - gic_irq_base); |
39b8d525 RB |
224 | } |
225 | ||
161d049e | 226 | static void gic_unmask_irq(struct irq_data *d) |
39b8d525 | 227 | { |
2299c49d | 228 | GIC_SET_INTR_MASK(d->irq - gic_irq_base); |
39b8d525 RB |
229 | } |
230 | ||
231 | #ifdef CONFIG_SMP | |
39b8d525 RB |
232 | static DEFINE_SPINLOCK(gic_lock); |
233 | ||
161d049e TG |
234 | static int gic_set_affinity(struct irq_data *d, const struct cpumask *cpumask, |
235 | bool force) | |
39b8d525 | 236 | { |
2299c49d | 237 | unsigned int irq = (d->irq - gic_irq_base); |
39b8d525 RB |
238 | cpumask_t tmp = CPU_MASK_NONE; |
239 | unsigned long flags; | |
240 | int i; | |
241 | ||
0de26520 | 242 | cpumask_and(&tmp, cpumask, cpu_online_mask); |
39b8d525 | 243 | if (cpus_empty(tmp)) |
d5dedd45 | 244 | return -1; |
39b8d525 RB |
245 | |
246 | /* Assumption : cpumask refers to a single CPU */ | |
247 | spin_lock_irqsave(&gic_lock, flags); | |
39b8d525 | 248 | |
c214c035 TW |
249 | /* Re-route this IRQ */ |
250 | GIC_SH_MAP_TO_VPE_SMASK(irq, first_cpu(tmp)); | |
251 | ||
252 | /* Update the pcpu_masks */ | |
253 | for (i = 0; i < NR_CPUS; i++) | |
254 | clear_bit(irq, pcpu_masks[i].pcpu_mask); | |
255 | set_bit(irq, pcpu_masks[first_cpu(tmp)].pcpu_mask); | |
39b8d525 | 256 | |
161d049e | 257 | cpumask_copy(d->affinity, cpumask); |
39b8d525 RB |
258 | spin_unlock_irqrestore(&gic_lock, flags); |
259 | ||
161d049e | 260 | return IRQ_SET_MASK_OK_NOCOPY; |
39b8d525 RB |
261 | } |
262 | #endif | |
263 | ||
264 | static struct irq_chip gic_irq_controller = { | |
161d049e TG |
265 | .name = "MIPS GIC", |
266 | .irq_ack = gic_irq_ack, | |
267 | .irq_mask = gic_mask_irq, | |
268 | .irq_mask_ack = gic_mask_irq, | |
269 | .irq_unmask = gic_unmask_irq, | |
ec167f2d | 270 | .irq_eoi = gic_finish_irq, |
39b8d525 | 271 | #ifdef CONFIG_SMP |
161d049e | 272 | .irq_set_affinity = gic_set_affinity, |
39b8d525 RB |
273 | #endif |
274 | }; | |
275 | ||
7098f748 CD |
276 | static void __init gic_setup_intr(unsigned int intr, unsigned int cpu, |
277 | unsigned int pin, unsigned int polarity, unsigned int trigtype, | |
278 | unsigned int flags) | |
39b8d525 | 279 | { |
98b67c37 SH |
280 | struct gic_shared_intr_map *map_ptr; |
281 | ||
39b8d525 RB |
282 | /* Setup Intr to Pin mapping */ |
283 | if (pin & GIC_MAP_TO_NMI_MSK) { | |
284 | GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), pin); | |
285 | /* FIXME: hack to route NMI to all cpu's */ | |
286 | for (cpu = 0; cpu < NR_CPUS; cpu += 32) { | |
287 | GICWRITE(GIC_REG_ADDR(SHARED, | |
288 | GIC_SH_MAP_TO_VPE_REG_OFF(intr, cpu)), | |
289 | 0xffffffff); | |
290 | } | |
291 | } else { | |
292 | GICWRITE(GIC_REG_ADDR(SHARED, GIC_SH_MAP_TO_PIN(intr)), | |
293 | GIC_MAP_TO_PIN_MSK | pin); | |
294 | /* Setup Intr to CPU mapping */ | |
295 | GIC_SH_MAP_TO_VPE_SMASK(intr, cpu); | |
98b67c37 SH |
296 | if (cpu_has_veic) { |
297 | set_vi_handler(pin + GIC_PIN_TO_VEC_OFFSET, | |
298 | gic_eic_irq_dispatch); | |
299 | map_ptr = &gic_shared_intr_map[pin + GIC_PIN_TO_VEC_OFFSET]; | |
300 | if (map_ptr->num_shared_intr >= GIC_MAX_SHARED_INTR) | |
301 | BUG(); | |
302 | map_ptr->intr_list[map_ptr->num_shared_intr++] = intr; | |
303 | } | |
39b8d525 RB |
304 | } |
305 | ||
306 | /* Setup Intr Polarity */ | |
307 | GIC_SET_POLARITY(intr, polarity); | |
308 | ||
309 | /* Setup Intr Trigger Type */ | |
310 | GIC_SET_TRIGGER(intr, trigtype); | |
311 | ||
312 | /* Init Intr Masks */ | |
7098f748 CD |
313 | GIC_CLR_INTR_MASK(intr); |
314 | /* Initialise per-cpu Interrupt software masks */ | |
315 | if (flags & GIC_FLAG_IPI) | |
316 | set_bit(intr, pcpu_masks[cpu].pcpu_mask); | |
98b67c37 | 317 | if ((flags & GIC_FLAG_TRANSPARENT) && (cpu_has_veic == 0)) |
7098f748 CD |
318 | GIC_SET_INTR_MASK(intr); |
319 | if (trigtype == GIC_TRIG_EDGE) | |
0b271f56 | 320 | gic_irq_flags[intr] |= GIC_TRIG_EDGE; |
39b8d525 RB |
321 | } |
322 | ||
7098f748 CD |
323 | static void __init gic_basic_init(int numintrs, int numvpes, |
324 | struct gic_intr_map *intrmap, int mapsize) | |
39b8d525 RB |
325 | { |
326 | unsigned int i, cpu; | |
98b67c37 SH |
327 | unsigned int pin_offset = 0; |
328 | ||
329 | board_bind_eic_interrupt = &gic_bind_eic_interrupt; | |
39b8d525 RB |
330 | |
331 | /* Setup defaults */ | |
7098f748 | 332 | for (i = 0; i < numintrs; i++) { |
39b8d525 RB |
333 | GIC_SET_POLARITY(i, GIC_POL_POS); |
334 | GIC_SET_TRIGGER(i, GIC_TRIG_LEVEL); | |
7098f748 | 335 | GIC_CLR_INTR_MASK(i); |
98b67c37 | 336 | if (i < GIC_NUM_INTRS) { |
7098f748 | 337 | gic_irq_flags[i] = 0; |
98b67c37 SH |
338 | gic_shared_intr_map[i].num_shared_intr = 0; |
339 | gic_shared_intr_map[i].local_intr_mask = 0; | |
340 | } | |
39b8d525 RB |
341 | } |
342 | ||
98b67c37 SH |
343 | /* |
344 | * In EIC mode, the HW_INT# is offset by (2-1). Need to subtract | |
345 | * one because the GIC will add one (since 0=no intr). | |
346 | */ | |
347 | if (cpu_has_veic) | |
348 | pin_offset = (GIC_CPU_TO_VEC_OFFSET - GIC_PIN_TO_VEC_OFFSET); | |
349 | ||
39b8d525 | 350 | /* Setup specifics */ |
7098f748 CD |
351 | for (i = 0; i < mapsize; i++) { |
352 | cpu = intrmap[i].cpunum; | |
863cb9ba | 353 | if (cpu == GIC_UNUSED) |
39b8d525 | 354 | continue; |
7098f748 | 355 | if (cpu == 0 && i != 0 && intrmap[i].flags == 0) |
a214cef9 | 356 | continue; |
7098f748 CD |
357 | gic_setup_intr(i, |
358 | intrmap[i].cpunum, | |
98b67c37 | 359 | intrmap[i].pin + pin_offset, |
7098f748 CD |
360 | intrmap[i].polarity, |
361 | intrmap[i].trigtype, | |
362 | intrmap[i].flags); | |
39b8d525 RB |
363 | } |
364 | ||
365 | vpe_local_setup(numvpes); | |
39b8d525 RB |
366 | } |
367 | ||
368 | void __init gic_init(unsigned long gic_base_addr, | |
369 | unsigned long gic_addrspace_size, | |
370 | struct gic_intr_map *intr_map, unsigned int intr_map_size, | |
371 | unsigned int irqbase) | |
372 | { | |
373 | unsigned int gicconfig; | |
7098f748 | 374 | int numvpes, numintrs; |
39b8d525 RB |
375 | |
376 | _gic_base = (unsigned long) ioremap_nocache(gic_base_addr, | |
377 | gic_addrspace_size); | |
0b271f56 | 378 | gic_irq_base = irqbase; |
39b8d525 RB |
379 | |
380 | GICREAD(GIC_REG(SHARED, GIC_SH_CONFIG), gicconfig); | |
381 | numintrs = (gicconfig & GIC_SH_CONFIG_NUMINTRS_MSK) >> | |
382 | GIC_SH_CONFIG_NUMINTRS_SHF; | |
383 | numintrs = ((numintrs + 1) * 8); | |
384 | ||
385 | numvpes = (gicconfig & GIC_SH_CONFIG_NUMVPES_MSK) >> | |
386 | GIC_SH_CONFIG_NUMVPES_SHF; | |
3234f446 | 387 | numvpes = numvpes + 1; |
39b8d525 | 388 | |
7098f748 | 389 | gic_basic_init(numintrs, numvpes, intr_map, intr_map_size); |
0b271f56 SH |
390 | |
391 | gic_platform_init(numintrs, &gic_irq_controller); | |
39b8d525 | 392 | } |