blob: 52610ce50dfcb26b4b3f542c26f10a926e6e5b62 [file] [log] [blame]
David Daney5b3b1682009-01-08 16:46:40 -08001/*
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 *
David Daneya0c16582012-07-05 18:12:39 +02006 * Copyright (C) 2004-2012 Cavium, Inc.
David Daney5b3b1682009-01-08 16:46:40 -08007 */
David Daney0c326382011-03-25 12:38:51 -07008
David Daney5b3b1682009-01-08 16:46:40 -08009#include <linux/interrupt.h>
David Daneya0c16582012-07-05 18:12:39 +020010#include <linux/irqdomain.h>
David Daney0c326382011-03-25 12:38:51 -070011#include <linux/bitops.h>
12#include <linux/percpu.h>
David Daneya0c16582012-07-05 18:12:39 +020013#include <linux/slab.h>
David Daney0c326382011-03-25 12:38:51 -070014#include <linux/irq.h>
Ralf Baechle631330f2009-06-19 14:05:26 +010015#include <linux/smp.h>
David Daneya0c16582012-07-05 18:12:39 +020016#include <linux/of.h>
David Daney5b3b1682009-01-08 16:46:40 -080017
18#include <asm/octeon/octeon.h>
19
David Daney39961422010-02-18 11:47:40 -080020static DEFINE_RAW_SPINLOCK(octeon_irq_ciu0_lock);
21static DEFINE_RAW_SPINLOCK(octeon_irq_ciu1_lock);
David Daney5b3b1682009-01-08 16:46:40 -080022
David Daney0c326382011-03-25 12:38:51 -070023static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror);
24static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror);
25
26static __read_mostly u8 octeon_irq_ciu_to_irq[8][64];
27
28union 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
37struct 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
46static struct octeon_core_chip_data octeon_irq_core_chip_data[MIPS_CORE_IRQ_LINES];
47
David Daneya0c16582012-07-05 18:12:39 +020048static void octeon_irq_set_ciu_mapping(int irq, int line, int bit,
49 struct irq_chip *chip,
50 irq_flow_handler_t handler)
David Daney0c326382011-03-25 12:38:51 -070051{
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
David Daneycd847b72009-10-13 11:26:03 -070064static 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
David Daney0c326382011-03-25 12:38:51 -070073static int octeon_cpu_for_coreid(int coreid)
David Daney5b3b1682009-01-08 16:46:40 -080074{
David Daney0c326382011-03-25 12:38:51 -070075#ifdef CONFIG_SMP
76 return cpu_number_map(coreid);
77#else
78 return smp_processor_id();
79#endif
80}
81
82static void octeon_irq_core_ack(struct irq_data *data)
83{
84 struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
85 unsigned int bit = cd->bit;
86
David Daney5b3b1682009-01-08 16:46:40 -080087 /*
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
David Daney0c326382011-03-25 12:38:51 -070098static void octeon_irq_core_eoi(struct irq_data *data)
David Daney5b3b1682009-01-08 16:46:40 -080099{
David Daney0c326382011-03-25 12:38:51 -0700100 struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
101
David Daney5b3b1682009-01-08 16:46:40 -0800102 /*
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 */
David Daney0c326382011-03-25 12:38:51 -0700107 set_c0_status(0x100 << cd->bit);
David Daney5b3b1682009-01-08 16:46:40 -0800108}
109
David Daney0c326382011-03-25 12:38:51 -0700110static void octeon_irq_core_set_enable_local(void *arg)
David Daney5b3b1682009-01-08 16:46:40 -0800111{
David Daney0c326382011-03-25 12:38:51 -0700112 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;
David Daney5b3b1682009-01-08 16:46:40 -0800115
116 /*
David Daney0c326382011-03-25 12:38:51 -0700117 * Interrupts are already disabled, so these are atomic.
David Daney5b3b1682009-01-08 16:46:40 -0800118 */
David Daney0c326382011-03-25 12:38:51 -0700119 if (cd->desired_en)
120 set_c0_status(mask);
121 else
122 clear_c0_status(mask);
123
David Daney5b3b1682009-01-08 16:46:40 -0800124}
125
David Daney0c326382011-03-25 12:38:51 -0700126static void octeon_irq_core_disable(struct irq_data *data)
David Daney5b3b1682009-01-08 16:46:40 -0800127{
David Daney0c326382011-03-25 12:38:51 -0700128 struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
129 cd->desired_en = false;
David Daney5b3b1682009-01-08 16:46:40 -0800130}
131
David Daney0c326382011-03-25 12:38:51 -0700132static void octeon_irq_core_enable(struct irq_data *data)
David Daney5b3b1682009-01-08 16:46:40 -0800133{
David Daney0c326382011-03-25 12:38:51 -0700134 struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data);
135 cd->desired_en = true;
136}
137
138static 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);
141
142 mutex_lock(&cd->core_irq_mutex);
143}
144
145static void octeon_irq_core_bus_sync_unlock(struct irq_data *data)
146{
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;
153 }
154
155 mutex_unlock(&cd->core_irq_mutex);
156}
157
David Daney5b3b1682009-01-08 16:46:40 -0800158static struct irq_chip octeon_irq_chip_core = {
159 .name = "Core",
David Daney0c326382011-03-25 12:38:51 -0700160 .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
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200167 .irq_cpu_online = octeon_irq_core_eoi,
168 .irq_cpu_offline = octeon_irq_core_ack,
169 .flags = IRQCHIP_ONOFFLINE_ENABLED,
David Daney5b3b1682009-01-08 16:46:40 -0800170};
171
David Daney0c326382011-03-25 12:38:51 -0700172static void __init octeon_irq_init_core(void)
David Daney5b3b1682009-01-08 16:46:40 -0800173{
David Daney0c326382011-03-25 12:38:51 -0700174 int i;
175 int irq;
176 struct octeon_core_chip_data *cd;
David Daney5aae1fd2010-07-23 10:43:46 -0700177
David Daney0c326382011-03-25 12:38:51 -0700178 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 }
David Daney5b3b1682009-01-08 16:46:40 -0800200}
201
David Daney0c326382011-03-25 12:38:51 -0700202static int next_cpu_for_irq(struct irq_data *data)
David Daney5aae1fd2010-07-23 10:43:46 -0700203{
204
205#ifdef CONFIG_SMP
David Daney0c326382011-03-25 12:38:51 -0700206 int cpu;
207 int weight = cpumask_weight(data->affinity);
David Daney5aae1fd2010-07-23 10:43:46 -0700208
209 if (weight > 1) {
David Daney0c326382011-03-25 12:38:51 -0700210 cpu = smp_processor_id();
David Daney5aae1fd2010-07-23 10:43:46 -0700211 for (;;) {
David Daney0c326382011-03-25 12:38:51 -0700212 cpu = cpumask_next(cpu, data->affinity);
David Daney5aae1fd2010-07-23 10:43:46 -0700213 if (cpu >= nr_cpu_ids) {
214 cpu = -1;
215 continue;
216 } else if (cpumask_test_cpu(cpu, cpu_online_mask)) {
217 break;
218 }
219 }
David Daney5aae1fd2010-07-23 10:43:46 -0700220 } else if (weight == 1) {
David Daney0c326382011-03-25 12:38:51 -0700221 cpu = cpumask_first(data->affinity);
David Daney5aae1fd2010-07-23 10:43:46 -0700222 } else {
David Daney0c326382011-03-25 12:38:51 -0700223 cpu = smp_processor_id();
David Daney5aae1fd2010-07-23 10:43:46 -0700224 }
David Daney0c326382011-03-25 12:38:51 -0700225 return cpu;
David Daney5aae1fd2010-07-23 10:43:46 -0700226#else
David Daney0c326382011-03-25 12:38:51 -0700227 return smp_processor_id();
David Daney5aae1fd2010-07-23 10:43:46 -0700228#endif
229}
230
David Daney0c326382011-03-25 12:38:51 -0700231static void octeon_irq_ciu_enable(struct irq_data *data)
David Daney5b3b1682009-01-08 16:46:40 -0800232{
David Daney0c326382011-03-25 12:38:51 -0700233 int cpu = next_cpu_for_irq(data);
234 int coreid = octeon_coreid_for_cpu(cpu);
235 unsigned long *pen;
David Daney5aae1fd2010-07-23 10:43:46 -0700236 unsigned long flags;
David Daney0c326382011-03-25 12:38:51 -0700237 union octeon_ciu_chip_data cd;
David Daney5aae1fd2010-07-23 10:43:46 -0700238
David Daney0c326382011-03-25 12:38:51 -0700239 cd.p = irq_data_get_irq_chip_data(data);
David Daney5aae1fd2010-07-23 10:43:46 -0700240
David Daney0c326382011-03-25 12:38:51 -0700241 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);
David Daney5b3b1682009-01-08 16:46:40 -0800253 }
David Daney0c326382011-03-25 12:38:51 -0700254}
255
256static 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
279static 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
302static void octeon_irq_ciu_disable_all(struct irq_data *data)
303{
304 unsigned long flags;
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. */
310
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 }
332}
333
334static void octeon_irq_ciu_enable_all(struct irq_data *data)
335{
336 unsigned long flags;
337 unsigned long *pen;
338 int cpu;
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);
361 }
David Daneycd847b72009-10-13 11:26:03 -0700362}
363
364/*
David Daney5aae1fd2010-07-23 10:43:46 -0700365 * Enable the irq on the next core in the affinity set for chips that
366 * have the EN*_W1{S,C} registers.
David Daneycd847b72009-10-13 11:26:03 -0700367 */
David Daney0c326382011-03-25 12:38:51 -0700368static void octeon_irq_ciu_enable_v2(struct irq_data *data)
David Daneycd847b72009-10-13 11:26:03 -0700369{
David Daney0c326382011-03-25 12:38:51 -0700370 u64 mask;
371 int cpu = next_cpu_for_irq(data);
372 union octeon_ciu_chip_data cd;
David Daney5aae1fd2010-07-23 10:43:46 -0700373
David Daney0c326382011-03-25 12:38:51 -0700374 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));
David Daney5aae1fd2010-07-23 10:43:46 -0700384 cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
David Daney0c326382011-03-25 12:38:51 -0700385 } 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);
David Daney5aae1fd2010-07-23 10:43:46 -0700389 }
390}
391
392/*
393 * Enable the irq on the current CPU for chips that
394 * have the EN*_W1{S,C} registers.
395 */
David Daney0c326382011-03-25 12:38:51 -0700396static void octeon_irq_ciu_enable_local_v2(struct irq_data *data)
David Daney5aae1fd2010-07-23 10:43:46 -0700397{
David Daney0c326382011-03-25 12:38:51 -0700398 u64 mask;
399 union octeon_ciu_chip_data cd;
David Daneycd847b72009-10-13 11:26:03 -0700400
David Daney0c326382011-03-25 12:38:51 -0700401 cd.p = irq_data_get_irq_chip_data(data);
402 mask = 1ull << (cd.s.bit);
David Daneycd847b72009-10-13 11:26:03 -0700403
David Daney0c326382011-03-25 12:38:51 -0700404 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));
David Daneydbb103b2010-01-07 11:05:00 -0800407 cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask);
David Daney0c326382011-03-25 12:38:51 -0700408 } 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 }
David Daneydbb103b2010-01-07 11:05:00 -0800413}
414
David Daney0c326382011-03-25 12:38:51 -0700415static void octeon_irq_ciu_disable_local_v2(struct irq_data *data)
David Daneycd847b72009-10-13 11:26:03 -0700416{
David Daney0c326382011-03-25 12:38:51 -0700417 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));
David Daneycd847b72009-10-13 11:26:03 -0700426 cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask);
David Daney0c326382011-03-25 12:38:51 -0700427 } else {
428 int index = cvmx_get_core_num() * 2 + 1;
429 clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror));
David Daneycd847b72009-10-13 11:26:03 -0700430 cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask);
431 }
David Daney5b3b1682009-01-08 16:46:40 -0800432}
433
David Daney0c326382011-03-25 12:38:51 -0700434/*
435 * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq.
436 */
437static 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;
447 cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask);
448 } else {
449 cvmx_write_csr(CVMX_CIU_INT_SUM1, mask);
450 }
451}
452
453/*
454 * Disable the irq on the all cores for chips that have the EN*_W1{S,C}
455 * registers.
456 */
457static void octeon_irq_ciu_disable_all_v2(struct irq_data *data)
David Daney5b3b1682009-01-08 16:46:40 -0800458{
459 int cpu;
David Daney0c326382011-03-25 12:38:51 -0700460 u64 mask;
461 union octeon_ciu_chip_data cd;
462
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 }
481}
482
483/*
484 * Enable the irq on the all cores for chips that have the EN*_W1{S,C}
485 * registers.
486 */
487static void octeon_irq_ciu_enable_all_v2(struct irq_data *data)
488{
489 int cpu;
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 }
508 }
509}
510
David Daney6d1ab4c2012-07-05 18:12:37 +0200511static 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
531static 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
537static 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
543static 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
551static 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
561static 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
571static 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
582static 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
David Daney0c326382011-03-25 12:38:51 -0700590#ifdef CONFIG_SMP
591
592static 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
615static int octeon_irq_ciu_set_affinity(struct irq_data *data,
616 const struct cpumask *dest, bool force)
617{
618 int cpu;
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200619 bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
David Daneyb6b74d52009-10-13 08:52:28 -0700620 unsigned long flags;
David Daney0c326382011-03-25 12:38:51 -0700621 union octeon_ciu_chip_data cd;
622
623 cd.p = data->chip_data;
David Daney5b3b1682009-01-08 16:46:40 -0800624
David Daney5aae1fd2010-07-23 10:43:46 -0700625 /*
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
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200633 if (!enable_one)
David Daney0c326382011-03-25 12:38:51 -0700634 return 0;
Yinghai Lud5dedd42009-04-27 17:59:21 -0700635
David Daney0c326382011-03-25 12:38:51 -0700636 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) {
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200643 enable_one = false;
David Daney0c326382011-03-25 12:38:51 -0700644 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) {
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200658 enable_one = false;
David Daney0c326382011-03-25 12:38:51 -0700659 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);
664 }
665 raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
666 }
Yinghai Lud5dedd42009-04-27 17:59:21 -0700667 return 0;
David Daney5b3b1682009-01-08 16:46:40 -0800668}
David Daneycd847b72009-10-13 11:26:03 -0700669
670/*
671 * Set affinity for the irq for chips that have the EN*_W1{S,C}
672 * registers.
673 */
David Daney0c326382011-03-25 12:38:51 -0700674static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data,
675 const struct cpumask *dest,
676 bool force)
David Daneycd847b72009-10-13 11:26:03 -0700677{
678 int cpu;
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200679 bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data);
David Daney0c326382011-03-25 12:38:51 -0700680 u64 mask;
681 union octeon_ciu_chip_data cd;
682
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200683 if (!enable_one)
David Daney0c326382011-03-25 12:38:51 -0700684 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) {
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200694 enable_one = false;
David Daney0c326382011-03-25 12:38:51 -0700695 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) {
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200707 enable_one = false;
David Daney0c326382011-03-25 12:38:51 -0700708 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 }
David Daney5aae1fd2010-07-23 10:43:46 -0700714 }
David Daneycd847b72009-10-13 11:26:03 -0700715 }
716 return 0;
717}
David Daney5b3b1682009-01-08 16:46:40 -0800718#endif
719
David Daneycd847b72009-10-13 11:26:03 -0700720/*
David Daney0c326382011-03-25 12:38:51 -0700721 * The v1 CIU code already masks things, so supply a dummy version to
722 * the core chip code.
723 */
724static void octeon_irq_dummy_mask(struct irq_data *data)
725{
David Daney0c326382011-03-25 12:38:51 -0700726}
727
728/*
David Daneycd847b72009-10-13 11:26:03 -0700729 * Newer octeon chips have support for lockless CIU operation.
730 */
David Daney0c326382011-03-25 12:38:51 -0700731static 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,
David Daney0c326382011-03-25 12:38:51 -0700735 .irq_ack = octeon_irq_ciu_ack,
736 .irq_mask = octeon_irq_ciu_disable_local_v2,
737 .irq_unmask = octeon_irq_ciu_enable_v2,
David Daney5b3b1682009-01-08 16:46:40 -0800738#ifdef CONFIG_SMP
David Daney0c326382011-03-25 12:38:51 -0700739 .irq_set_affinity = octeon_irq_ciu_set_affinity_v2,
740 .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
David Daney5b3b1682009-01-08 16:46:40 -0800741#endif
742};
743
David Daney0c326382011-03-25 12:38:51 -0700744static 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,
David Daney0c326382011-03-25 12:38:51 -0700748 .irq_ack = octeon_irq_ciu_ack,
David Daneya339aef2012-07-05 18:12:38 +0200749 .irq_mask = octeon_irq_dummy_mask,
David Daney0c326382011-03-25 12:38:51 -0700750#ifdef CONFIG_SMP
751 .irq_set_affinity = octeon_irq_ciu_set_affinity,
752 .irq_cpu_offline = octeon_irq_cpu_offline_ciu,
753#endif
David Daney5aae1fd2010-07-23 10:43:46 -0700754};
755
David Daney0c326382011-03-25 12:38:51 -0700756/* The mbox versions don't do any affinity or round-robin. */
757static 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
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200764 .irq_cpu_online = octeon_irq_ciu_enable_local_v2,
765 .irq_cpu_offline = octeon_irq_ciu_disable_local_v2,
766 .flags = IRQCHIP_ONOFFLINE_ENABLED,
David Daney0c326382011-03-25 12:38:51 -0700767};
768
769static 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,
773
Thomas Gleixner5b7cd6f2011-03-27 16:04:30 +0200774 .irq_cpu_online = octeon_irq_ciu_enable_local,
775 .irq_cpu_offline = octeon_irq_ciu_disable_local,
776 .flags = IRQCHIP_ONOFFLINE_ENABLED,
David Daney0c326382011-03-25 12:38:51 -0700777};
778
David Daney6d1ab4c2012-07-05 18:12:37 +0200779static 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
793static 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
David Daney0c326382011-03-25 12:38:51 -0700806/*
807 * Watchdog interrupts are special. They are associated with a single
808 * core, so we hardwire the affinity to that core.
809 */
810static void octeon_irq_ciu_wd_enable(struct irq_data *data)
811{
812 unsigned long flags;
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);
816
817 raw_spin_lock_irqsave(&octeon_irq_ciu1_lock, flags);
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);
821 raw_spin_unlock_irqrestore(&octeon_irq_ciu1_lock, flags);
822}
823
824/*
825 * Watchdog interrupts are special. They are associated with a single
826 * core, so we hardwire the affinity to that core.
827 */
828static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data)
829{
830 int coreid = data->irq - OCTEON_IRQ_WDOG0;
831 int cpu = octeon_cpu_for_coreid(coreid);
832
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);
835}
836
837
838static 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
846static 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
David Daneya0c16582012-07-05 18:12:39 +0200853static 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
879struct octeon_irq_gpio_domain_data {
880 unsigned int base_hwirq;
881};
882
883static 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
934static 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
959static struct irq_chip *octeon_irq_ciu_chip;
960static struct irq_chip *octeon_irq_gpio_chip;
961
962static 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
972static 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
996static 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
1015static struct irq_domain_ops octeon_irq_domain_ciu_ops = {
1016 .map = octeon_irq_ciu_map,
1017 .xlate = octeon_irq_ciu_xlat,
1018};
1019
1020static struct irq_domain_ops octeon_irq_domain_gpio_ops = {
1021 .map = octeon_irq_gpio_map,
1022 .xlate = octeon_irq_gpio_xlat,
1023};
1024
David Daney0c326382011-03-25 12:38:51 -07001025static void octeon_irq_ip2_v1(void)
1026{
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();
1041 }
1042 set_c0_status(STATUSF_IP2);
1043}
1044
1045static void octeon_irq_ip2_v2(void)
1046{
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();
1060 }
1061}
1062static void octeon_irq_ip3_v1(void)
1063{
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();
1077 }
1078 set_c0_status(STATUSF_IP3);
1079}
1080
1081static void octeon_irq_ip3_v2(void)
1082{
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 }
1096}
1097
1098static void octeon_irq_ip4_mask(void)
1099{
1100 clear_c0_status(STATUSF_IP4);
1101 spurious_interrupt();
1102}
1103
1104static void (*octeon_irq_ip2)(void);
1105static void (*octeon_irq_ip3)(void);
1106static void (*octeon_irq_ip4)(void);
1107
1108void __cpuinitdata (*octeon_irq_setup_secondary)(void);
1109
1110static void __cpuinit octeon_irq_percpu_enable(void)
1111{
1112 irq_cpu_online();
1113}
1114
1115static void __cpuinit octeon_irq_init_ciu_percpu(void)
1116{
1117 int coreid = cvmx_get_core_num();
1118 /*
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.
1122 */
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)));
1128}
1129
1130static void __cpuinit octeon_irq_setup_secondary_ciu(void)
1131{
1132
1133 __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0;
1134 __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0;
1135
1136 octeon_irq_init_ciu_percpu();
1137 octeon_irq_percpu_enable();
1138
1139 /* Enable the CIU lines */
1140 set_c0_status(STATUSF_IP3 | STATUSF_IP2);
1141 clear_c0_status(STATUSF_IP4);
1142}
1143
1144static void __init octeon_irq_init_ciu(void)
1145{
1146 unsigned int i;
1147 struct irq_chip *chip;
David Daney0c326382011-03-25 12:38:51 -07001148 struct irq_chip *chip_mbox;
1149 struct irq_chip *chip_wd;
David Daneya0c16582012-07-05 18:12:39 +02001150 struct device_node *gpio_node;
1151 struct device_node *ciu_node;
David Daney0c326382011-03-25 12:38:51 -07001152
1153 octeon_irq_init_ciu_percpu();
1154 octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu;
1155
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;
David Daney0c326382011-03-25 12:38:51 -07001163 chip_mbox = &octeon_irq_chip_ciu_mbox_v2;
1164 chip_wd = &octeon_irq_chip_ciu_wd_v2;
David Daneya0c16582012-07-05 18:12:39 +02001165 octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2;
David Daney0c326382011-03-25 12:38:51 -07001166 } else {
1167 octeon_irq_ip2 = octeon_irq_ip2_v1;
1168 octeon_irq_ip3 = octeon_irq_ip3_v1;
1169 chip = &octeon_irq_chip_ciu;
David Daney0c326382011-03-25 12:38:51 -07001170 chip_mbox = &octeon_irq_chip_ciu_mbox;
1171 chip_wd = &octeon_irq_chip_ciu_wd;
David Daneya0c16582012-07-05 18:12:39 +02001172 octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio;
David Daney0c326382011-03-25 12:38:51 -07001173 }
David Daneya0c16582012-07-05 18:12:39 +02001174 octeon_irq_ciu_chip = chip;
David Daney0c326382011-03-25 12:38:51 -07001175 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);
David Daney0c326382011-03-25 12:38:51 -07001183
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
1187 octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART0, 0, 34, chip, handle_level_irq);
1188 octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART1, 0, 35, chip, handle_level_irq);
1189
1190 for (i = 0; i < 4; i++)
1191 octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_INT0, 0, i + 36, chip, handle_level_irq);
1192 for (i = 0; i < 4; i++)
1193 octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_PCI_MSI0, 0, i + 40, chip, handle_level_irq);
1194
1195 octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI, 0, 45, chip, handle_level_irq);
1196 octeon_irq_set_ciu_mapping(OCTEON_IRQ_RML, 0, 46, chip, handle_level_irq);
David Daney0c326382011-03-25 12:38:51 -07001197 for (i = 0; i < 4; i++)
David Daneya339aef2012-07-05 18:12:38 +02001198 octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_TIMER0, 0, i + 52, chip, handle_edge_irq);
David Daney0c326382011-03-25 12:38:51 -07001199
1200 octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB0, 0, 56, chip, handle_level_irq);
David Daney0c326382011-03-25 12:38:51 -07001201 octeon_irq_set_ciu_mapping(OCTEON_IRQ_TWSI2, 0, 59, chip, handle_level_irq);
David Daney0c326382011-03-25 12:38:51 -07001202 octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII0, 0, 62, chip, handle_level_irq);
1203 octeon_irq_set_ciu_mapping(OCTEON_IRQ_BOOTDMA, 0, 63, chip, handle_level_irq);
1204
1205 /* CIU_1 */
1206 for (i = 0; i < 16; i++)
1207 octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, chip_wd, handle_level_irq);
1208
1209 octeon_irq_set_ciu_mapping(OCTEON_IRQ_UART2, 1, 16, chip, handle_level_irq);
1210 octeon_irq_set_ciu_mapping(OCTEON_IRQ_USB1, 1, 17, chip, handle_level_irq);
1211 octeon_irq_set_ciu_mapping(OCTEON_IRQ_MII1, 1, 18, chip, handle_level_irq);
David Daney0c326382011-03-25 12:38:51 -07001212
David Daneya0c16582012-07-05 18:12:39 +02001213 gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio");
1214 if (gpio_node) {
1215 struct octeon_irq_gpio_domain_data *gpiod;
1216
1217 gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL);
1218 if (gpiod) {
1219 /* gpio domain host_data is the base hwirq number. */
1220 gpiod->base_hwirq = 16;
1221 irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod);
1222 of_node_put(gpio_node);
1223 } else
1224 pr_warn("Cannot allocate memory for GPIO irq_domain.\n");
1225 } else
1226 pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n");
1227
1228 ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu");
1229 if (ciu_node) {
1230 irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL);
1231 of_node_put(ciu_node);
1232 } else
1233 pr_warn("Cannot find device node for cavium,octeon-3860-ciu.\n");
1234
David Daney0c326382011-03-25 12:38:51 -07001235 /* Enable the CIU lines */
1236 set_c0_status(STATUSF_IP3 | STATUSF_IP2);
1237 clear_c0_status(STATUSF_IP4);
1238}
David Daney5aae1fd2010-07-23 10:43:46 -07001239
David Daney5b3b1682009-01-08 16:46:40 -08001240void __init arch_init_irq(void)
1241{
David Daney5b3b1682009-01-08 16:46:40 -08001242#ifdef CONFIG_SMP
1243 /* Set the default affinity to the boot cpu. */
1244 cpumask_clear(irq_default_affinity);
1245 cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
1246#endif
David Daney0c326382011-03-25 12:38:51 -07001247 octeon_irq_init_ciu();
David Daney5b3b1682009-01-08 16:46:40 -08001248}
1249
1250asmlinkage void plat_irq_dispatch(void)
1251{
David Daney5b3b1682009-01-08 16:46:40 -08001252 unsigned long cop0_cause;
1253 unsigned long cop0_status;
David Daney5b3b1682009-01-08 16:46:40 -08001254
1255 while (1) {
1256 cop0_cause = read_c0_cause();
1257 cop0_status = read_c0_status();
1258 cop0_cause &= cop0_status;
1259 cop0_cause &= ST0_IM;
1260
David Daney0c326382011-03-25 12:38:51 -07001261 if (unlikely(cop0_cause & STATUSF_IP2))
1262 octeon_irq_ip2();
1263 else if (unlikely(cop0_cause & STATUSF_IP3))
1264 octeon_irq_ip3();
1265 else if (unlikely(cop0_cause & STATUSF_IP4))
1266 octeon_irq_ip4();
1267 else if (likely(cop0_cause))
David Daney5b3b1682009-01-08 16:46:40 -08001268 do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE);
David Daney0c326382011-03-25 12:38:51 -07001269 else
David Daney5b3b1682009-01-08 16:46:40 -08001270 break;
David Daney5b3b1682009-01-08 16:46:40 -08001271 }
1272}
Ralf Baechle773cb772009-06-23 10:36:38 +01001273
1274#ifdef CONFIG_HOTPLUG_CPU
Ralf Baechle773cb772009-06-23 10:36:38 +01001275
1276void fixup_irqs(void)
1277{
David Daney0c326382011-03-25 12:38:51 -07001278 irq_cpu_offline();
Ralf Baechle773cb772009-06-23 10:36:38 +01001279}
1280
1281#endif /* CONFIG_HOTPLUG_CPU */