]>
Commit | Line | Data |
---|---|---|
5b3b1688 DD |
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 | * | |
0c326387 | 6 | * Copyright (C) 2004-2008, 2009, 2010, 2011 Cavium Networks |
5b3b1688 | 7 | */ |
0c326387 | 8 | |
5b3b1688 | 9 | #include <linux/interrupt.h> |
0c326387 DD |
10 | #include <linux/bitops.h> |
11 | #include <linux/percpu.h> | |
12 | #include <linux/irq.h> | |
631330f5 | 13 | #include <linux/smp.h> |
5b3b1688 DD |
14 | |
15 | #include <asm/octeon/octeon.h> | |
16 | ||
39961422 DD |
17 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock); |
18 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock); | |
5b3b1688 | 19 | |
0c326387 DD |
20 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror); |
21 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror); | |
22 | ||
23 | static __read_mostly u8 octeon_irq_ciu_to_irq[8][64]; | |
24 | ||
25 | union octeon_ciu_chip_data { | |
26 | void *p; | |
27 | unsigned long l; | |
28 | struct { | |
29 | unsigned int line:6; | |
30 | unsigned int bit:6; | |
31 | } s; | |
32 | }; | |
33 | ||
34 | struct octeon_core_chip_data { | |
35 | struct mutex core_irq_mutex; | |
36 | bool current_en; | |
37 | bool desired_en; | |
38 | u8 bit; | |
39 | }; | |
40 | ||
41 | #define MIPS_CORE_IRQ_LINES 8 | |
42 | ||
43 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; | |
44 | ||
45 | static void __init octeon_irq_set_ciu_mapping(int irq, int line, int bit, | |
46 | struct irq_chip *chip, | |
47 | irq_flow_handler_t handler) | |
48 | { | |
49 | union octeon_ciu_chip_data cd; | |
50 | ||
51 | irq_set_chip_and_handler(irq, chip, handler); | |
52 | ||
53 | cd.l = 0; | |
54 | cd.s.line = line; | |
55 | cd.s.bit = bit; | |
56 | ||
57 | irq_set_chip_data(irq, cd.p); | |
58 | octeon_irq_ciu_to_irq[line][bit] = irq; | |
59 | } | |
60 | ||
cd847b78 DD |
61 | static int octeon_coreid_for_cpu(int cpu) |
62 | { | |
63 | #ifdef CONFIG_SMP | |
64 | return cpu_logical_map(cpu); | |
65 | #else | |
66 | return cvmx_get_core_num(); | |
67 | #endif | |
68 | } | |
69 | ||
0c326387 DD |
70 | static int octeon_cpu_for_coreid(int coreid) |
71 | { | |
72 | #ifdef CONFIG_SMP | |
73 | return cpu_number_map(coreid); | |
74 | #else | |
75 | return smp_processor_id(); | |
76 | #endif | |
77 | } | |
78 | ||
79 | static void octeon_irq_core_ack(struct irq_data *data) | |
5b3b1688 | 80 | { |
0c326387 DD |
81 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
82 | unsigned int bit = cd->bit; | |
83 | ||
5b3b1688 DD |
84 | /* |
85 | * We don't need to disable IRQs to make these atomic since | |
86 | * they are already disabled earlier in the low level | |
87 | * interrupt code. | |
88 | */ | |
89 | clear_c0_status(0x100 << bit); | |
90 | /* The two user interrupts must be cleared manually. */ | |
91 | if (bit < 2) | |
92 | clear_c0_cause(0x100 << bit); | |
93 | } | |
94 | ||
0c326387 | 95 | static void octeon_irq_core_eoi(struct irq_data *data) |
5b3b1688 | 96 | { |
0c326387 DD |
97 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
98 | ||
5b3b1688 DD |
99 | /* |
100 | * We don't need to disable IRQs to make these atomic since | |
101 | * they are already disabled earlier in the low level | |
102 | * interrupt code. | |
103 | */ | |
0c326387 | 104 | set_c0_status(0x100 << cd->bit); |
5b3b1688 DD |
105 | } |
106 | ||
0c326387 | 107 | static void octeon_irq_core_set_enable_local(void *arg) |
5b3b1688 | 108 | { |
0c326387 DD |
109 | struct irq_data *data = arg; |
110 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
111 | unsigned int mask = 0x100 << cd->bit; | |
5b3b1688 DD |
112 | |
113 | /* | |
0c326387 | 114 | * Interrupts are already disabled, so these are atomic. |
5b3b1688 | 115 | */ |
0c326387 DD |
116 | if (cd->desired_en) |
117 | set_c0_status(mask); | |
118 | else | |
119 | clear_c0_status(mask); | |
120 | ||
5b3b1688 DD |
121 | } |
122 | ||
0c326387 | 123 | static void octeon_irq_core_disable(struct irq_data *data) |
5b3b1688 | 124 | { |
0c326387 DD |
125 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
126 | cd->desired_en = false; | |
5b3b1688 DD |
127 | } |
128 | ||
0c326387 | 129 | static void octeon_irq_core_enable(struct irq_data *data) |
5b3b1688 | 130 | { |
0c326387 DD |
131 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
132 | cd->desired_en = true; | |
5b3b1688 DD |
133 | } |
134 | ||
0c326387 DD |
135 | static void octeon_irq_core_bus_lock(struct irq_data *data) |
136 | { | |
137 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
5b3b1688 | 138 | |
0c326387 DD |
139 | mutex_lock(&cd->core_irq_mutex); |
140 | } | |
5b3b1688 | 141 | |
0c326387 | 142 | static void octeon_irq_core_bus_sync_unlock(struct irq_data *data) |
5b3b1688 | 143 | { |
0c326387 DD |
144 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
145 | ||
146 | if (cd->desired_en != cd->current_en) { | |
147 | on_each_cpu(octeon_irq_core_set_enable_local, data, 1); | |
148 | ||
149 | cd->current_en = cd->desired_en; | |
5aae1fd4 DD |
150 | } |
151 | ||
0c326387 | 152 | mutex_unlock(&cd->core_irq_mutex); |
5b3b1688 DD |
153 | } |
154 | ||
0c326387 DD |
155 | static struct irq_chip octeon_irq_chip_core = { |
156 | .name = "Core", | |
157 | .irq_enable = octeon_irq_core_enable, | |
158 | .irq_disable = octeon_irq_core_disable, | |
159 | .irq_ack = octeon_irq_core_ack, | |
160 | .irq_eoi = octeon_irq_core_eoi, | |
161 | .irq_bus_lock = octeon_irq_core_bus_lock, | |
162 | .irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock, | |
163 | ||
5b7cd6fd TG |
164 | .irq_cpu_online = octeon_irq_core_eoi, |
165 | .irq_cpu_offline = octeon_irq_core_ack, | |
166 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
167 | }; |
168 | ||
169 | static void __init octeon_irq_init_core(void) | |
170 | { | |
171 | int i; | |
172 | int irq; | |
173 | struct octeon_core_chip_data *cd; | |
174 | ||
175 | for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) { | |
176 | cd = &octeon_irq_core_chip_data[i]; | |
177 | cd->current_en = false; | |
178 | cd->desired_en = false; | |
179 | cd->bit = i; | |
180 | mutex_init(&cd->core_irq_mutex); | |
181 | ||
182 | irq = OCTEON_IRQ_SW0 + i; | |
183 | switch (irq) { | |
184 | case OCTEON_IRQ_TIMER: | |
185 | case OCTEON_IRQ_SW0: | |
186 | case OCTEON_IRQ_SW1: | |
187 | case OCTEON_IRQ_5: | |
188 | case OCTEON_IRQ_PERF: | |
189 | irq_set_chip_data(irq, cd); | |
190 | irq_set_chip_and_handler(irq, &octeon_irq_chip_core, | |
191 | handle_percpu_irq); | |
192 | break; | |
193 | default: | |
194 | break; | |
195 | } | |
196 | } | |
197 | } | |
198 | ||
199 | static int next_cpu_for_irq(struct irq_data *data) | |
5aae1fd4 DD |
200 | { |
201 | ||
202 | #ifdef CONFIG_SMP | |
0c326387 DD |
203 | int cpu; |
204 | int weight = cpumask_weight(data->affinity); | |
5aae1fd4 DD |
205 | |
206 | if (weight > 1) { | |
0c326387 | 207 | cpu = smp_processor_id(); |
5aae1fd4 | 208 | for (;;) { |
0c326387 | 209 | cpu = cpumask_next(cpu, data->affinity); |
5aae1fd4 DD |
210 | if (cpu >= nr_cpu_ids) { |
211 | cpu = -1; | |
212 | continue; | |
213 | } else if (cpumask_test_cpu(cpu, cpu_online_mask)) { | |
214 | break; | |
215 | } | |
216 | } | |
5aae1fd4 | 217 | } else if (weight == 1) { |
0c326387 | 218 | cpu = cpumask_first(data->affinity); |
5aae1fd4 | 219 | } else { |
0c326387 | 220 | cpu = smp_processor_id(); |
5aae1fd4 | 221 | } |
0c326387 | 222 | return cpu; |
5aae1fd4 | 223 | #else |
0c326387 | 224 | return smp_processor_id(); |
5aae1fd4 DD |
225 | #endif |
226 | } | |
227 | ||
0c326387 | 228 | static void octeon_irq_ciu_enable(struct irq_data *data) |
5aae1fd4 | 229 | { |
0c326387 DD |
230 | int cpu = next_cpu_for_irq(data); |
231 | int coreid = octeon_coreid_for_cpu(cpu); | |
232 | unsigned long *pen; | |
5aae1fd4 | 233 | unsigned long flags; |
0c326387 DD |
234 | union octeon_ciu_chip_data cd; |
235 | ||
236 | cd.p = irq_data_get_irq_chip_data(data); | |
5aae1fd4 | 237 | |
0c326387 DD |
238 | if (cd.s.line == 0) { |
239 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
240 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
241 | set_bit(cd.s.bit, pen); | |
242 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
243 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
244 | } else { | |
245 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
246 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
247 | set_bit(cd.s.bit, pen); | |
248 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
249 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
250 | } | |
5aae1fd4 DD |
251 | } |
252 | ||
0c326387 DD |
253 | static void octeon_irq_ciu_enable_local(struct irq_data *data) |
254 | { | |
255 | unsigned long *pen; | |
256 | unsigned long flags; | |
257 | union octeon_ciu_chip_data cd; | |
258 | ||
259 | cd.p = irq_data_get_irq_chip_data(data); | |
260 | ||
261 | if (cd.s.line == 0) { | |
262 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
263 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); | |
264 | set_bit(cd.s.bit, pen); | |
265 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); | |
266 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
267 | } else { | |
268 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
269 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); | |
270 | set_bit(cd.s.bit, pen); | |
271 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); | |
272 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
273 | } | |
274 | } | |
275 | ||
276 | static void octeon_irq_ciu_disable_local(struct irq_data *data) | |
277 | { | |
278 | unsigned long *pen; | |
279 | unsigned long flags; | |
280 | union octeon_ciu_chip_data cd; | |
281 | ||
282 | cd.p = irq_data_get_irq_chip_data(data); | |
283 | ||
284 | if (cd.s.line == 0) { | |
285 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
286 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); | |
287 | clear_bit(cd.s.bit, pen); | |
288 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); | |
289 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
290 | } else { | |
291 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
292 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); | |
293 | clear_bit(cd.s.bit, pen); | |
294 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); | |
295 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
296 | } | |
297 | } | |
298 | ||
299 | static void octeon_irq_ciu_disable_all(struct irq_data *data) | |
5b3b1688 | 300 | { |
5b3b1688 | 301 | unsigned long flags; |
0c326387 DD |
302 | unsigned long *pen; |
303 | int cpu; | |
304 | union octeon_ciu_chip_data cd; | |
305 | ||
306 | wmb(); /* Make sure flag changes arrive before register updates. */ | |
5b3b1688 | 307 | |
0c326387 DD |
308 | cd.p = irq_data_get_irq_chip_data(data); |
309 | ||
310 | if (cd.s.line == 0) { | |
311 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
312 | for_each_online_cpu(cpu) { | |
313 | int coreid = octeon_coreid_for_cpu(cpu); | |
314 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
315 | clear_bit(cd.s.bit, pen); | |
316 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
317 | } | |
318 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
319 | } else { | |
320 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
321 | for_each_online_cpu(cpu) { | |
322 | int coreid = octeon_coreid_for_cpu(cpu); | |
323 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
324 | clear_bit(cd.s.bit, pen); | |
325 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
326 | } | |
327 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
328 | } | |
5b3b1688 DD |
329 | } |
330 | ||
0c326387 | 331 | static void octeon_irq_ciu_enable_all(struct irq_data *data) |
5b3b1688 | 332 | { |
5b3b1688 | 333 | unsigned long flags; |
0c326387 | 334 | unsigned long *pen; |
5b3b1688 | 335 | int cpu; |
0c326387 DD |
336 | union octeon_ciu_chip_data cd; |
337 | ||
338 | cd.p = irq_data_get_irq_chip_data(data); | |
339 | ||
340 | if (cd.s.line == 0) { | |
341 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
342 | for_each_online_cpu(cpu) { | |
343 | int coreid = octeon_coreid_for_cpu(cpu); | |
344 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
345 | set_bit(cd.s.bit, pen); | |
346 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
347 | } | |
348 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
349 | } else { | |
350 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
351 | for_each_online_cpu(cpu) { | |
352 | int coreid = octeon_coreid_for_cpu(cpu); | |
353 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
354 | set_bit(cd.s.bit, pen); | |
355 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
356 | } | |
357 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
5b3b1688 | 358 | } |
cd847b78 DD |
359 | } |
360 | ||
361 | /* | |
5aae1fd4 DD |
362 | * Enable the irq on the next core in the affinity set for chips that |
363 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 364 | */ |
0c326387 | 365 | static void octeon_irq_ciu_enable_v2(struct irq_data *data) |
cd847b78 | 366 | { |
0c326387 DD |
367 | u64 mask; |
368 | int cpu = next_cpu_for_irq(data); | |
369 | union octeon_ciu_chip_data cd; | |
cd847b78 | 370 | |
0c326387 DD |
371 | cd.p = irq_data_get_irq_chip_data(data); |
372 | mask = 1ull << (cd.s.bit); | |
373 | ||
374 | /* | |
375 | * Called under the desc lock, so these should never get out | |
376 | * of sync. | |
377 | */ | |
378 | if (cd.s.line == 0) { | |
379 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
380 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
5aae1fd4 | 381 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); |
0c326387 DD |
382 | } else { |
383 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
384 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
385 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
5aae1fd4 | 386 | } |
cd847b78 DD |
387 | } |
388 | ||
389 | /* | |
5aae1fd4 DD |
390 | * Enable the irq on the current CPU for chips that |
391 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 392 | */ |
0c326387 | 393 | static void octeon_irq_ciu_enable_local_v2(struct irq_data *data) |
cd847b78 | 394 | { |
0c326387 DD |
395 | u64 mask; |
396 | union octeon_ciu_chip_data cd; | |
397 | ||
398 | cd.p = irq_data_get_irq_chip_data(data); | |
399 | mask = 1ull << (cd.s.bit); | |
cd847b78 | 400 | |
0c326387 DD |
401 | if (cd.s.line == 0) { |
402 | int index = cvmx_get_core_num() * 2; | |
403 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
404 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
405 | } else { | |
406 | int index = cvmx_get_core_num() * 2 + 1; | |
407 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
408 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
409 | } | |
410 | } | |
411 | ||
412 | static void octeon_irq_ciu_disable_local_v2(struct irq_data *data) | |
413 | { | |
414 | u64 mask; | |
415 | union octeon_ciu_chip_data cd; | |
416 | ||
417 | cd.p = irq_data_get_irq_chip_data(data); | |
418 | mask = 1ull << (cd.s.bit); | |
419 | ||
420 | if (cd.s.line == 0) { | |
421 | int index = cvmx_get_core_num() * 2; | |
422 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
423 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
424 | } else { | |
425 | int index = cvmx_get_core_num() * 2 + 1; | |
426 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
427 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
428 | } | |
cd847b78 DD |
429 | } |
430 | ||
86568dc4 | 431 | /* |
0c326387 | 432 | * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq. |
86568dc4 | 433 | */ |
0c326387 DD |
434 | static void octeon_irq_ciu_ack(struct irq_data *data) |
435 | { | |
436 | u64 mask; | |
437 | union octeon_ciu_chip_data cd; | |
438 | ||
439 | cd.p = data->chip_data; | |
440 | mask = 1ull << (cd.s.bit); | |
441 | ||
442 | if (cd.s.line == 0) { | |
443 | int index = cvmx_get_core_num() * 2; | |
5aae1fd4 | 444 | cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask); |
0c326387 DD |
445 | } else { |
446 | cvmx_write_csr(CVMX_CIU_INT_SUM1, mask); | |
5aae1fd4 | 447 | } |
86568dc4 DD |
448 | } |
449 | ||
dbb103b2 | 450 | /* |
0c326387 | 451 | * Disable the irq on the all cores for chips that have the EN*_W1{S,C} |
dbb103b2 DD |
452 | * registers. |
453 | */ | |
0c326387 | 454 | static void octeon_irq_ciu_disable_all_v2(struct irq_data *data) |
dbb103b2 | 455 | { |
0c326387 DD |
456 | int cpu; |
457 | u64 mask; | |
458 | union octeon_ciu_chip_data cd; | |
dbb103b2 | 459 | |
0c326387 DD |
460 | wmb(); /* Make sure flag changes arrive before register updates. */ |
461 | ||
462 | cd.p = data->chip_data; | |
463 | mask = 1ull << (cd.s.bit); | |
464 | ||
465 | if (cd.s.line == 0) { | |
466 | for_each_online_cpu(cpu) { | |
467 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
468 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
469 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
470 | } | |
471 | } else { | |
472 | for_each_online_cpu(cpu) { | |
473 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
474 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
475 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
476 | } | |
477 | } | |
dbb103b2 DD |
478 | } |
479 | ||
cd847b78 | 480 | /* |
0c326387 | 481 | * Enable the irq on the all cores for chips that have the EN*_W1{S,C} |
cd847b78 DD |
482 | * registers. |
483 | */ | |
0c326387 | 484 | static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) |
cd847b78 | 485 | { |
cd847b78 | 486 | int cpu; |
0c326387 DD |
487 | u64 mask; |
488 | union octeon_ciu_chip_data cd; | |
489 | ||
490 | cd.p = data->chip_data; | |
491 | mask = 1ull << (cd.s.bit); | |
492 | ||
493 | if (cd.s.line == 0) { | |
494 | for_each_online_cpu(cpu) { | |
495 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
496 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
497 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
498 | } | |
499 | } else { | |
500 | for_each_online_cpu(cpu) { | |
501 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
502 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
503 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
504 | } | |
cd847b78 | 505 | } |
5b3b1688 DD |
506 | } |
507 | ||
508 | #ifdef CONFIG_SMP | |
0c326387 DD |
509 | |
510 | static void octeon_irq_cpu_offline_ciu(struct irq_data *data) | |
511 | { | |
512 | int cpu = smp_processor_id(); | |
513 | cpumask_t new_affinity; | |
514 | ||
515 | if (!cpumask_test_cpu(cpu, data->affinity)) | |
516 | return; | |
517 | ||
518 | if (cpumask_weight(data->affinity) > 1) { | |
519 | /* | |
520 | * It has multi CPU affinity, just remove this CPU | |
521 | * from the affinity set. | |
522 | */ | |
523 | cpumask_copy(&new_affinity, data->affinity); | |
524 | cpumask_clear_cpu(cpu, &new_affinity); | |
525 | } else { | |
526 | /* Otherwise, put it on lowest numbered online CPU. */ | |
527 | cpumask_clear(&new_affinity); | |
528 | cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); | |
529 | } | |
530 | __irq_set_affinity_locked(data, &new_affinity); | |
531 | } | |
532 | ||
533 | static int octeon_irq_ciu_set_affinity(struct irq_data *data, | |
534 | const struct cpumask *dest, bool force) | |
5b3b1688 DD |
535 | { |
536 | int cpu; | |
5b7cd6fd | 537 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
b6b74d54 | 538 | unsigned long flags; |
0c326387 DD |
539 | union octeon_ciu_chip_data cd; |
540 | ||
541 | cd.p = data->chip_data; | |
5b3b1688 | 542 | |
5aae1fd4 DD |
543 | /* |
544 | * For non-v2 CIU, we will allow only single CPU affinity. | |
545 | * This removes the need to do locking in the .ack/.eoi | |
546 | * functions. | |
547 | */ | |
548 | if (cpumask_weight(dest) != 1) | |
549 | return -EINVAL; | |
550 | ||
5b7cd6fd | 551 | if (!enable_one) |
0c326387 DD |
552 | return 0; |
553 | ||
554 | if (cd.s.line == 0) { | |
555 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
556 | for_each_online_cpu(cpu) { | |
557 | int coreid = octeon_coreid_for_cpu(cpu); | |
558 | unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
559 | ||
560 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 561 | enable_one = false; |
0c326387 DD |
562 | set_bit(cd.s.bit, pen); |
563 | } else { | |
564 | clear_bit(cd.s.bit, pen); | |
565 | } | |
566 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
567 | } | |
568 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
569 | } else { | |
570 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
571 | for_each_online_cpu(cpu) { | |
572 | int coreid = octeon_coreid_for_cpu(cpu); | |
573 | unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
574 | ||
575 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 576 | enable_one = false; |
0c326387 DD |
577 | set_bit(cd.s.bit, pen); |
578 | } else { | |
579 | clear_bit(cd.s.bit, pen); | |
580 | } | |
581 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
5aae1fd4 | 582 | } |
0c326387 | 583 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); |
5b3b1688 | 584 | } |
d5dedd45 | 585 | return 0; |
5b3b1688 | 586 | } |
cd847b78 DD |
587 | |
588 | /* | |
589 | * Set affinity for the irq for chips that have the EN*_W1{S,C} | |
590 | * registers. | |
591 | */ | |
0c326387 DD |
592 | static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, |
593 | const struct cpumask *dest, | |
594 | bool force) | |
cd847b78 DD |
595 | { |
596 | int cpu; | |
5b7cd6fd | 597 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
0c326387 DD |
598 | u64 mask; |
599 | union octeon_ciu_chip_data cd; | |
600 | ||
5b7cd6fd | 601 | if (!enable_one) |
0c326387 DD |
602 | return 0; |
603 | ||
604 | cd.p = data->chip_data; | |
605 | mask = 1ull << cd.s.bit; | |
606 | ||
607 | if (cd.s.line == 0) { | |
608 | for_each_online_cpu(cpu) { | |
609 | unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
610 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
611 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 612 | enable_one = false; |
0c326387 DD |
613 | set_bit(cd.s.bit, pen); |
614 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
615 | } else { | |
616 | clear_bit(cd.s.bit, pen); | |
617 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
618 | } | |
619 | } | |
620 | } else { | |
621 | for_each_online_cpu(cpu) { | |
622 | unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
623 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
624 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 625 | enable_one = false; |
0c326387 DD |
626 | set_bit(cd.s.bit, pen); |
627 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
628 | } else { | |
629 | clear_bit(cd.s.bit, pen); | |
630 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
631 | } | |
5aae1fd4 | 632 | } |
cd847b78 DD |
633 | } |
634 | return 0; | |
635 | } | |
5b3b1688 DD |
636 | #endif |
637 | ||
0c326387 DD |
638 | /* |
639 | * The v1 CIU code already masks things, so supply a dummy version to | |
640 | * the core chip code. | |
641 | */ | |
642 | static void octeon_irq_dummy_mask(struct irq_data *data) | |
643 | { | |
0c326387 DD |
644 | } |
645 | ||
cd847b78 DD |
646 | /* |
647 | * Newer octeon chips have support for lockless CIU operation. | |
648 | */ | |
0c326387 DD |
649 | static struct irq_chip octeon_irq_chip_ciu_v2 = { |
650 | .name = "CIU", | |
651 | .irq_enable = octeon_irq_ciu_enable_v2, | |
652 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
653 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
654 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
cd847b78 | 655 | #ifdef CONFIG_SMP |
0c326387 DD |
656 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, |
657 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
cd847b78 DD |
658 | #endif |
659 | }; | |
660 | ||
0c326387 DD |
661 | static struct irq_chip octeon_irq_chip_ciu_edge_v2 = { |
662 | .name = "CIU-E", | |
663 | .irq_enable = octeon_irq_ciu_enable_v2, | |
664 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
665 | .irq_ack = octeon_irq_ciu_ack, | |
666 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
667 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
5b3b1688 | 668 | #ifdef CONFIG_SMP |
0c326387 DD |
669 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, |
670 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
5b3b1688 DD |
671 | #endif |
672 | }; | |
673 | ||
0c326387 DD |
674 | static struct irq_chip octeon_irq_chip_ciu = { |
675 | .name = "CIU", | |
676 | .irq_enable = octeon_irq_ciu_enable, | |
677 | .irq_disable = octeon_irq_ciu_disable_all, | |
678 | .irq_mask = octeon_irq_dummy_mask, | |
679 | #ifdef CONFIG_SMP | |
680 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
681 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
682 | #endif | |
86568dc4 DD |
683 | }; |
684 | ||
0c326387 DD |
685 | static struct irq_chip octeon_irq_chip_ciu_edge = { |
686 | .name = "CIU-E", | |
687 | .irq_enable = octeon_irq_ciu_enable, | |
688 | .irq_disable = octeon_irq_ciu_disable_all, | |
689 | .irq_mask = octeon_irq_dummy_mask, | |
690 | .irq_ack = octeon_irq_ciu_ack, | |
691 | #ifdef CONFIG_SMP | |
692 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
693 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
694 | #endif | |
86568dc4 DD |
695 | }; |
696 | ||
0c326387 DD |
697 | /* The mbox versions don't do any affinity or round-robin. */ |
698 | static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = { | |
699 | .name = "CIU-M", | |
700 | .irq_enable = octeon_irq_ciu_enable_all_v2, | |
701 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
702 | .irq_ack = octeon_irq_ciu_disable_local_v2, | |
703 | .irq_eoi = octeon_irq_ciu_enable_local_v2, | |
704 | ||
5b7cd6fd TG |
705 | .irq_cpu_online = octeon_irq_ciu_enable_local_v2, |
706 | .irq_cpu_offline = octeon_irq_ciu_disable_local_v2, | |
707 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 | 708 | }; |
5b3b1688 | 709 | |
0c326387 DD |
710 | static struct irq_chip octeon_irq_chip_ciu_mbox = { |
711 | .name = "CIU-M", | |
712 | .irq_enable = octeon_irq_ciu_enable_all, | |
713 | .irq_disable = octeon_irq_ciu_disable_all, | |
5b3b1688 | 714 | |
5b7cd6fd TG |
715 | .irq_cpu_online = octeon_irq_ciu_enable_local, |
716 | .irq_cpu_offline = octeon_irq_ciu_disable_local, | |
717 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
718 | }; |
719 | ||
720 | /* | |
721 | * Watchdog interrupts are special. They are associated with a single | |
722 | * core, so we hardwire the affinity to that core. | |
723 | */ | |
724 | static void octeon_irq_ciu_wd_enable(struct irq_data *data) | |
5b3b1688 | 725 | { |
5b3b1688 | 726 | unsigned long flags; |
0c326387 DD |
727 | unsigned long *pen; |
728 | int coreid = data->irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */ | |
729 | int cpu = octeon_cpu_for_coreid(coreid); | |
5b3b1688 | 730 | |
39961422 | 731 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); |
0c326387 DD |
732 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
733 | set_bit(coreid, pen); | |
734 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
39961422 | 735 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); |
5b3b1688 DD |
736 | } |
737 | ||
5aae1fd4 DD |
738 | /* |
739 | * Watchdog interrupts are special. They are associated with a single | |
740 | * core, so we hardwire the affinity to that core. | |
741 | */ | |
0c326387 | 742 | static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data) |
5aae1fd4 | 743 | { |
0c326387 DD |
744 | int coreid = data->irq - OCTEON_IRQ_WDOG0; |
745 | int cpu = octeon_cpu_for_coreid(coreid); | |
5aae1fd4 | 746 | |
0c326387 DD |
747 | set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); |
748 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid); | |
5aae1fd4 DD |
749 | } |
750 | ||
0c326387 DD |
751 | |
752 | static struct irq_chip octeon_irq_chip_ciu_wd_v2 = { | |
753 | .name = "CIU-W", | |
754 | .irq_enable = octeon_irq_ciu1_wd_enable_v2, | |
755 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
756 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
757 | .irq_unmask = octeon_irq_ciu_enable_local_v2, | |
758 | }; | |
759 | ||
760 | static struct irq_chip octeon_irq_chip_ciu_wd = { | |
761 | .name = "CIU-W", | |
762 | .irq_enable = octeon_irq_ciu_wd_enable, | |
763 | .irq_disable = octeon_irq_ciu_disable_all, | |
764 | .irq_mask = octeon_irq_dummy_mask, | |
765 | }; | |
766 | ||
767 | static void octeon_irq_ip2_v1(void) | |
5b3b1688 | 768 | { |
0c326387 DD |
769 | const unsigned long core_id = cvmx_get_core_num(); |
770 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); | |
771 | ||
772 | ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); | |
773 | clear_c0_status(STATUSF_IP2); | |
774 | if (likely(ciu_sum)) { | |
775 | int bit = fls64(ciu_sum) - 1; | |
776 | int irq = octeon_irq_ciu_to_irq[0][bit]; | |
777 | if (likely(irq)) | |
778 | do_IRQ(irq); | |
779 | else | |
780 | spurious_interrupt(); | |
781 | } else { | |
782 | spurious_interrupt(); | |
5b3b1688 | 783 | } |
0c326387 | 784 | set_c0_status(STATUSF_IP2); |
cd847b78 DD |
785 | } |
786 | ||
0c326387 | 787 | static void octeon_irq_ip2_v2(void) |
cd847b78 | 788 | { |
0c326387 DD |
789 | const unsigned long core_id = cvmx_get_core_num(); |
790 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); | |
791 | ||
792 | ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); | |
793 | if (likely(ciu_sum)) { | |
794 | int bit = fls64(ciu_sum) - 1; | |
795 | int irq = octeon_irq_ciu_to_irq[0][bit]; | |
796 | if (likely(irq)) | |
797 | do_IRQ(irq); | |
798 | else | |
799 | spurious_interrupt(); | |
800 | } else { | |
801 | spurious_interrupt(); | |
5aae1fd4 | 802 | } |
cd847b78 | 803 | } |
0c326387 | 804 | static void octeon_irq_ip3_v1(void) |
cd847b78 | 805 | { |
0c326387 DD |
806 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); |
807 | ||
808 | ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); | |
809 | clear_c0_status(STATUSF_IP3); | |
810 | if (likely(ciu_sum)) { | |
811 | int bit = fls64(ciu_sum) - 1; | |
812 | int irq = octeon_irq_ciu_to_irq[1][bit]; | |
813 | if (likely(irq)) | |
814 | do_IRQ(irq); | |
815 | else | |
816 | spurious_interrupt(); | |
817 | } else { | |
818 | spurious_interrupt(); | |
5aae1fd4 | 819 | } |
0c326387 | 820 | set_c0_status(STATUSF_IP3); |
cd847b78 DD |
821 | } |
822 | ||
0c326387 | 823 | static void octeon_irq_ip3_v2(void) |
dbb103b2 | 824 | { |
0c326387 DD |
825 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); |
826 | ||
827 | ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); | |
828 | if (likely(ciu_sum)) { | |
829 | int bit = fls64(ciu_sum) - 1; | |
830 | int irq = octeon_irq_ciu_to_irq[1][bit]; | |
831 | if (likely(irq)) | |
832 | do_IRQ(irq); | |
833 | else | |
834 | spurious_interrupt(); | |
835 | } else { | |
836 | spurious_interrupt(); | |
837 | } | |
dbb103b2 DD |
838 | } |
839 | ||
0c326387 | 840 | static void octeon_irq_ip4_mask(void) |
cd847b78 | 841 | { |
0c326387 DD |
842 | clear_c0_status(STATUSF_IP4); |
843 | spurious_interrupt(); | |
5b3b1688 DD |
844 | } |
845 | ||
0c326387 DD |
846 | static void (*octeon_irq_ip2)(void); |
847 | static void (*octeon_irq_ip3)(void); | |
848 | static void (*octeon_irq_ip4)(void); | |
5b3b1688 | 849 | |
0c326387 | 850 | void __cpuinitdata (*octeon_irq_setup_secondary)(void); |
5aae1fd4 | 851 | |
0c326387 DD |
852 | static void __cpuinit octeon_irq_percpu_enable(void) |
853 | { | |
854 | irq_cpu_online(); | |
855 | } | |
856 | ||
857 | static void __cpuinit octeon_irq_init_ciu_percpu(void) | |
858 | { | |
859 | int coreid = cvmx_get_core_num(); | |
5b3b1688 | 860 | /* |
0c326387 DD |
861 | * Disable All CIU Interrupts. The ones we need will be |
862 | * enabled later. Read the SUM register so we know the write | |
863 | * completed. | |
5b3b1688 | 864 | */ |
0c326387 DD |
865 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0); |
866 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0); | |
867 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0); | |
868 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0); | |
869 | cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); | |
5b3b1688 | 870 | } |
cd847b78 | 871 | |
0c326387 | 872 | static void __cpuinit octeon_irq_setup_secondary_ciu(void) |
cd847b78 | 873 | { |
5b3b1688 | 874 | |
0c326387 DD |
875 | __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0; |
876 | __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0; | |
cd847b78 | 877 | |
0c326387 DD |
878 | octeon_irq_init_ciu_percpu(); |
879 | octeon_irq_percpu_enable(); | |
5b3b1688 | 880 | |
0c326387 DD |
881 | /* Enable the CIU lines */ |
882 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
883 | clear_c0_status(STATUSF_IP4); | |
884 | } | |
5aae1fd4 | 885 | |
0c326387 DD |
886 | static void __init octeon_irq_init_ciu(void) |
887 | { | |
888 | unsigned int i; | |
889 | struct irq_chip *chip; | |
890 | struct irq_chip *chip_edge; | |
891 | struct irq_chip *chip_mbox; | |
892 | struct irq_chip *chip_wd; | |
893 | ||
894 | octeon_irq_init_ciu_percpu(); | |
895 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu; | |
5aae1fd4 | 896 | |
0c326387 DD |
897 | if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) || |
898 | OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || | |
899 | OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) || | |
900 | OCTEON_IS_MODEL(OCTEON_CN6XXX)) { | |
901 | octeon_irq_ip2 = octeon_irq_ip2_v2; | |
902 | octeon_irq_ip3 = octeon_irq_ip3_v2; | |
903 | chip = &octeon_irq_chip_ciu_v2; | |
904 | chip_edge = &octeon_irq_chip_ciu_edge_v2; | |
905 | chip_mbox = &octeon_irq_chip_ciu_mbox_v2; | |
906 | chip_wd = &octeon_irq_chip_ciu_wd_v2; | |
907 | } else { | |
908 | octeon_irq_ip2 = octeon_irq_ip2_v1; | |
909 | octeon_irq_ip3 = octeon_irq_ip3_v1; | |
910 | chip = &octeon_irq_chip_ciu; | |
911 | chip_edge = &octeon_irq_chip_ciu_edge; | |
912 | chip_mbox = &octeon_irq_chip_ciu_mbox; | |
913 | chip_wd = &octeon_irq_chip_ciu_wd; | |
914 | } | |
915 | octeon_irq_ip4 = octeon_irq_ip4_mask; | |
916 | ||
917 | /* Mips internal */ | |
918 | octeon_irq_init_core(); | |
919 | ||
920 | /* CIU_0 */ | |
921 | for (i = 0; i < 16; i++) | |
922 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq); | |
923 | for (i = 0; i < 16; i++) | |
924 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GPIO0, 0, i + 16, chip, handle_level_irq); | |
925 | ||
926 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); | |
927 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); | |
928 | ||
929 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART0, 0, 34, chip, handle_level_irq); | |
930 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART1, 0, 35, chip, handle_level_irq); | |
931 | ||
932 | for (i = 0; i < 4; i++) | |
933 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq); | |
934 | for (i = 0; i < 4; i++) | |
935 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq); | |
936 | ||
937 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI, 0, 45, chip, handle_level_irq); | |
938 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq); | |
939 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_TRACE0, 0, 47, chip, handle_level_irq); | |
940 | ||
941 | for (i = 0; i < 2; i++) | |
942 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_GMX_DRP0, 0, i + 48, chip_edge, handle_edge_irq); | |
943 | ||
944 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD_DRP, 0, 50, chip_edge, handle_edge_irq); | |
945 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY_ZERO, 0, 51, chip_edge, handle_edge_irq); | |
946 | ||
947 | for (i = 0; i < 4; i++) | |
948 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip_edge, handle_edge_irq); | |
949 | ||
950 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq); | |
951 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PCM, 0, 57, chip, handle_level_irq); | |
952 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MPI, 0, 58, chip, handle_level_irq); | |
953 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI2, 0, 59, chip, handle_level_irq); | |
954 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_POWIQ, 0, 60, chip, handle_level_irq); | |
955 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPDPPTHR, 0, 61, chip, handle_level_irq); | |
956 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII0, 0, 62, chip, handle_level_irq); | |
957 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq); | |
958 | ||
959 | /* CIU_1 */ | |
960 | for (i = 0; i < 16; i++) | |
961 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq); | |
962 | ||
963 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART2, 1, 16, chip, handle_level_irq); | |
964 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq); | |
965 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq); | |
966 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_NAND, 1, 19, chip, handle_level_irq); | |
967 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MIO, 1, 20, chip, handle_level_irq); | |
968 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_IOB, 1, 21, chip, handle_level_irq); | |
969 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_FPA, 1, 22, chip, handle_level_irq); | |
970 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_POW, 1, 23, chip, handle_level_irq); | |
971 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_L2C, 1, 24, chip, handle_level_irq); | |
972 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_IPD, 1, 25, chip, handle_level_irq); | |
973 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PIP, 1, 26, chip, handle_level_irq); | |
974 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PKO, 1, 27, chip, handle_level_irq); | |
975 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_ZIP, 1, 28, chip, handle_level_irq); | |
976 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_TIM, 1, 29, chip, handle_level_irq); | |
977 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_RAD, 1, 30, chip, handle_level_irq); | |
978 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_KEY, 1, 31, chip, handle_level_irq); | |
979 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFA, 1, 32, chip, handle_level_irq); | |
980 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USBCTL, 1, 33, chip, handle_level_irq); | |
981 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_SLI, 1, 34, chip, handle_level_irq); | |
982 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_DPI, 1, 35, chip, handle_level_irq); | |
983 | ||
984 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGX0, 1, 36, chip, handle_level_irq); | |
985 | ||
986 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_AGL, 1, 46, chip, handle_level_irq); | |
987 | ||
988 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PTP, 1, 47, chip_edge, handle_edge_irq); | |
989 | ||
990 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM0, 1, 48, chip, handle_level_irq); | |
991 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_PEM1, 1, 49, chip, handle_level_irq); | |
992 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO0, 1, 50, chip, handle_level_irq); | |
993 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_SRIO1, 1, 51, chip, handle_level_irq); | |
994 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_LMC0, 1, 52, chip, handle_level_irq); | |
995 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_DFM, 1, 56, chip, handle_level_irq); | |
996 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_RST, 1, 63, chip, handle_level_irq); | |
997 | ||
998 | /* Enable the CIU lines */ | |
999 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1000 | clear_c0_status(STATUSF_IP4); | |
1001 | } | |
5aae1fd4 | 1002 | |
5b3b1688 DD |
1003 | void __init arch_init_irq(void) |
1004 | { | |
5b3b1688 DD |
1005 | #ifdef CONFIG_SMP |
1006 | /* Set the default affinity to the boot cpu. */ | |
1007 | cpumask_clear(irq_default_affinity); | |
1008 | cpumask_set_cpu(smp_processor_id(), irq_default_affinity); | |
1009 | #endif | |
0c326387 | 1010 | octeon_irq_init_ciu(); |
5b3b1688 DD |
1011 | } |
1012 | ||
1013 | asmlinkage void plat_irq_dispatch(void) | |
1014 | { | |
5b3b1688 DD |
1015 | unsigned long cop0_cause; |
1016 | unsigned long cop0_status; | |
5b3b1688 DD |
1017 | |
1018 | while (1) { | |
1019 | cop0_cause = read_c0_cause(); | |
1020 | cop0_status = read_c0_status(); | |
1021 | cop0_cause &= cop0_status; | |
1022 | cop0_cause &= ST0_IM; | |
1023 | ||
0c326387 DD |
1024 | if (unlikely(cop0_cause & STATUSF_IP2)) |
1025 | octeon_irq_ip2(); | |
1026 | else if (unlikely(cop0_cause & STATUSF_IP3)) | |
1027 | octeon_irq_ip3(); | |
1028 | else if (unlikely(cop0_cause & STATUSF_IP4)) | |
1029 | octeon_irq_ip4(); | |
1030 | else if (likely(cop0_cause)) | |
5b3b1688 | 1031 | do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE); |
0c326387 | 1032 | else |
5b3b1688 | 1033 | break; |
5b3b1688 DD |
1034 | } |
1035 | } | |
773cb77d RB |
1036 | |
1037 | #ifdef CONFIG_HOTPLUG_CPU | |
773cb77d RB |
1038 | |
1039 | void fixup_irqs(void) | |
1040 | { | |
0c326387 | 1041 | irq_cpu_offline(); |
773cb77d RB |
1042 | } |
1043 | ||
1044 | #endif /* CONFIG_HOTPLUG_CPU */ |