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