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 | * | |
a0c16582 | 6 | * Copyright (C) 2004-2012 Cavium, Inc. |
5b3b1688 | 7 | */ |
0c326387 | 8 | |
5b3b1688 | 9 | #include <linux/interrupt.h> |
a0c16582 | 10 | #include <linux/irqdomain.h> |
0c326387 DD |
11 | #include <linux/bitops.h> |
12 | #include <linux/percpu.h> | |
a0c16582 | 13 | #include <linux/slab.h> |
0c326387 | 14 | #include <linux/irq.h> |
631330f5 | 15 | #include <linux/smp.h> |
a0c16582 | 16 | #include <linux/of.h> |
5b3b1688 DD |
17 | |
18 | #include <asm/octeon/octeon.h> | |
19 | ||
39961422 DD |
20 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock); |
21 | static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock); | |
5b3b1688 | 22 | |
0c326387 DD |
23 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror); |
24 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror); | |
25 | ||
26 | static __read_mostly u8 octeon_irq_ciu_to_irq[8][64]; | |
27 | ||
28 | union octeon_ciu_chip_data { | |
29 | void *p; | |
30 | unsigned long l; | |
31 | struct { | |
32 | unsigned int line:6; | |
33 | unsigned int bit:6; | |
34 | } s; | |
35 | }; | |
36 | ||
37 | struct octeon_core_chip_data { | |
38 | struct mutex core_irq_mutex; | |
39 | bool current_en; | |
40 | bool desired_en; | |
41 | u8 bit; | |
42 | }; | |
43 | ||
44 | #define MIPS_CORE_IRQ_LINES 8 | |
45 | ||
46 | static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES]; | |
47 | ||
a0c16582 DD |
48 | static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, |
49 | struct irq_chip *chip, | |
50 | irq_flow_handler_t handler) | |
0c326387 DD |
51 | { |
52 | union octeon_ciu_chip_data cd; | |
53 | ||
54 | irq_set_chip_and_handler(irq, chip, handler); | |
55 | ||
56 | cd.l = 0; | |
57 | cd.s.line = line; | |
58 | cd.s.bit = bit; | |
59 | ||
60 | irq_set_chip_data(irq, cd.p); | |
61 | octeon_irq_ciu_to_irq[line][bit] = irq; | |
62 | } | |
63 | ||
cd847b78 DD |
64 | static int octeon_coreid_for_cpu(int cpu) |
65 | { | |
66 | #ifdef CONFIG_SMP | |
67 | return cpu_logical_map(cpu); | |
68 | #else | |
69 | return cvmx_get_core_num(); | |
70 | #endif | |
71 | } | |
72 | ||
0c326387 DD |
73 | static int octeon_cpu_for_coreid(int coreid) |
74 | { | |
75 | #ifdef CONFIG_SMP | |
76 | return cpu_number_map(coreid); | |
77 | #else | |
78 | return smp_processor_id(); | |
79 | #endif | |
80 | } | |
81 | ||
82 | static void octeon_irq_core_ack(struct irq_data *data) | |
5b3b1688 | 83 | { |
0c326387 DD |
84 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
85 | unsigned int bit = cd->bit; | |
86 | ||
5b3b1688 DD |
87 | /* |
88 | * We don't need to disable IRQs to make these atomic since | |
89 | * they are already disabled earlier in the low level | |
90 | * interrupt code. | |
91 | */ | |
92 | clear_c0_status(0x100 << bit); | |
93 | /* The two user interrupts must be cleared manually. */ | |
94 | if (bit < 2) | |
95 | clear_c0_cause(0x100 << bit); | |
96 | } | |
97 | ||
0c326387 | 98 | static void octeon_irq_core_eoi(struct irq_data *data) |
5b3b1688 | 99 | { |
0c326387 DD |
100 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
101 | ||
5b3b1688 DD |
102 | /* |
103 | * We don't need to disable IRQs to make these atomic since | |
104 | * they are already disabled earlier in the low level | |
105 | * interrupt code. | |
106 | */ | |
0c326387 | 107 | set_c0_status(0x100 << cd->bit); |
5b3b1688 DD |
108 | } |
109 | ||
0c326387 | 110 | static void octeon_irq_core_set_enable_local(void *arg) |
5b3b1688 | 111 | { |
0c326387 DD |
112 | struct irq_data *data = arg; |
113 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
114 | unsigned int mask = 0x100 << cd->bit; | |
5b3b1688 DD |
115 | |
116 | /* | |
0c326387 | 117 | * Interrupts are already disabled, so these are atomic. |
5b3b1688 | 118 | */ |
0c326387 DD |
119 | if (cd->desired_en) |
120 | set_c0_status(mask); | |
121 | else | |
122 | clear_c0_status(mask); | |
123 | ||
5b3b1688 DD |
124 | } |
125 | ||
0c326387 | 126 | static void octeon_irq_core_disable(struct irq_data *data) |
5b3b1688 | 127 | { |
0c326387 DD |
128 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
129 | cd->desired_en = false; | |
5b3b1688 DD |
130 | } |
131 | ||
0c326387 | 132 | static void octeon_irq_core_enable(struct irq_data *data) |
5b3b1688 | 133 | { |
0c326387 DD |
134 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
135 | cd->desired_en = true; | |
5b3b1688 DD |
136 | } |
137 | ||
0c326387 DD |
138 | static void octeon_irq_core_bus_lock(struct irq_data *data) |
139 | { | |
140 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
5b3b1688 | 141 | |
0c326387 DD |
142 | mutex_lock(&cd->core_irq_mutex); |
143 | } | |
5b3b1688 | 144 | |
0c326387 | 145 | static void octeon_irq_core_bus_sync_unlock(struct irq_data *data) |
5b3b1688 | 146 | { |
0c326387 DD |
147 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
148 | ||
149 | if (cd->desired_en != cd->current_en) { | |
150 | on_each_cpu(octeon_irq_core_set_enable_local, data, 1); | |
151 | ||
152 | cd->current_en = cd->desired_en; | |
5aae1fd4 DD |
153 | } |
154 | ||
0c326387 | 155 | mutex_unlock(&cd->core_irq_mutex); |
5b3b1688 DD |
156 | } |
157 | ||
0c326387 DD |
158 | static struct irq_chip octeon_irq_chip_core = { |
159 | .name = "Core", | |
160 | .irq_enable = octeon_irq_core_enable, | |
161 | .irq_disable = octeon_irq_core_disable, | |
162 | .irq_ack = octeon_irq_core_ack, | |
163 | .irq_eoi = octeon_irq_core_eoi, | |
164 | .irq_bus_lock = octeon_irq_core_bus_lock, | |
165 | .irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock, | |
166 | ||
5b7cd6fd TG |
167 | .irq_cpu_online = octeon_irq_core_eoi, |
168 | .irq_cpu_offline = octeon_irq_core_ack, | |
169 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
170 | }; |
171 | ||
172 | static void __init octeon_irq_init_core(void) | |
173 | { | |
174 | int i; | |
175 | int irq; | |
176 | struct octeon_core_chip_data *cd; | |
177 | ||
178 | for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) { | |
179 | cd = &octeon_irq_core_chip_data[i]; | |
180 | cd->current_en = false; | |
181 | cd->desired_en = false; | |
182 | cd->bit = i; | |
183 | mutex_init(&cd->core_irq_mutex); | |
184 | ||
185 | irq = OCTEON_IRQ_SW0 + i; | |
186 | switch (irq) { | |
187 | case OCTEON_IRQ_TIMER: | |
188 | case OCTEON_IRQ_SW0: | |
189 | case OCTEON_IRQ_SW1: | |
190 | case OCTEON_IRQ_5: | |
191 | case OCTEON_IRQ_PERF: | |
192 | irq_set_chip_data(irq, cd); | |
193 | irq_set_chip_and_handler(irq, &octeon_irq_chip_core, | |
194 | handle_percpu_irq); | |
195 | break; | |
196 | default: | |
197 | break; | |
198 | } | |
199 | } | |
200 | } | |
201 | ||
202 | static int next_cpu_for_irq(struct irq_data *data) | |
5aae1fd4 DD |
203 | { |
204 | ||
205 | #ifdef CONFIG_SMP | |
0c326387 DD |
206 | int cpu; |
207 | int weight = cpumask_weight(data->affinity); | |
5aae1fd4 DD |
208 | |
209 | if (weight > 1) { | |
0c326387 | 210 | cpu = smp_processor_id(); |
5aae1fd4 | 211 | for (;;) { |
0c326387 | 212 | cpu = cpumask_next(cpu, data->affinity); |
5aae1fd4 DD |
213 | if (cpu >= nr_cpu_ids) { |
214 | cpu = -1; | |
215 | continue; | |
216 | } else if (cpumask_test_cpu(cpu, cpu_online_mask)) { | |
217 | break; | |
218 | } | |
219 | } | |
5aae1fd4 | 220 | } else if (weight == 1) { |
0c326387 | 221 | cpu = cpumask_first(data->affinity); |
5aae1fd4 | 222 | } else { |
0c326387 | 223 | cpu = smp_processor_id(); |
5aae1fd4 | 224 | } |
0c326387 | 225 | return cpu; |
5aae1fd4 | 226 | #else |
0c326387 | 227 | return smp_processor_id(); |
5aae1fd4 DD |
228 | #endif |
229 | } | |
230 | ||
0c326387 | 231 | static void octeon_irq_ciu_enable(struct irq_data *data) |
5aae1fd4 | 232 | { |
0c326387 DD |
233 | int cpu = next_cpu_for_irq(data); |
234 | int coreid = octeon_coreid_for_cpu(cpu); | |
235 | unsigned long *pen; | |
5aae1fd4 | 236 | unsigned long flags; |
0c326387 DD |
237 | union octeon_ciu_chip_data cd; |
238 | ||
239 | cd.p = irq_data_get_irq_chip_data(data); | |
5aae1fd4 | 240 | |
0c326387 DD |
241 | if (cd.s.line == 0) { |
242 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
243 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
244 | set_bit(cd.s.bit, pen); | |
245 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
246 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
247 | } else { | |
248 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
249 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
250 | set_bit(cd.s.bit, pen); | |
251 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
252 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
253 | } | |
5aae1fd4 DD |
254 | } |
255 | ||
0c326387 DD |
256 | static void octeon_irq_ciu_enable_local(struct irq_data *data) |
257 | { | |
258 | unsigned long *pen; | |
259 | unsigned long flags; | |
260 | union octeon_ciu_chip_data cd; | |
261 | ||
262 | cd.p = irq_data_get_irq_chip_data(data); | |
263 | ||
264 | if (cd.s.line == 0) { | |
265 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
266 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); | |
267 | set_bit(cd.s.bit, pen); | |
268 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); | |
269 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
270 | } else { | |
271 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
272 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); | |
273 | set_bit(cd.s.bit, pen); | |
274 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); | |
275 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
276 | } | |
277 | } | |
278 | ||
279 | static void octeon_irq_ciu_disable_local(struct irq_data *data) | |
280 | { | |
281 | unsigned long *pen; | |
282 | unsigned long flags; | |
283 | union octeon_ciu_chip_data cd; | |
284 | ||
285 | cd.p = irq_data_get_irq_chip_data(data); | |
286 | ||
287 | if (cd.s.line == 0) { | |
288 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
289 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); | |
290 | clear_bit(cd.s.bit, pen); | |
291 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); | |
292 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
293 | } else { | |
294 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
295 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); | |
296 | clear_bit(cd.s.bit, pen); | |
297 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); | |
298 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
299 | } | |
300 | } | |
301 | ||
302 | static void octeon_irq_ciu_disable_all(struct irq_data *data) | |
5b3b1688 | 303 | { |
5b3b1688 | 304 | unsigned long flags; |
0c326387 DD |
305 | unsigned long *pen; |
306 | int cpu; | |
307 | union octeon_ciu_chip_data cd; | |
308 | ||
309 | wmb(); /* Make sure flag changes arrive before register updates. */ | |
5b3b1688 | 310 | |
0c326387 DD |
311 | cd.p = irq_data_get_irq_chip_data(data); |
312 | ||
313 | if (cd.s.line == 0) { | |
314 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
315 | for_each_online_cpu(cpu) { | |
316 | int coreid = octeon_coreid_for_cpu(cpu); | |
317 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
318 | clear_bit(cd.s.bit, pen); | |
319 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
320 | } | |
321 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
322 | } else { | |
323 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
324 | for_each_online_cpu(cpu) { | |
325 | int coreid = octeon_coreid_for_cpu(cpu); | |
326 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
327 | clear_bit(cd.s.bit, pen); | |
328 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
329 | } | |
330 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
331 | } | |
5b3b1688 DD |
332 | } |
333 | ||
0c326387 | 334 | static void octeon_irq_ciu_enable_all(struct irq_data *data) |
5b3b1688 | 335 | { |
5b3b1688 | 336 | unsigned long flags; |
0c326387 | 337 | unsigned long *pen; |
5b3b1688 | 338 | int cpu; |
0c326387 DD |
339 | union octeon_ciu_chip_data cd; |
340 | ||
341 | cd.p = irq_data_get_irq_chip_data(data); | |
342 | ||
343 | if (cd.s.line == 0) { | |
344 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
345 | for_each_online_cpu(cpu) { | |
346 | int coreid = octeon_coreid_for_cpu(cpu); | |
347 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
348 | set_bit(cd.s.bit, pen); | |
349 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
350 | } | |
351 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
352 | } else { | |
353 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
354 | for_each_online_cpu(cpu) { | |
355 | int coreid = octeon_coreid_for_cpu(cpu); | |
356 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
357 | set_bit(cd.s.bit, pen); | |
358 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
359 | } | |
360 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); | |
5b3b1688 | 361 | } |
cd847b78 DD |
362 | } |
363 | ||
364 | /* | |
5aae1fd4 DD |
365 | * Enable the irq on the next core in the affinity set for chips that |
366 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 367 | */ |
0c326387 | 368 | static void octeon_irq_ciu_enable_v2(struct irq_data *data) |
cd847b78 | 369 | { |
0c326387 DD |
370 | u64 mask; |
371 | int cpu = next_cpu_for_irq(data); | |
372 | union octeon_ciu_chip_data cd; | |
cd847b78 | 373 | |
0c326387 DD |
374 | cd.p = irq_data_get_irq_chip_data(data); |
375 | mask = 1ull << (cd.s.bit); | |
376 | ||
377 | /* | |
378 | * Called under the desc lock, so these should never get out | |
379 | * of sync. | |
380 | */ | |
381 | if (cd.s.line == 0) { | |
382 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
383 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
5aae1fd4 | 384 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); |
0c326387 DD |
385 | } else { |
386 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
387 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
388 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
5aae1fd4 | 389 | } |
cd847b78 DD |
390 | } |
391 | ||
392 | /* | |
5aae1fd4 DD |
393 | * Enable the irq on the current CPU for chips that |
394 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 395 | */ |
0c326387 | 396 | static void octeon_irq_ciu_enable_local_v2(struct irq_data *data) |
cd847b78 | 397 | { |
0c326387 DD |
398 | u64 mask; |
399 | union octeon_ciu_chip_data cd; | |
400 | ||
401 | cd.p = irq_data_get_irq_chip_data(data); | |
402 | mask = 1ull << (cd.s.bit); | |
cd847b78 | 403 | |
0c326387 DD |
404 | if (cd.s.line == 0) { |
405 | int index = cvmx_get_core_num() * 2; | |
406 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
407 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
408 | } else { | |
409 | int index = cvmx_get_core_num() * 2 + 1; | |
410 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
411 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
412 | } | |
413 | } | |
414 | ||
415 | static void octeon_irq_ciu_disable_local_v2(struct irq_data *data) | |
416 | { | |
417 | u64 mask; | |
418 | union octeon_ciu_chip_data cd; | |
419 | ||
420 | cd.p = irq_data_get_irq_chip_data(data); | |
421 | mask = 1ull << (cd.s.bit); | |
422 | ||
423 | if (cd.s.line == 0) { | |
424 | int index = cvmx_get_core_num() * 2; | |
425 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
426 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
427 | } else { | |
428 | int index = cvmx_get_core_num() * 2 + 1; | |
429 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
430 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
431 | } | |
cd847b78 DD |
432 | } |
433 | ||
86568dc4 | 434 | /* |
0c326387 | 435 | * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq. |
86568dc4 | 436 | */ |
0c326387 DD |
437 | static void octeon_irq_ciu_ack(struct irq_data *data) |
438 | { | |
439 | u64 mask; | |
440 | union octeon_ciu_chip_data cd; | |
441 | ||
442 | cd.p = data->chip_data; | |
443 | mask = 1ull << (cd.s.bit); | |
444 | ||
445 | if (cd.s.line == 0) { | |
446 | int index = cvmx_get_core_num() * 2; | |
5aae1fd4 | 447 | cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask); |
0c326387 DD |
448 | } else { |
449 | cvmx_write_csr(CVMX_CIU_INT_SUM1, mask); | |
5aae1fd4 | 450 | } |
86568dc4 DD |
451 | } |
452 | ||
dbb103b2 | 453 | /* |
0c326387 | 454 | * Disable the irq on the all cores for chips that have the EN*_W1{S,C} |
dbb103b2 DD |
455 | * registers. |
456 | */ | |
0c326387 | 457 | static void octeon_irq_ciu_disable_all_v2(struct irq_data *data) |
dbb103b2 | 458 | { |
0c326387 DD |
459 | int cpu; |
460 | u64 mask; | |
461 | union octeon_ciu_chip_data cd; | |
dbb103b2 | 462 | |
0c326387 DD |
463 | wmb(); /* Make sure flag changes arrive before register updates. */ |
464 | ||
465 | cd.p = data->chip_data; | |
466 | mask = 1ull << (cd.s.bit); | |
467 | ||
468 | if (cd.s.line == 0) { | |
469 | for_each_online_cpu(cpu) { | |
470 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
471 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
472 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
473 | } | |
474 | } else { | |
475 | for_each_online_cpu(cpu) { | |
476 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
477 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
478 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
479 | } | |
480 | } | |
dbb103b2 DD |
481 | } |
482 | ||
cd847b78 | 483 | /* |
0c326387 | 484 | * Enable the irq on the all cores for chips that have the EN*_W1{S,C} |
cd847b78 DD |
485 | * registers. |
486 | */ | |
0c326387 | 487 | static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) |
cd847b78 | 488 | { |
cd847b78 | 489 | int cpu; |
0c326387 DD |
490 | u64 mask; |
491 | union octeon_ciu_chip_data cd; | |
492 | ||
493 | cd.p = data->chip_data; | |
494 | mask = 1ull << (cd.s.bit); | |
495 | ||
496 | if (cd.s.line == 0) { | |
497 | for_each_online_cpu(cpu) { | |
498 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
499 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
500 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
501 | } | |
502 | } else { | |
503 | for_each_online_cpu(cpu) { | |
504 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
505 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
506 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
507 | } | |
cd847b78 | 508 | } |
5b3b1688 DD |
509 | } |
510 | ||
6d1ab4c2 DD |
511 | static void octeon_irq_gpio_setup(struct irq_data *data) |
512 | { | |
513 | union cvmx_gpio_bit_cfgx cfg; | |
514 | union octeon_ciu_chip_data cd; | |
515 | u32 t = irqd_get_trigger_type(data); | |
516 | ||
517 | cd.p = irq_data_get_irq_chip_data(data); | |
518 | ||
519 | cfg.u64 = 0; | |
520 | cfg.s.int_en = 1; | |
521 | cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0; | |
522 | cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0; | |
523 | ||
524 | /* 140 nS glitch filter*/ | |
525 | cfg.s.fil_cnt = 7; | |
526 | cfg.s.fil_sel = 3; | |
527 | ||
528 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), cfg.u64); | |
529 | } | |
530 | ||
531 | static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data) | |
532 | { | |
533 | octeon_irq_gpio_setup(data); | |
534 | octeon_irq_ciu_enable_v2(data); | |
535 | } | |
536 | ||
537 | static void octeon_irq_ciu_enable_gpio(struct irq_data *data) | |
538 | { | |
539 | octeon_irq_gpio_setup(data); | |
540 | octeon_irq_ciu_enable(data); | |
541 | } | |
542 | ||
543 | static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t) | |
544 | { | |
545 | irqd_set_trigger_type(data, t); | |
546 | octeon_irq_gpio_setup(data); | |
547 | ||
548 | return IRQ_SET_MASK_OK; | |
549 | } | |
550 | ||
551 | static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data) | |
552 | { | |
553 | union octeon_ciu_chip_data cd; | |
554 | ||
555 | cd.p = irq_data_get_irq_chip_data(data); | |
556 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0); | |
557 | ||
558 | octeon_irq_ciu_disable_all_v2(data); | |
559 | } | |
560 | ||
561 | static void octeon_irq_ciu_disable_gpio(struct irq_data *data) | |
562 | { | |
563 | union octeon_ciu_chip_data cd; | |
564 | ||
565 | cd.p = irq_data_get_irq_chip_data(data); | |
566 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.bit - 16), 0); | |
567 | ||
568 | octeon_irq_ciu_disable_all(data); | |
569 | } | |
570 | ||
571 | static void octeon_irq_ciu_gpio_ack(struct irq_data *data) | |
572 | { | |
573 | union octeon_ciu_chip_data cd; | |
574 | u64 mask; | |
575 | ||
576 | cd.p = irq_data_get_irq_chip_data(data); | |
577 | mask = 1ull << (cd.s.bit - 16); | |
578 | ||
579 | cvmx_write_csr(CVMX_GPIO_INT_CLR, mask); | |
580 | } | |
581 | ||
582 | static void octeon_irq_handle_gpio(unsigned int irq, struct irq_desc *desc) | |
583 | { | |
584 | if (irqd_get_trigger_type(irq_desc_get_irq_data(desc)) & IRQ_TYPE_EDGE_BOTH) | |
585 | handle_edge_irq(irq, desc); | |
586 | else | |
587 | handle_level_irq(irq, desc); | |
588 | } | |
589 | ||
5b3b1688 | 590 | #ifdef CONFIG_SMP |
0c326387 DD |
591 | |
592 | static void octeon_irq_cpu_offline_ciu(struct irq_data *data) | |
593 | { | |
594 | int cpu = smp_processor_id(); | |
595 | cpumask_t new_affinity; | |
596 | ||
597 | if (!cpumask_test_cpu(cpu, data->affinity)) | |
598 | return; | |
599 | ||
600 | if (cpumask_weight(data->affinity) > 1) { | |
601 | /* | |
602 | * It has multi CPU affinity, just remove this CPU | |
603 | * from the affinity set. | |
604 | */ | |
605 | cpumask_copy(&new_affinity, data->affinity); | |
606 | cpumask_clear_cpu(cpu, &new_affinity); | |
607 | } else { | |
608 | /* Otherwise, put it on lowest numbered online CPU. */ | |
609 | cpumask_clear(&new_affinity); | |
610 | cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); | |
611 | } | |
612 | __irq_set_affinity_locked(data, &new_affinity); | |
613 | } | |
614 | ||
615 | static int octeon_irq_ciu_set_affinity(struct irq_data *data, | |
616 | const struct cpumask *dest, bool force) | |
5b3b1688 DD |
617 | { |
618 | int cpu; | |
5b7cd6fd | 619 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
b6b74d54 | 620 | unsigned long flags; |
0c326387 DD |
621 | union octeon_ciu_chip_data cd; |
622 | ||
623 | cd.p = data->chip_data; | |
5b3b1688 | 624 | |
5aae1fd4 DD |
625 | /* |
626 | * For non-v2 CIU, we will allow only single CPU affinity. | |
627 | * This removes the need to do locking in the .ack/.eoi | |
628 | * functions. | |
629 | */ | |
630 | if (cpumask_weight(dest) != 1) | |
631 | return -EINVAL; | |
632 | ||
5b7cd6fd | 633 | if (!enable_one) |
0c326387 DD |
634 | return 0; |
635 | ||
636 | if (cd.s.line == 0) { | |
637 | raw_spin_lock_irqsave(&octeon_irq_ciu0_lock, flags); | |
638 | for_each_online_cpu(cpu) { | |
639 | int coreid = octeon_coreid_for_cpu(cpu); | |
640 | unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
641 | ||
642 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 643 | enable_one = false; |
0c326387 DD |
644 | set_bit(cd.s.bit, pen); |
645 | } else { | |
646 | clear_bit(cd.s.bit, pen); | |
647 | } | |
648 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
649 | } | |
650 | raw_spin_unlock_irqrestore(&octeon_irq_ciu0_lock, flags); | |
651 | } else { | |
652 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); | |
653 | for_each_online_cpu(cpu) { | |
654 | int coreid = octeon_coreid_for_cpu(cpu); | |
655 | unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
656 | ||
657 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 658 | enable_one = false; |
0c326387 DD |
659 | set_bit(cd.s.bit, pen); |
660 | } else { | |
661 | clear_bit(cd.s.bit, pen); | |
662 | } | |
663 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
5aae1fd4 | 664 | } |
0c326387 | 665 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); |
5b3b1688 | 666 | } |
d5dedd45 | 667 | return 0; |
5b3b1688 | 668 | } |
cd847b78 DD |
669 | |
670 | /* | |
671 | * Set affinity for the irq for chips that have the EN*_W1{S,C} | |
672 | * registers. | |
673 | */ | |
0c326387 DD |
674 | static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, |
675 | const struct cpumask *dest, | |
676 | bool force) | |
cd847b78 DD |
677 | { |
678 | int cpu; | |
5b7cd6fd | 679 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
0c326387 DD |
680 | u64 mask; |
681 | union octeon_ciu_chip_data cd; | |
682 | ||
5b7cd6fd | 683 | if (!enable_one) |
0c326387 DD |
684 | return 0; |
685 | ||
686 | cd.p = data->chip_data; | |
687 | mask = 1ull << cd.s.bit; | |
688 | ||
689 | if (cd.s.line == 0) { | |
690 | for_each_online_cpu(cpu) { | |
691 | unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
692 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
693 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 694 | enable_one = false; |
0c326387 DD |
695 | set_bit(cd.s.bit, pen); |
696 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
697 | } else { | |
698 | clear_bit(cd.s.bit, pen); | |
699 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
700 | } | |
701 | } | |
702 | } else { | |
703 | for_each_online_cpu(cpu) { | |
704 | unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
705 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
706 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 707 | enable_one = false; |
0c326387 DD |
708 | set_bit(cd.s.bit, pen); |
709 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
710 | } else { | |
711 | clear_bit(cd.s.bit, pen); | |
712 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
713 | } | |
5aae1fd4 | 714 | } |
cd847b78 DD |
715 | } |
716 | return 0; | |
717 | } | |
5b3b1688 DD |
718 | #endif |
719 | ||
0c326387 DD |
720 | /* |
721 | * The v1 CIU code already masks things, so supply a dummy version to | |
722 | * the core chip code. | |
723 | */ | |
724 | static void octeon_irq_dummy_mask(struct irq_data *data) | |
725 | { | |
0c326387 DD |
726 | } |
727 | ||
cd847b78 DD |
728 | /* |
729 | * Newer octeon chips have support for lockless CIU operation. | |
730 | */ | |
0c326387 DD |
731 | static struct irq_chip octeon_irq_chip_ciu_v2 = { |
732 | .name = "CIU", | |
733 | .irq_enable = octeon_irq_ciu_enable_v2, | |
734 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
0c326387 DD |
735 | .irq_ack = octeon_irq_ciu_ack, |
736 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
737 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
5b3b1688 | 738 | #ifdef CONFIG_SMP |
0c326387 DD |
739 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, |
740 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
5b3b1688 DD |
741 | #endif |
742 | }; | |
743 | ||
0c326387 DD |
744 | static struct irq_chip octeon_irq_chip_ciu = { |
745 | .name = "CIU", | |
746 | .irq_enable = octeon_irq_ciu_enable, | |
747 | .irq_disable = octeon_irq_ciu_disable_all, | |
0c326387 | 748 | .irq_ack = octeon_irq_ciu_ack, |
a339aef9 | 749 | .irq_mask = octeon_irq_dummy_mask, |
0c326387 DD |
750 | #ifdef CONFIG_SMP |
751 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
752 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
753 | #endif | |
86568dc4 DD |
754 | }; |
755 | ||
0c326387 DD |
756 | /* The mbox versions don't do any affinity or round-robin. */ |
757 | static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = { | |
758 | .name = "CIU-M", | |
759 | .irq_enable = octeon_irq_ciu_enable_all_v2, | |
760 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
761 | .irq_ack = octeon_irq_ciu_disable_local_v2, | |
762 | .irq_eoi = octeon_irq_ciu_enable_local_v2, | |
763 | ||
5b7cd6fd TG |
764 | .irq_cpu_online = octeon_irq_ciu_enable_local_v2, |
765 | .irq_cpu_offline = octeon_irq_ciu_disable_local_v2, | |
766 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 | 767 | }; |
5b3b1688 | 768 | |
0c326387 DD |
769 | static struct irq_chip octeon_irq_chip_ciu_mbox = { |
770 | .name = "CIU-M", | |
771 | .irq_enable = octeon_irq_ciu_enable_all, | |
772 | .irq_disable = octeon_irq_ciu_disable_all, | |
5b3b1688 | 773 | |
5b7cd6fd TG |
774 | .irq_cpu_online = octeon_irq_ciu_enable_local, |
775 | .irq_cpu_offline = octeon_irq_ciu_disable_local, | |
776 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
777 | }; |
778 | ||
6d1ab4c2 DD |
779 | static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = { |
780 | .name = "CIU-GPIO", | |
781 | .irq_enable = octeon_irq_ciu_enable_gpio_v2, | |
782 | .irq_disable = octeon_irq_ciu_disable_gpio_v2, | |
783 | .irq_ack = octeon_irq_ciu_gpio_ack, | |
784 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
785 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
786 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | |
787 | #ifdef CONFIG_SMP | |
788 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, | |
789 | #endif | |
790 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
791 | }; | |
792 | ||
793 | static struct irq_chip octeon_irq_chip_ciu_gpio = { | |
794 | .name = "CIU-GPIO", | |
795 | .irq_enable = octeon_irq_ciu_enable_gpio, | |
796 | .irq_disable = octeon_irq_ciu_disable_gpio, | |
797 | .irq_mask = octeon_irq_dummy_mask, | |
798 | .irq_ack = octeon_irq_ciu_gpio_ack, | |
799 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | |
800 | #ifdef CONFIG_SMP | |
801 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
802 | #endif | |
803 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
804 | }; | |
805 | ||
0c326387 DD |
806 | /* |
807 | * Watchdog interrupts are special. They are associated with a single | |
808 | * core, so we hardwire the affinity to that core. | |
809 | */ | |
810 | static void octeon_irq_ciu_wd_enable(struct irq_data *data) | |
5b3b1688 | 811 | { |
5b3b1688 | 812 | unsigned long flags; |
0c326387 DD |
813 | unsigned long *pen; |
814 | int coreid = data->irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */ | |
815 | int cpu = octeon_cpu_for_coreid(coreid); | |
5b3b1688 | 816 | |
39961422 | 817 | raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags); |
0c326387 DD |
818 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
819 | set_bit(coreid, pen); | |
820 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); | |
39961422 | 821 | raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags); |
5b3b1688 DD |
822 | } |
823 | ||
5aae1fd4 DD |
824 | /* |
825 | * Watchdog interrupts are special. They are associated with a single | |
826 | * core, so we hardwire the affinity to that core. | |
827 | */ | |
0c326387 | 828 | static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data) |
5aae1fd4 | 829 | { |
0c326387 DD |
830 | int coreid = data->irq - OCTEON_IRQ_WDOG0; |
831 | int cpu = octeon_cpu_for_coreid(coreid); | |
5aae1fd4 | 832 | |
0c326387 DD |
833 | set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); |
834 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid); | |
5aae1fd4 DD |
835 | } |
836 | ||
0c326387 DD |
837 | |
838 | static struct irq_chip octeon_irq_chip_ciu_wd_v2 = { | |
839 | .name = "CIU-W", | |
840 | .irq_enable = octeon_irq_ciu1_wd_enable_v2, | |
841 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
842 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
843 | .irq_unmask = octeon_irq_ciu_enable_local_v2, | |
844 | }; | |
845 | ||
846 | static struct irq_chip octeon_irq_chip_ciu_wd = { | |
847 | .name = "CIU-W", | |
848 | .irq_enable = octeon_irq_ciu_wd_enable, | |
849 | .irq_disable = octeon_irq_ciu_disable_all, | |
850 | .irq_mask = octeon_irq_dummy_mask, | |
851 | }; | |
852 | ||
a0c16582 DD |
853 | static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit) |
854 | { | |
855 | bool edge = false; | |
856 | ||
857 | if (line == 0) | |
858 | switch (bit) { | |
859 | case 48 ... 49: /* GMX DRP */ | |
860 | case 50: /* IPD_DRP */ | |
861 | case 52 ... 55: /* Timers */ | |
862 | case 58: /* MPI */ | |
863 | edge = true; | |
864 | break; | |
865 | default: | |
866 | break; | |
867 | } | |
868 | else /* line == 1 */ | |
869 | switch (bit) { | |
870 | case 47: /* PTP */ | |
871 | edge = true; | |
872 | break; | |
873 | default: | |
874 | break; | |
875 | } | |
876 | return edge; | |
877 | } | |
878 | ||
879 | struct octeon_irq_gpio_domain_data { | |
880 | unsigned int base_hwirq; | |
881 | }; | |
882 | ||
883 | static int octeon_irq_gpio_xlat(struct irq_domain *d, | |
884 | struct device_node *node, | |
885 | const u32 *intspec, | |
886 | unsigned int intsize, | |
887 | unsigned long *out_hwirq, | |
888 | unsigned int *out_type) | |
889 | { | |
890 | unsigned int type; | |
891 | unsigned int pin; | |
892 | unsigned int trigger; | |
893 | struct octeon_irq_gpio_domain_data *gpiod; | |
894 | ||
895 | if (d->of_node != node) | |
896 | return -EINVAL; | |
897 | ||
898 | if (intsize < 2) | |
899 | return -EINVAL; | |
900 | ||
901 | pin = intspec[0]; | |
902 | if (pin >= 16) | |
903 | return -EINVAL; | |
904 | ||
905 | trigger = intspec[1]; | |
906 | ||
907 | switch (trigger) { | |
908 | case 1: | |
909 | type = IRQ_TYPE_EDGE_RISING; | |
910 | break; | |
911 | case 2: | |
912 | type = IRQ_TYPE_EDGE_FALLING; | |
913 | break; | |
914 | case 4: | |
915 | type = IRQ_TYPE_LEVEL_HIGH; | |
916 | break; | |
917 | case 8: | |
918 | type = IRQ_TYPE_LEVEL_LOW; | |
919 | break; | |
920 | default: | |
921 | pr_err("Error: (%s) Invalid irq trigger specification: %x\n", | |
922 | node->name, | |
923 | trigger); | |
924 | type = IRQ_TYPE_LEVEL_LOW; | |
925 | break; | |
926 | } | |
927 | *out_type = type; | |
928 | gpiod = d->host_data; | |
929 | *out_hwirq = gpiod->base_hwirq + pin; | |
930 | ||
931 | return 0; | |
932 | } | |
933 | ||
934 | static int octeon_irq_ciu_xlat(struct irq_domain *d, | |
935 | struct device_node *node, | |
936 | const u32 *intspec, | |
937 | unsigned int intsize, | |
938 | unsigned long *out_hwirq, | |
939 | unsigned int *out_type) | |
940 | { | |
941 | unsigned int ciu, bit; | |
942 | ||
943 | ciu = intspec[0]; | |
944 | bit = intspec[1]; | |
945 | ||
946 | if (ciu > 1 || bit > 63) | |
947 | return -EINVAL; | |
948 | ||
949 | /* These are the GPIO lines */ | |
950 | if (ciu == 0 && bit >= 16 && bit < 32) | |
951 | return -EINVAL; | |
952 | ||
953 | *out_hwirq = (ciu << 6) | bit; | |
954 | *out_type = 0; | |
955 | ||
956 | return 0; | |
957 | } | |
958 | ||
959 | static struct irq_chip *octeon_irq_ciu_chip; | |
960 | static struct irq_chip *octeon_irq_gpio_chip; | |
961 | ||
962 | static bool octeon_irq_virq_in_range(unsigned int virq) | |
963 | { | |
964 | /* We cannot let it overflow the mapping array. */ | |
965 | if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0]))) | |
966 | return true; | |
967 | ||
968 | WARN_ONCE(true, "virq out of range %u.\n", virq); | |
969 | return false; | |
970 | } | |
971 | ||
972 | static int octeon_irq_ciu_map(struct irq_domain *d, | |
973 | unsigned int virq, irq_hw_number_t hw) | |
974 | { | |
975 | unsigned int line = hw >> 6; | |
976 | unsigned int bit = hw & 63; | |
977 | ||
978 | if (!octeon_irq_virq_in_range(virq)) | |
979 | return -EINVAL; | |
980 | ||
981 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | |
982 | return -EINVAL; | |
983 | ||
984 | if (octeon_irq_ciu_is_edge(line, bit)) | |
985 | octeon_irq_set_ciu_mapping(virq, line, bit, | |
986 | octeon_irq_ciu_chip, | |
987 | handle_edge_irq); | |
988 | else | |
989 | octeon_irq_set_ciu_mapping(virq, line, bit, | |
990 | octeon_irq_ciu_chip, | |
991 | handle_level_irq); | |
992 | ||
993 | return 0; | |
994 | } | |
995 | ||
996 | static int octeon_irq_gpio_map(struct irq_domain *d, | |
997 | unsigned int virq, irq_hw_number_t hw) | |
998 | { | |
999 | unsigned int line = hw >> 6; | |
1000 | unsigned int bit = hw & 63; | |
1001 | ||
1002 | if (!octeon_irq_virq_in_range(virq)) | |
1003 | return -EINVAL; | |
1004 | ||
1005 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | |
1006 | return -EINVAL; | |
1007 | ||
1008 | octeon_irq_set_ciu_mapping(virq, line, bit, | |
1009 | octeon_irq_gpio_chip, | |
1010 | octeon_irq_handle_gpio); | |
1011 | ||
1012 | return 0; | |
1013 | } | |
1014 | ||
1015 | static struct irq_domain_ops octeon_irq_domain_ciu_ops = { | |
1016 | .map = octeon_irq_ciu_map, | |
1017 | .xlate = octeon_irq_ciu_xlat, | |
1018 | }; | |
1019 | ||
1020 | static struct irq_domain_ops octeon_irq_domain_gpio_ops = { | |
1021 | .map = octeon_irq_gpio_map, | |
1022 | .xlate = octeon_irq_gpio_xlat, | |
1023 | }; | |
1024 | ||
0c326387 | 1025 | static void octeon_irq_ip2_v1(void) |
5b3b1688 | 1026 | { |
0c326387 DD |
1027 | const unsigned long core_id = cvmx_get_core_num(); |
1028 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); | |
1029 | ||
1030 | ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); | |
1031 | clear_c0_status(STATUSF_IP2); | |
1032 | if (likely(ciu_sum)) { | |
1033 | int bit = fls64(ciu_sum) - 1; | |
1034 | int irq = octeon_irq_ciu_to_irq[0][bit]; | |
1035 | if (likely(irq)) | |
1036 | do_IRQ(irq); | |
1037 | else | |
1038 | spurious_interrupt(); | |
1039 | } else { | |
1040 | spurious_interrupt(); | |
5b3b1688 | 1041 | } |
0c326387 | 1042 | set_c0_status(STATUSF_IP2); |
cd847b78 DD |
1043 | } |
1044 | ||
0c326387 | 1045 | static void octeon_irq_ip2_v2(void) |
cd847b78 | 1046 | { |
0c326387 DD |
1047 | const unsigned long core_id = cvmx_get_core_num(); |
1048 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); | |
1049 | ||
1050 | ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); | |
1051 | if (likely(ciu_sum)) { | |
1052 | int bit = fls64(ciu_sum) - 1; | |
1053 | int irq = octeon_irq_ciu_to_irq[0][bit]; | |
1054 | if (likely(irq)) | |
1055 | do_IRQ(irq); | |
1056 | else | |
1057 | spurious_interrupt(); | |
1058 | } else { | |
1059 | spurious_interrupt(); | |
5aae1fd4 | 1060 | } |
cd847b78 | 1061 | } |
0c326387 | 1062 | static void octeon_irq_ip3_v1(void) |
cd847b78 | 1063 | { |
0c326387 DD |
1064 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); |
1065 | ||
1066 | ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); | |
1067 | clear_c0_status(STATUSF_IP3); | |
1068 | if (likely(ciu_sum)) { | |
1069 | int bit = fls64(ciu_sum) - 1; | |
1070 | int irq = octeon_irq_ciu_to_irq[1][bit]; | |
1071 | if (likely(irq)) | |
1072 | do_IRQ(irq); | |
1073 | else | |
1074 | spurious_interrupt(); | |
1075 | } else { | |
1076 | spurious_interrupt(); | |
5aae1fd4 | 1077 | } |
0c326387 | 1078 | set_c0_status(STATUSF_IP3); |
cd847b78 DD |
1079 | } |
1080 | ||
0c326387 | 1081 | static void octeon_irq_ip3_v2(void) |
dbb103b2 | 1082 | { |
0c326387 DD |
1083 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); |
1084 | ||
1085 | ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); | |
1086 | if (likely(ciu_sum)) { | |
1087 | int bit = fls64(ciu_sum) - 1; | |
1088 | int irq = octeon_irq_ciu_to_irq[1][bit]; | |
1089 | if (likely(irq)) | |
1090 | do_IRQ(irq); | |
1091 | else | |
1092 | spurious_interrupt(); | |
1093 | } else { | |
1094 | spurious_interrupt(); | |
1095 | } | |
dbb103b2 DD |
1096 | } |
1097 | ||
0c326387 | 1098 | static void octeon_irq_ip4_mask(void) |
cd847b78 | 1099 | { |
0c326387 DD |
1100 | clear_c0_status(STATUSF_IP4); |
1101 | spurious_interrupt(); | |
5b3b1688 DD |
1102 | } |
1103 | ||
0c326387 DD |
1104 | static void (*octeon_irq_ip2)(void); |
1105 | static void (*octeon_irq_ip3)(void); | |
1106 | static void (*octeon_irq_ip4)(void); | |
5b3b1688 | 1107 | |
0c326387 | 1108 | void __cpuinitdata (*octeon_irq_setup_secondary)(void); |
5aae1fd4 | 1109 | |
0c326387 DD |
1110 | static void __cpuinit octeon_irq_percpu_enable(void) |
1111 | { | |
1112 | irq_cpu_online(); | |
1113 | } | |
1114 | ||
1115 | static void __cpuinit octeon_irq_init_ciu_percpu(void) | |
1116 | { | |
1117 | int coreid = cvmx_get_core_num(); | |
5b3b1688 | 1118 | /* |
0c326387 DD |
1119 | * Disable All CIU Interrupts. The ones we need will be |
1120 | * enabled later. Read the SUM register so we know the write | |
1121 | * completed. | |
5b3b1688 | 1122 | */ |
0c326387 DD |
1123 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0); |
1124 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0); | |
1125 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0); | |
1126 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0); | |
1127 | cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); | |
5b3b1688 | 1128 | } |
cd847b78 | 1129 | |
0c326387 | 1130 | static void __cpuinit octeon_irq_setup_secondary_ciu(void) |
cd847b78 | 1131 | { |
5b3b1688 | 1132 | |
0c326387 DD |
1133 | __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0; |
1134 | __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0; | |
cd847b78 | 1135 | |
0c326387 DD |
1136 | octeon_irq_init_ciu_percpu(); |
1137 | octeon_irq_percpu_enable(); | |
5b3b1688 | 1138 | |
0c326387 DD |
1139 | /* Enable the CIU lines */ |
1140 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1141 | clear_c0_status(STATUSF_IP4); | |
1142 | } | |
5aae1fd4 | 1143 | |
0c326387 DD |
1144 | static void __init octeon_irq_init_ciu(void) |
1145 | { | |
1146 | unsigned int i; | |
1147 | struct irq_chip *chip; | |
0c326387 DD |
1148 | struct irq_chip *chip_mbox; |
1149 | struct irq_chip *chip_wd; | |
a0c16582 DD |
1150 | struct device_node *gpio_node; |
1151 | struct device_node *ciu_node; | |
0c326387 DD |
1152 | |
1153 | octeon_irq_init_ciu_percpu(); | |
1154 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu; | |
5aae1fd4 | 1155 | |
0c326387 DD |
1156 | if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) || |
1157 | OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || | |
1158 | OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) || | |
1159 | OCTEON_IS_MODEL(OCTEON_CN6XXX)) { | |
1160 | octeon_irq_ip2 = octeon_irq_ip2_v2; | |
1161 | octeon_irq_ip3 = octeon_irq_ip3_v2; | |
1162 | chip = &octeon_irq_chip_ciu_v2; | |
0c326387 DD |
1163 | chip_mbox = &octeon_irq_chip_ciu_mbox_v2; |
1164 | chip_wd = &octeon_irq_chip_ciu_wd_v2; | |
a0c16582 | 1165 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2; |
0c326387 DD |
1166 | } else { |
1167 | octeon_irq_ip2 = octeon_irq_ip2_v1; | |
1168 | octeon_irq_ip3 = octeon_irq_ip3_v1; | |
1169 | chip = &octeon_irq_chip_ciu; | |
0c326387 DD |
1170 | chip_mbox = &octeon_irq_chip_ciu_mbox; |
1171 | chip_wd = &octeon_irq_chip_ciu_wd; | |
a0c16582 | 1172 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio; |
0c326387 | 1173 | } |
a0c16582 | 1174 | octeon_irq_ciu_chip = chip; |
0c326387 DD |
1175 | octeon_irq_ip4 = octeon_irq_ip4_mask; |
1176 | ||
1177 | /* Mips internal */ | |
1178 | octeon_irq_init_core(); | |
1179 | ||
1180 | /* CIU_0 */ | |
1181 | for (i = 0; i < 16; i++) | |
1182 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WORKQ0, 0, i + 0, chip, handle_level_irq); | |
0c326387 DD |
1183 | |
1184 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, chip_mbox, handle_percpu_irq); | |
1185 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, chip_mbox, handle_percpu_irq); | |
1186 | ||
0c326387 DD |
1187 | for (i = 0; i < 4; i++) |
1188 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq); | |
1189 | for (i = 0; i < 4; i++) | |
1190 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq); | |
1191 | ||
0c326387 | 1192 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq); |
0c326387 | 1193 | for (i = 0; i < 4; i++) |
a339aef9 | 1194 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip, handle_edge_irq); |
0c326387 DD |
1195 | |
1196 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq); | |
0c326387 DD |
1197 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq); |
1198 | ||
1199 | /* CIU_1 */ | |
1200 | for (i = 0; i < 16; i++) | |
1201 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq); | |
1202 | ||
0c326387 | 1203 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq); |
0c326387 | 1204 | |
a0c16582 DD |
1205 | gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio"); |
1206 | if (gpio_node) { | |
1207 | struct octeon_irq_gpio_domain_data *gpiod; | |
1208 | ||
1209 | gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL); | |
1210 | if (gpiod) { | |
1211 | /* gpio domain host_data is the base hwirq number. */ | |
1212 | gpiod->base_hwirq = 16; | |
1213 | irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod); | |
1214 | of_node_put(gpio_node); | |
1215 | } else | |
1216 | pr_warn("Cannot allocate memory for GPIO irq_domain.\n"); | |
1217 | } else | |
1218 | pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n"); | |
1219 | ||
1220 | ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu"); | |
1221 | if (ciu_node) { | |
1222 | irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL); | |
1223 | of_node_put(ciu_node); | |
1224 | } else | |
1225 | pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n"); | |
1226 | ||
0c326387 DD |
1227 | /* Enable the CIU lines */ |
1228 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1229 | clear_c0_status(STATUSF_IP4); | |
1230 | } | |
5aae1fd4 | 1231 | |
5b3b1688 DD |
1232 | void __init arch_init_irq(void) |
1233 | { | |
5b3b1688 DD |
1234 | #ifdef CONFIG_SMP |
1235 | /* Set the default affinity to the boot cpu. */ | |
1236 | cpumask_clear(irq_default_affinity); | |
1237 | cpumask_set_cpu(smp_processor_id(), irq_default_affinity); | |
1238 | #endif | |
0c326387 | 1239 | octeon_irq_init_ciu(); |
5b3b1688 DD |
1240 | } |
1241 | ||
1242 | asmlinkage void plat_irq_dispatch(void) | |
1243 | { | |
5b3b1688 DD |
1244 | unsigned long cop0_cause; |
1245 | unsigned long cop0_status; | |
5b3b1688 DD |
1246 | |
1247 | while (1) { | |
1248 | cop0_cause = read_c0_cause(); | |
1249 | cop0_status = read_c0_status(); | |
1250 | cop0_cause &= cop0_status; | |
1251 | cop0_cause &= ST0_IM; | |
1252 | ||
0c326387 DD |
1253 | if (unlikely(cop0_cause & STATUSF_IP2)) |
1254 | octeon_irq_ip2(); | |
1255 | else if (unlikely(cop0_cause & STATUSF_IP3)) | |
1256 | octeon_irq_ip3(); | |
1257 | else if (unlikely(cop0_cause & STATUSF_IP4)) | |
1258 | octeon_irq_ip4(); | |
1259 | else if (likely(cop0_cause)) | |
5b3b1688 | 1260 | do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE); |
0c326387 | 1261 | else |
5b3b1688 | 1262 | break; |
5b3b1688 DD |
1263 | } |
1264 | } | |
773cb77d RB |
1265 | |
1266 | #ifdef CONFIG_HOTPLUG_CPU | |
773cb77d RB |
1267 | |
1268 | void fixup_irqs(void) | |
1269 | { | |
0c326387 | 1270 | irq_cpu_offline(); |
773cb77d RB |
1271 | } |
1272 | ||
1273 | #endif /* CONFIG_HOTPLUG_CPU */ |