]>
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> | |
88fd8589 | 19 | #include <asm/octeon/cvmx-ciu2-defs.h> |
5b3b1688 | 20 | |
0c326387 DD |
21 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu0_en_mirror); |
22 | static DEFINE_PER_CPU(unsigned long, octeon_irq_ciu1_en_mirror); | |
1a7e68f2 | 23 | static DEFINE_PER_CPU(raw_spinlock_t, octeon_irq_ciu_spinlock); |
0c326387 DD |
24 | |
25 | static __read_mostly u8 octeon_irq_ciu_to_irq[8][64]; | |
26 | ||
27 | union octeon_ciu_chip_data { | |
28 | void *p; | |
29 | unsigned long l; | |
30 | struct { | |
88fd8589 DD |
31 | unsigned long line:6; |
32 | unsigned long bit:6; | |
33 | unsigned long gpio_line:6; | |
0c326387 DD |
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 | ||
88fd8589 | 48 | static void octeon_irq_set_ciu_mapping(int irq, int line, int bit, int gpio_line, |
a0c16582 DD |
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; | |
88fd8589 | 59 | cd.s.gpio_line = gpio_line; |
0c326387 DD |
60 | |
61 | irq_set_chip_data(irq, cd.p); | |
62 | octeon_irq_ciu_to_irq[line][bit] = irq; | |
63 | } | |
64 | ||
87161ccd DD |
65 | static void octeon_irq_force_ciu_mapping(struct irq_domain *domain, |
66 | int irq, int line, int bit) | |
67 | { | |
68 | irq_domain_associate(domain, irq, line << 6 | bit); | |
69 | } | |
70 | ||
cd847b78 DD |
71 | static int octeon_coreid_for_cpu(int cpu) |
72 | { | |
73 | #ifdef CONFIG_SMP | |
74 | return cpu_logical_map(cpu); | |
75 | #else | |
76 | return cvmx_get_core_num(); | |
77 | #endif | |
78 | } | |
79 | ||
0c326387 DD |
80 | static int octeon_cpu_for_coreid(int coreid) |
81 | { | |
82 | #ifdef CONFIG_SMP | |
83 | return cpu_number_map(coreid); | |
84 | #else | |
85 | return smp_processor_id(); | |
86 | #endif | |
87 | } | |
88 | ||
89 | static void octeon_irq_core_ack(struct irq_data *data) | |
5b3b1688 | 90 | { |
0c326387 DD |
91 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
92 | unsigned int bit = cd->bit; | |
93 | ||
5b3b1688 DD |
94 | /* |
95 | * We don't need to disable IRQs to make these atomic since | |
96 | * they are already disabled earlier in the low level | |
97 | * interrupt code. | |
98 | */ | |
99 | clear_c0_status(0x100 << bit); | |
100 | /* The two user interrupts must be cleared manually. */ | |
101 | if (bit < 2) | |
102 | clear_c0_cause(0x100 << bit); | |
103 | } | |
104 | ||
0c326387 | 105 | static void octeon_irq_core_eoi(struct irq_data *data) |
5b3b1688 | 106 | { |
0c326387 DD |
107 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
108 | ||
5b3b1688 DD |
109 | /* |
110 | * We don't need to disable IRQs to make these atomic since | |
111 | * they are already disabled earlier in the low level | |
112 | * interrupt code. | |
113 | */ | |
0c326387 | 114 | set_c0_status(0x100 << cd->bit); |
5b3b1688 DD |
115 | } |
116 | ||
0c326387 | 117 | static void octeon_irq_core_set_enable_local(void *arg) |
5b3b1688 | 118 | { |
0c326387 DD |
119 | struct irq_data *data = arg; |
120 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
121 | unsigned int mask = 0x100 << cd->bit; | |
5b3b1688 DD |
122 | |
123 | /* | |
0c326387 | 124 | * Interrupts are already disabled, so these are atomic. |
5b3b1688 | 125 | */ |
0c326387 DD |
126 | if (cd->desired_en) |
127 | set_c0_status(mask); | |
128 | else | |
129 | clear_c0_status(mask); | |
130 | ||
5b3b1688 DD |
131 | } |
132 | ||
0c326387 | 133 | static void octeon_irq_core_disable(struct irq_data *data) |
5b3b1688 | 134 | { |
0c326387 DD |
135 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
136 | cd->desired_en = false; | |
5b3b1688 DD |
137 | } |
138 | ||
0c326387 | 139 | static void octeon_irq_core_enable(struct irq_data *data) |
5b3b1688 | 140 | { |
0c326387 DD |
141 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
142 | cd->desired_en = true; | |
5b3b1688 DD |
143 | } |
144 | ||
0c326387 DD |
145 | static void octeon_irq_core_bus_lock(struct irq_data *data) |
146 | { | |
147 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); | |
5b3b1688 | 148 | |
0c326387 DD |
149 | mutex_lock(&cd->core_irq_mutex); |
150 | } | |
5b3b1688 | 151 | |
0c326387 | 152 | static void octeon_irq_core_bus_sync_unlock(struct irq_data *data) |
5b3b1688 | 153 | { |
0c326387 DD |
154 | struct octeon_core_chip_data *cd = irq_data_get_irq_chip_data(data); |
155 | ||
156 | if (cd->desired_en != cd->current_en) { | |
157 | on_each_cpu(octeon_irq_core_set_enable_local, data, 1); | |
158 | ||
159 | cd->current_en = cd->desired_en; | |
5aae1fd4 DD |
160 | } |
161 | ||
0c326387 | 162 | mutex_unlock(&cd->core_irq_mutex); |
5b3b1688 DD |
163 | } |
164 | ||
0c326387 DD |
165 | static struct irq_chip octeon_irq_chip_core = { |
166 | .name = "Core", | |
167 | .irq_enable = octeon_irq_core_enable, | |
168 | .irq_disable = octeon_irq_core_disable, | |
169 | .irq_ack = octeon_irq_core_ack, | |
170 | .irq_eoi = octeon_irq_core_eoi, | |
171 | .irq_bus_lock = octeon_irq_core_bus_lock, | |
172 | .irq_bus_sync_unlock = octeon_irq_core_bus_sync_unlock, | |
173 | ||
5b7cd6fd TG |
174 | .irq_cpu_online = octeon_irq_core_eoi, |
175 | .irq_cpu_offline = octeon_irq_core_ack, | |
176 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
177 | }; |
178 | ||
179 | static void __init octeon_irq_init_core(void) | |
180 | { | |
181 | int i; | |
182 | int irq; | |
183 | struct octeon_core_chip_data *cd; | |
184 | ||
185 | for (i = 0; i < MIPS_CORE_IRQ_LINES; i++) { | |
186 | cd = &octeon_irq_core_chip_data[i]; | |
187 | cd->current_en = false; | |
188 | cd->desired_en = false; | |
189 | cd->bit = i; | |
190 | mutex_init(&cd->core_irq_mutex); | |
191 | ||
192 | irq = OCTEON_IRQ_SW0 + i; | |
87161ccd DD |
193 | irq_set_chip_data(irq, cd); |
194 | irq_set_chip_and_handler(irq, &octeon_irq_chip_core, | |
195 | handle_percpu_irq); | |
0c326387 DD |
196 | } |
197 | } | |
198 | ||
199 | static int next_cpu_for_irq(struct irq_data *data) | |
5aae1fd4 DD |
200 | { |
201 | ||
202 | #ifdef CONFIG_SMP | |
0c326387 DD |
203 | int cpu; |
204 | int weight = cpumask_weight(data->affinity); | |
5aae1fd4 DD |
205 | |
206 | if (weight > 1) { | |
0c326387 | 207 | cpu = smp_processor_id(); |
5aae1fd4 | 208 | for (;;) { |
0c326387 | 209 | cpu = cpumask_next(cpu, data->affinity); |
5aae1fd4 DD |
210 | if (cpu >= nr_cpu_ids) { |
211 | cpu = -1; | |
212 | continue; | |
213 | } else if (cpumask_test_cpu(cpu, cpu_online_mask)) { | |
214 | break; | |
215 | } | |
216 | } | |
5aae1fd4 | 217 | } else if (weight == 1) { |
0c326387 | 218 | cpu = cpumask_first(data->affinity); |
5aae1fd4 | 219 | } else { |
0c326387 | 220 | cpu = smp_processor_id(); |
5aae1fd4 | 221 | } |
0c326387 | 222 | return cpu; |
5aae1fd4 | 223 | #else |
0c326387 | 224 | return smp_processor_id(); |
5aae1fd4 DD |
225 | #endif |
226 | } | |
227 | ||
0c326387 | 228 | static void octeon_irq_ciu_enable(struct irq_data *data) |
5aae1fd4 | 229 | { |
0c326387 DD |
230 | int cpu = next_cpu_for_irq(data); |
231 | int coreid = octeon_coreid_for_cpu(cpu); | |
232 | unsigned long *pen; | |
5aae1fd4 | 233 | unsigned long flags; |
0c326387 | 234 | union octeon_ciu_chip_data cd; |
1a7e68f2 | 235 | raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu); |
0c326387 DD |
236 | |
237 | cd.p = irq_data_get_irq_chip_data(data); | |
5aae1fd4 | 238 | |
1a7e68f2 | 239 | raw_spin_lock_irqsave(lock, flags); |
0c326387 | 240 | if (cd.s.line == 0) { |
0c326387 | 241 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); |
1a7e68f2 DD |
242 | __set_bit(cd.s.bit, pen); |
243 | /* | |
244 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
245 | * enabling the irq. | |
246 | */ | |
247 | wmb(); | |
0c326387 | 248 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); |
0c326387 | 249 | } else { |
0c326387 | 250 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
1a7e68f2 DD |
251 | __set_bit(cd.s.bit, pen); |
252 | /* | |
253 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
254 | * enabling the irq. | |
255 | */ | |
256 | wmb(); | |
0c326387 | 257 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); |
0c326387 | 258 | } |
1a7e68f2 | 259 | raw_spin_unlock_irqrestore(lock, flags); |
5aae1fd4 DD |
260 | } |
261 | ||
0c326387 DD |
262 | static void octeon_irq_ciu_enable_local(struct irq_data *data) |
263 | { | |
264 | unsigned long *pen; | |
265 | unsigned long flags; | |
266 | union octeon_ciu_chip_data cd; | |
1a7e68f2 | 267 | raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock); |
0c326387 DD |
268 | |
269 | cd.p = irq_data_get_irq_chip_data(data); | |
270 | ||
1a7e68f2 | 271 | raw_spin_lock_irqsave(lock, flags); |
0c326387 | 272 | if (cd.s.line == 0) { |
0c326387 | 273 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); |
1a7e68f2 DD |
274 | __set_bit(cd.s.bit, pen); |
275 | /* | |
276 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
277 | * enabling the irq. | |
278 | */ | |
279 | wmb(); | |
0c326387 | 280 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); |
0c326387 | 281 | } else { |
0c326387 | 282 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); |
1a7e68f2 DD |
283 | __set_bit(cd.s.bit, pen); |
284 | /* | |
285 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
286 | * enabling the irq. | |
287 | */ | |
288 | wmb(); | |
0c326387 | 289 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); |
0c326387 | 290 | } |
1a7e68f2 | 291 | raw_spin_unlock_irqrestore(lock, flags); |
0c326387 DD |
292 | } |
293 | ||
294 | static void octeon_irq_ciu_disable_local(struct irq_data *data) | |
295 | { | |
296 | unsigned long *pen; | |
297 | unsigned long flags; | |
298 | union octeon_ciu_chip_data cd; | |
1a7e68f2 | 299 | raw_spinlock_t *lock = &__get_cpu_var(octeon_irq_ciu_spinlock); |
0c326387 DD |
300 | |
301 | cd.p = irq_data_get_irq_chip_data(data); | |
302 | ||
1a7e68f2 | 303 | raw_spin_lock_irqsave(lock, flags); |
0c326387 | 304 | if (cd.s.line == 0) { |
0c326387 | 305 | pen = &__get_cpu_var(octeon_irq_ciu0_en_mirror); |
1a7e68f2 DD |
306 | __clear_bit(cd.s.bit, pen); |
307 | /* | |
308 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
309 | * enabling the irq. | |
310 | */ | |
311 | wmb(); | |
0c326387 | 312 | cvmx_write_csr(CVMX_CIU_INTX_EN0(cvmx_get_core_num() * 2), *pen); |
0c326387 | 313 | } else { |
0c326387 | 314 | pen = &__get_cpu_var(octeon_irq_ciu1_en_mirror); |
1a7e68f2 DD |
315 | __clear_bit(cd.s.bit, pen); |
316 | /* | |
317 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
318 | * enabling the irq. | |
319 | */ | |
320 | wmb(); | |
0c326387 | 321 | cvmx_write_csr(CVMX_CIU_INTX_EN1(cvmx_get_core_num() * 2 + 1), *pen); |
0c326387 | 322 | } |
1a7e68f2 | 323 | raw_spin_unlock_irqrestore(lock, flags); |
0c326387 DD |
324 | } |
325 | ||
326 | static void octeon_irq_ciu_disable_all(struct irq_data *data) | |
5b3b1688 | 327 | { |
5b3b1688 | 328 | unsigned long flags; |
0c326387 DD |
329 | unsigned long *pen; |
330 | int cpu; | |
331 | union octeon_ciu_chip_data cd; | |
1a7e68f2 | 332 | raw_spinlock_t *lock; |
5b3b1688 | 333 | |
0c326387 DD |
334 | cd.p = irq_data_get_irq_chip_data(data); |
335 | ||
1a7e68f2 DD |
336 | for_each_online_cpu(cpu) { |
337 | int coreid = octeon_coreid_for_cpu(cpu); | |
338 | lock = &per_cpu(octeon_irq_ciu_spinlock, cpu); | |
339 | if (cd.s.line == 0) | |
0c326387 | 340 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); |
1a7e68f2 | 341 | else |
0c326387 | 342 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
1a7e68f2 DD |
343 | |
344 | raw_spin_lock_irqsave(lock, flags); | |
345 | __clear_bit(cd.s.bit, pen); | |
346 | /* | |
347 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
348 | * enabling the irq. | |
349 | */ | |
350 | wmb(); | |
351 | if (cd.s.line == 0) | |
352 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
353 | else | |
0c326387 | 354 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); |
1a7e68f2 | 355 | raw_spin_unlock_irqrestore(lock, flags); |
0c326387 | 356 | } |
5b3b1688 DD |
357 | } |
358 | ||
0c326387 | 359 | static void octeon_irq_ciu_enable_all(struct irq_data *data) |
5b3b1688 | 360 | { |
5b3b1688 | 361 | unsigned long flags; |
0c326387 | 362 | unsigned long *pen; |
5b3b1688 | 363 | int cpu; |
0c326387 | 364 | union octeon_ciu_chip_data cd; |
1a7e68f2 | 365 | raw_spinlock_t *lock; |
0c326387 DD |
366 | |
367 | cd.p = irq_data_get_irq_chip_data(data); | |
368 | ||
1a7e68f2 DD |
369 | for_each_online_cpu(cpu) { |
370 | int coreid = octeon_coreid_for_cpu(cpu); | |
371 | lock = &per_cpu(octeon_irq_ciu_spinlock, cpu); | |
372 | if (cd.s.line == 0) | |
0c326387 | 373 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); |
1a7e68f2 | 374 | else |
0c326387 | 375 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
1a7e68f2 DD |
376 | |
377 | raw_spin_lock_irqsave(lock, flags); | |
378 | __set_bit(cd.s.bit, pen); | |
379 | /* | |
380 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
381 | * enabling the irq. | |
382 | */ | |
383 | wmb(); | |
384 | if (cd.s.line == 0) | |
385 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
386 | else | |
0c326387 | 387 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); |
1a7e68f2 | 388 | raw_spin_unlock_irqrestore(lock, flags); |
5b3b1688 | 389 | } |
cd847b78 DD |
390 | } |
391 | ||
392 | /* | |
5aae1fd4 DD |
393 | * Enable the irq on the next core in the affinity set for chips that |
394 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 395 | */ |
0c326387 | 396 | static void octeon_irq_ciu_enable_v2(struct irq_data *data) |
cd847b78 | 397 | { |
0c326387 DD |
398 | u64 mask; |
399 | int cpu = next_cpu_for_irq(data); | |
400 | union octeon_ciu_chip_data cd; | |
cd847b78 | 401 | |
0c326387 DD |
402 | cd.p = irq_data_get_irq_chip_data(data); |
403 | mask = 1ull << (cd.s.bit); | |
404 | ||
405 | /* | |
406 | * Called under the desc lock, so these should never get out | |
407 | * of sync. | |
408 | */ | |
409 | if (cd.s.line == 0) { | |
410 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
411 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
5aae1fd4 | 412 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); |
0c326387 DD |
413 | } else { |
414 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
415 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
416 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
5aae1fd4 | 417 | } |
cd847b78 DD |
418 | } |
419 | ||
420 | /* | |
5aae1fd4 DD |
421 | * Enable the irq on the current CPU for chips that |
422 | * have the EN*_W1{S,C} registers. | |
cd847b78 | 423 | */ |
0c326387 | 424 | static void octeon_irq_ciu_enable_local_v2(struct irq_data *data) |
cd847b78 | 425 | { |
0c326387 DD |
426 | u64 mask; |
427 | union octeon_ciu_chip_data cd; | |
428 | ||
429 | cd.p = irq_data_get_irq_chip_data(data); | |
430 | mask = 1ull << (cd.s.bit); | |
cd847b78 | 431 | |
0c326387 DD |
432 | if (cd.s.line == 0) { |
433 | int index = cvmx_get_core_num() * 2; | |
434 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
435 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
436 | } else { | |
437 | int index = cvmx_get_core_num() * 2 + 1; | |
438 | set_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
439 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
440 | } | |
441 | } | |
442 | ||
443 | static void octeon_irq_ciu_disable_local_v2(struct irq_data *data) | |
444 | { | |
445 | u64 mask; | |
446 | union octeon_ciu_chip_data cd; | |
447 | ||
448 | cd.p = irq_data_get_irq_chip_data(data); | |
449 | mask = 1ull << (cd.s.bit); | |
450 | ||
451 | if (cd.s.line == 0) { | |
452 | int index = cvmx_get_core_num() * 2; | |
453 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu0_en_mirror)); | |
454 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
455 | } else { | |
456 | int index = cvmx_get_core_num() * 2 + 1; | |
457 | clear_bit(cd.s.bit, &__get_cpu_var(octeon_irq_ciu1_en_mirror)); | |
458 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
459 | } | |
cd847b78 DD |
460 | } |
461 | ||
86568dc4 | 462 | /* |
0c326387 | 463 | * Write to the W1C bit in CVMX_CIU_INTX_SUM0 to clear the irq. |
86568dc4 | 464 | */ |
0c326387 DD |
465 | static void octeon_irq_ciu_ack(struct irq_data *data) |
466 | { | |
467 | u64 mask; | |
468 | union octeon_ciu_chip_data cd; | |
469 | ||
88fd8589 | 470 | cd.p = irq_data_get_irq_chip_data(data); |
0c326387 DD |
471 | mask = 1ull << (cd.s.bit); |
472 | ||
473 | if (cd.s.line == 0) { | |
474 | int index = cvmx_get_core_num() * 2; | |
5aae1fd4 | 475 | cvmx_write_csr(CVMX_CIU_INTX_SUM0(index), mask); |
0c326387 DD |
476 | } else { |
477 | cvmx_write_csr(CVMX_CIU_INT_SUM1, mask); | |
5aae1fd4 | 478 | } |
86568dc4 DD |
479 | } |
480 | ||
dbb103b2 | 481 | /* |
0c326387 | 482 | * Disable the irq on the all cores for chips that have the EN*_W1{S,C} |
dbb103b2 DD |
483 | * registers. |
484 | */ | |
0c326387 | 485 | static void octeon_irq_ciu_disable_all_v2(struct irq_data *data) |
dbb103b2 | 486 | { |
0c326387 DD |
487 | int cpu; |
488 | u64 mask; | |
489 | union octeon_ciu_chip_data cd; | |
dbb103b2 | 490 | |
88fd8589 | 491 | cd.p = irq_data_get_irq_chip_data(data); |
0c326387 DD |
492 | mask = 1ull << (cd.s.bit); |
493 | ||
494 | if (cd.s.line == 0) { | |
495 | for_each_online_cpu(cpu) { | |
496 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
497 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
498 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
499 | } | |
500 | } else { | |
501 | for_each_online_cpu(cpu) { | |
502 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
503 | clear_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
504 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
505 | } | |
506 | } | |
dbb103b2 DD |
507 | } |
508 | ||
cd847b78 | 509 | /* |
0c326387 | 510 | * Enable the irq on the all cores for chips that have the EN*_W1{S,C} |
cd847b78 DD |
511 | * registers. |
512 | */ | |
0c326387 | 513 | static void octeon_irq_ciu_enable_all_v2(struct irq_data *data) |
cd847b78 | 514 | { |
cd847b78 | 515 | int cpu; |
0c326387 DD |
516 | u64 mask; |
517 | union octeon_ciu_chip_data cd; | |
518 | ||
88fd8589 | 519 | cd.p = irq_data_get_irq_chip_data(data); |
0c326387 DD |
520 | mask = 1ull << (cd.s.bit); |
521 | ||
522 | if (cd.s.line == 0) { | |
523 | for_each_online_cpu(cpu) { | |
524 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
525 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu0_en_mirror, cpu)); | |
526 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
527 | } | |
528 | } else { | |
529 | for_each_online_cpu(cpu) { | |
530 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
531 | set_bit(cd.s.bit, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); | |
532 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
533 | } | |
cd847b78 | 534 | } |
5b3b1688 DD |
535 | } |
536 | ||
6d1ab4c2 DD |
537 | static void octeon_irq_gpio_setup(struct irq_data *data) |
538 | { | |
539 | union cvmx_gpio_bit_cfgx cfg; | |
540 | union octeon_ciu_chip_data cd; | |
541 | u32 t = irqd_get_trigger_type(data); | |
542 | ||
543 | cd.p = irq_data_get_irq_chip_data(data); | |
544 | ||
545 | cfg.u64 = 0; | |
546 | cfg.s.int_en = 1; | |
547 | cfg.s.int_type = (t & IRQ_TYPE_EDGE_BOTH) != 0; | |
548 | cfg.s.rx_xor = (t & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_EDGE_FALLING)) != 0; | |
549 | ||
550 | /* 140 nS glitch filter*/ | |
551 | cfg.s.fil_cnt = 7; | |
552 | cfg.s.fil_sel = 3; | |
553 | ||
88fd8589 | 554 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), cfg.u64); |
6d1ab4c2 DD |
555 | } |
556 | ||
557 | static void octeon_irq_ciu_enable_gpio_v2(struct irq_data *data) | |
558 | { | |
559 | octeon_irq_gpio_setup(data); | |
560 | octeon_irq_ciu_enable_v2(data); | |
561 | } | |
562 | ||
563 | static void octeon_irq_ciu_enable_gpio(struct irq_data *data) | |
564 | { | |
565 | octeon_irq_gpio_setup(data); | |
566 | octeon_irq_ciu_enable(data); | |
567 | } | |
568 | ||
569 | static int octeon_irq_ciu_gpio_set_type(struct irq_data *data, unsigned int t) | |
570 | { | |
571 | irqd_set_trigger_type(data, t); | |
572 | octeon_irq_gpio_setup(data); | |
573 | ||
574 | return IRQ_SET_MASK_OK; | |
575 | } | |
576 | ||
577 | static void octeon_irq_ciu_disable_gpio_v2(struct irq_data *data) | |
578 | { | |
579 | union octeon_ciu_chip_data cd; | |
580 | ||
581 | cd.p = irq_data_get_irq_chip_data(data); | |
88fd8589 | 582 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); |
6d1ab4c2 DD |
583 | |
584 | octeon_irq_ciu_disable_all_v2(data); | |
585 | } | |
586 | ||
587 | static void octeon_irq_ciu_disable_gpio(struct irq_data *data) | |
588 | { | |
589 | union octeon_ciu_chip_data cd; | |
590 | ||
591 | cd.p = irq_data_get_irq_chip_data(data); | |
88fd8589 | 592 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); |
6d1ab4c2 DD |
593 | |
594 | octeon_irq_ciu_disable_all(data); | |
595 | } | |
596 | ||
597 | static void octeon_irq_ciu_gpio_ack(struct irq_data *data) | |
598 | { | |
599 | union octeon_ciu_chip_data cd; | |
600 | u64 mask; | |
601 | ||
602 | cd.p = irq_data_get_irq_chip_data(data); | |
88fd8589 | 603 | mask = 1ull << (cd.s.gpio_line); |
6d1ab4c2 DD |
604 | |
605 | cvmx_write_csr(CVMX_GPIO_INT_CLR, mask); | |
606 | } | |
607 | ||
608 | static void octeon_irq_handle_gpio(unsigned int irq, struct irq_desc *desc) | |
609 | { | |
5ebf1f29 | 610 | if (irq_get_trigger_type(irq) & IRQ_TYPE_EDGE_BOTH) |
6d1ab4c2 DD |
611 | handle_edge_irq(irq, desc); |
612 | else | |
613 | handle_level_irq(irq, desc); | |
614 | } | |
615 | ||
5b3b1688 | 616 | #ifdef CONFIG_SMP |
0c326387 DD |
617 | |
618 | static void octeon_irq_cpu_offline_ciu(struct irq_data *data) | |
619 | { | |
620 | int cpu = smp_processor_id(); | |
621 | cpumask_t new_affinity; | |
622 | ||
623 | if (!cpumask_test_cpu(cpu, data->affinity)) | |
624 | return; | |
625 | ||
626 | if (cpumask_weight(data->affinity) > 1) { | |
627 | /* | |
628 | * It has multi CPU affinity, just remove this CPU | |
629 | * from the affinity set. | |
630 | */ | |
631 | cpumask_copy(&new_affinity, data->affinity); | |
632 | cpumask_clear_cpu(cpu, &new_affinity); | |
633 | } else { | |
634 | /* Otherwise, put it on lowest numbered online CPU. */ | |
635 | cpumask_clear(&new_affinity); | |
636 | cpumask_set_cpu(cpumask_first(cpu_online_mask), &new_affinity); | |
637 | } | |
638 | __irq_set_affinity_locked(data, &new_affinity); | |
639 | } | |
640 | ||
641 | static int octeon_irq_ciu_set_affinity(struct irq_data *data, | |
642 | const struct cpumask *dest, bool force) | |
5b3b1688 DD |
643 | { |
644 | int cpu; | |
5b7cd6fd | 645 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
b6b74d54 | 646 | unsigned long flags; |
0c326387 | 647 | union octeon_ciu_chip_data cd; |
1a7e68f2 DD |
648 | unsigned long *pen; |
649 | raw_spinlock_t *lock; | |
0c326387 | 650 | |
88fd8589 | 651 | cd.p = irq_data_get_irq_chip_data(data); |
5b3b1688 | 652 | |
5aae1fd4 DD |
653 | /* |
654 | * For non-v2 CIU, we will allow only single CPU affinity. | |
655 | * This removes the need to do locking in the .ack/.eoi | |
656 | * functions. | |
657 | */ | |
658 | if (cpumask_weight(dest) != 1) | |
659 | return -EINVAL; | |
660 | ||
5b7cd6fd | 661 | if (!enable_one) |
0c326387 DD |
662 | return 0; |
663 | ||
0c326387 | 664 | |
1a7e68f2 DD |
665 | for_each_online_cpu(cpu) { |
666 | int coreid = octeon_coreid_for_cpu(cpu); | |
667 | ||
668 | lock = &per_cpu(octeon_irq_ciu_spinlock, cpu); | |
669 | raw_spin_lock_irqsave(lock, flags); | |
670 | ||
671 | if (cd.s.line == 0) | |
672 | pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
673 | else | |
674 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
675 | ||
676 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
677 | enable_one = 0; | |
678 | __set_bit(cd.s.bit, pen); | |
679 | } else { | |
680 | __clear_bit(cd.s.bit, pen); | |
0c326387 | 681 | } |
1a7e68f2 DD |
682 | /* |
683 | * Must be visible to octeon_irq_ip{2,3}_ciu() before | |
684 | * enabling the irq. | |
685 | */ | |
686 | wmb(); | |
0c326387 | 687 | |
1a7e68f2 DD |
688 | if (cd.s.line == 0) |
689 | cvmx_write_csr(CVMX_CIU_INTX_EN0(coreid * 2), *pen); | |
690 | else | |
0c326387 | 691 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); |
1a7e68f2 DD |
692 | |
693 | raw_spin_unlock_irqrestore(lock, flags); | |
5b3b1688 | 694 | } |
d5dedd45 | 695 | return 0; |
5b3b1688 | 696 | } |
cd847b78 DD |
697 | |
698 | /* | |
699 | * Set affinity for the irq for chips that have the EN*_W1{S,C} | |
700 | * registers. | |
701 | */ | |
0c326387 DD |
702 | static int octeon_irq_ciu_set_affinity_v2(struct irq_data *data, |
703 | const struct cpumask *dest, | |
704 | bool force) | |
cd847b78 DD |
705 | { |
706 | int cpu; | |
5b7cd6fd | 707 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); |
0c326387 DD |
708 | u64 mask; |
709 | union octeon_ciu_chip_data cd; | |
710 | ||
5b7cd6fd | 711 | if (!enable_one) |
0c326387 DD |
712 | return 0; |
713 | ||
88fd8589 | 714 | cd.p = irq_data_get_irq_chip_data(data); |
0c326387 DD |
715 | mask = 1ull << cd.s.bit; |
716 | ||
717 | if (cd.s.line == 0) { | |
718 | for_each_online_cpu(cpu) { | |
719 | unsigned long *pen = &per_cpu(octeon_irq_ciu0_en_mirror, cpu); | |
720 | int index = octeon_coreid_for_cpu(cpu) * 2; | |
721 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 722 | enable_one = false; |
0c326387 DD |
723 | set_bit(cd.s.bit, pen); |
724 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1S(index), mask); | |
725 | } else { | |
726 | clear_bit(cd.s.bit, pen); | |
727 | cvmx_write_csr(CVMX_CIU_INTX_EN0_W1C(index), mask); | |
728 | } | |
729 | } | |
730 | } else { | |
731 | for_each_online_cpu(cpu) { | |
732 | unsigned long *pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); | |
733 | int index = octeon_coreid_for_cpu(cpu) * 2 + 1; | |
734 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
5b7cd6fd | 735 | enable_one = false; |
0c326387 DD |
736 | set_bit(cd.s.bit, pen); |
737 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(index), mask); | |
738 | } else { | |
739 | clear_bit(cd.s.bit, pen); | |
740 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1C(index), mask); | |
741 | } | |
5aae1fd4 | 742 | } |
cd847b78 DD |
743 | } |
744 | return 0; | |
745 | } | |
5b3b1688 DD |
746 | #endif |
747 | ||
cd847b78 DD |
748 | /* |
749 | * Newer octeon chips have support for lockless CIU operation. | |
750 | */ | |
0c326387 DD |
751 | static struct irq_chip octeon_irq_chip_ciu_v2 = { |
752 | .name = "CIU", | |
753 | .irq_enable = octeon_irq_ciu_enable_v2, | |
754 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
0c326387 DD |
755 | .irq_ack = octeon_irq_ciu_ack, |
756 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
757 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
5b3b1688 | 758 | #ifdef CONFIG_SMP |
0c326387 DD |
759 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, |
760 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
5b3b1688 DD |
761 | #endif |
762 | }; | |
763 | ||
0c326387 DD |
764 | static struct irq_chip octeon_irq_chip_ciu = { |
765 | .name = "CIU", | |
766 | .irq_enable = octeon_irq_ciu_enable, | |
767 | .irq_disable = octeon_irq_ciu_disable_all, | |
0c326387 | 768 | .irq_ack = octeon_irq_ciu_ack, |
1a7e68f2 DD |
769 | .irq_mask = octeon_irq_ciu_disable_local, |
770 | .irq_unmask = octeon_irq_ciu_enable, | |
0c326387 DD |
771 | #ifdef CONFIG_SMP |
772 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
773 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
774 | #endif | |
86568dc4 DD |
775 | }; |
776 | ||
0c326387 DD |
777 | /* The mbox versions don't do any affinity or round-robin. */ |
778 | static struct irq_chip octeon_irq_chip_ciu_mbox_v2 = { | |
779 | .name = "CIU-M", | |
780 | .irq_enable = octeon_irq_ciu_enable_all_v2, | |
781 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
782 | .irq_ack = octeon_irq_ciu_disable_local_v2, | |
783 | .irq_eoi = octeon_irq_ciu_enable_local_v2, | |
784 | ||
5b7cd6fd TG |
785 | .irq_cpu_online = octeon_irq_ciu_enable_local_v2, |
786 | .irq_cpu_offline = octeon_irq_ciu_disable_local_v2, | |
787 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 | 788 | }; |
5b3b1688 | 789 | |
0c326387 DD |
790 | static struct irq_chip octeon_irq_chip_ciu_mbox = { |
791 | .name = "CIU-M", | |
792 | .irq_enable = octeon_irq_ciu_enable_all, | |
793 | .irq_disable = octeon_irq_ciu_disable_all, | |
1a7e68f2 DD |
794 | .irq_ack = octeon_irq_ciu_disable_local, |
795 | .irq_eoi = octeon_irq_ciu_enable_local, | |
5b3b1688 | 796 | |
5b7cd6fd TG |
797 | .irq_cpu_online = octeon_irq_ciu_enable_local, |
798 | .irq_cpu_offline = octeon_irq_ciu_disable_local, | |
799 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
0c326387 DD |
800 | }; |
801 | ||
6d1ab4c2 DD |
802 | static struct irq_chip octeon_irq_chip_ciu_gpio_v2 = { |
803 | .name = "CIU-GPIO", | |
804 | .irq_enable = octeon_irq_ciu_enable_gpio_v2, | |
805 | .irq_disable = octeon_irq_ciu_disable_gpio_v2, | |
806 | .irq_ack = octeon_irq_ciu_gpio_ack, | |
807 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
808 | .irq_unmask = octeon_irq_ciu_enable_v2, | |
809 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | |
810 | #ifdef CONFIG_SMP | |
811 | .irq_set_affinity = octeon_irq_ciu_set_affinity_v2, | |
812 | #endif | |
813 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
814 | }; | |
815 | ||
816 | static struct irq_chip octeon_irq_chip_ciu_gpio = { | |
817 | .name = "CIU-GPIO", | |
818 | .irq_enable = octeon_irq_ciu_enable_gpio, | |
819 | .irq_disable = octeon_irq_ciu_disable_gpio, | |
1a7e68f2 DD |
820 | .irq_mask = octeon_irq_ciu_disable_local, |
821 | .irq_unmask = octeon_irq_ciu_enable, | |
6d1ab4c2 DD |
822 | .irq_ack = octeon_irq_ciu_gpio_ack, |
823 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | |
824 | #ifdef CONFIG_SMP | |
825 | .irq_set_affinity = octeon_irq_ciu_set_affinity, | |
826 | #endif | |
827 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
828 | }; | |
829 | ||
0c326387 DD |
830 | /* |
831 | * Watchdog interrupts are special. They are associated with a single | |
832 | * core, so we hardwire the affinity to that core. | |
833 | */ | |
834 | static void octeon_irq_ciu_wd_enable(struct irq_data *data) | |
5b3b1688 | 835 | { |
5b3b1688 | 836 | unsigned long flags; |
0c326387 DD |
837 | unsigned long *pen; |
838 | int coreid = data->irq - OCTEON_IRQ_WDOG0; /* Bit 0-63 of EN1 */ | |
839 | int cpu = octeon_cpu_for_coreid(coreid); | |
1a7e68f2 | 840 | raw_spinlock_t *lock = &per_cpu(octeon_irq_ciu_spinlock, cpu); |
5b3b1688 | 841 | |
1a7e68f2 | 842 | raw_spin_lock_irqsave(lock, flags); |
0c326387 | 843 | pen = &per_cpu(octeon_irq_ciu1_en_mirror, cpu); |
1a7e68f2 DD |
844 | __set_bit(coreid, pen); |
845 | /* | |
846 | * Must be visible to octeon_irq_ip{2,3}_ciu() before enabling | |
847 | * the irq. | |
848 | */ | |
849 | wmb(); | |
0c326387 | 850 | cvmx_write_csr(CVMX_CIU_INTX_EN1(coreid * 2 + 1), *pen); |
1a7e68f2 | 851 | raw_spin_unlock_irqrestore(lock, flags); |
5b3b1688 DD |
852 | } |
853 | ||
5aae1fd4 DD |
854 | /* |
855 | * Watchdog interrupts are special. They are associated with a single | |
856 | * core, so we hardwire the affinity to that core. | |
857 | */ | |
0c326387 | 858 | static void octeon_irq_ciu1_wd_enable_v2(struct irq_data *data) |
5aae1fd4 | 859 | { |
0c326387 DD |
860 | int coreid = data->irq - OCTEON_IRQ_WDOG0; |
861 | int cpu = octeon_cpu_for_coreid(coreid); | |
5aae1fd4 | 862 | |
0c326387 DD |
863 | set_bit(coreid, &per_cpu(octeon_irq_ciu1_en_mirror, cpu)); |
864 | cvmx_write_csr(CVMX_CIU_INTX_EN1_W1S(coreid * 2 + 1), 1ull << coreid); | |
5aae1fd4 DD |
865 | } |
866 | ||
0c326387 DD |
867 | |
868 | static struct irq_chip octeon_irq_chip_ciu_wd_v2 = { | |
869 | .name = "CIU-W", | |
870 | .irq_enable = octeon_irq_ciu1_wd_enable_v2, | |
871 | .irq_disable = octeon_irq_ciu_disable_all_v2, | |
872 | .irq_mask = octeon_irq_ciu_disable_local_v2, | |
873 | .irq_unmask = octeon_irq_ciu_enable_local_v2, | |
874 | }; | |
875 | ||
876 | static struct irq_chip octeon_irq_chip_ciu_wd = { | |
877 | .name = "CIU-W", | |
878 | .irq_enable = octeon_irq_ciu_wd_enable, | |
879 | .irq_disable = octeon_irq_ciu_disable_all, | |
1a7e68f2 DD |
880 | .irq_mask = octeon_irq_ciu_disable_local, |
881 | .irq_unmask = octeon_irq_ciu_enable_local, | |
0c326387 DD |
882 | }; |
883 | ||
a0c16582 DD |
884 | static bool octeon_irq_ciu_is_edge(unsigned int line, unsigned int bit) |
885 | { | |
886 | bool edge = false; | |
887 | ||
888 | if (line == 0) | |
889 | switch (bit) { | |
890 | case 48 ... 49: /* GMX DRP */ | |
891 | case 50: /* IPD_DRP */ | |
892 | case 52 ... 55: /* Timers */ | |
893 | case 58: /* MPI */ | |
894 | edge = true; | |
895 | break; | |
896 | default: | |
897 | break; | |
898 | } | |
899 | else /* line == 1 */ | |
900 | switch (bit) { | |
901 | case 47: /* PTP */ | |
902 | edge = true; | |
903 | break; | |
904 | default: | |
905 | break; | |
906 | } | |
907 | return edge; | |
908 | } | |
909 | ||
910 | struct octeon_irq_gpio_domain_data { | |
911 | unsigned int base_hwirq; | |
912 | }; | |
913 | ||
914 | static int octeon_irq_gpio_xlat(struct irq_domain *d, | |
915 | struct device_node *node, | |
916 | const u32 *intspec, | |
917 | unsigned int intsize, | |
918 | unsigned long *out_hwirq, | |
919 | unsigned int *out_type) | |
920 | { | |
921 | unsigned int type; | |
922 | unsigned int pin; | |
923 | unsigned int trigger; | |
a0c16582 DD |
924 | |
925 | if (d->of_node != node) | |
926 | return -EINVAL; | |
927 | ||
928 | if (intsize < 2) | |
929 | return -EINVAL; | |
930 | ||
931 | pin = intspec[0]; | |
932 | if (pin >= 16) | |
933 | return -EINVAL; | |
934 | ||
935 | trigger = intspec[1]; | |
936 | ||
937 | switch (trigger) { | |
938 | case 1: | |
939 | type = IRQ_TYPE_EDGE_RISING; | |
940 | break; | |
941 | case 2: | |
942 | type = IRQ_TYPE_EDGE_FALLING; | |
943 | break; | |
944 | case 4: | |
945 | type = IRQ_TYPE_LEVEL_HIGH; | |
946 | break; | |
947 | case 8: | |
948 | type = IRQ_TYPE_LEVEL_LOW; | |
949 | break; | |
950 | default: | |
951 | pr_err("Error: (%s) Invalid irq trigger specification: %x\n", | |
952 | node->name, | |
953 | trigger); | |
954 | type = IRQ_TYPE_LEVEL_LOW; | |
955 | break; | |
956 | } | |
957 | *out_type = type; | |
87161ccd | 958 | *out_hwirq = pin; |
a0c16582 DD |
959 | |
960 | return 0; | |
961 | } | |
962 | ||
963 | static int octeon_irq_ciu_xlat(struct irq_domain *d, | |
964 | struct device_node *node, | |
965 | const u32 *intspec, | |
966 | unsigned int intsize, | |
967 | unsigned long *out_hwirq, | |
968 | unsigned int *out_type) | |
969 | { | |
970 | unsigned int ciu, bit; | |
971 | ||
972 | ciu = intspec[0]; | |
973 | bit = intspec[1]; | |
974 | ||
975 | if (ciu > 1 || bit > 63) | |
976 | return -EINVAL; | |
977 | ||
978 | /* These are the GPIO lines */ | |
979 | if (ciu == 0 && bit >= 16 && bit < 32) | |
980 | return -EINVAL; | |
981 | ||
982 | *out_hwirq = (ciu << 6) | bit; | |
983 | *out_type = 0; | |
984 | ||
985 | return 0; | |
986 | } | |
987 | ||
988 | static struct irq_chip *octeon_irq_ciu_chip; | |
989 | static struct irq_chip *octeon_irq_gpio_chip; | |
990 | ||
991 | static bool octeon_irq_virq_in_range(unsigned int virq) | |
992 | { | |
993 | /* We cannot let it overflow the mapping array. */ | |
994 | if (virq < (1ul << 8 * sizeof(octeon_irq_ciu_to_irq[0][0]))) | |
995 | return true; | |
996 | ||
997 | WARN_ONCE(true, "virq out of range %u.\n", virq); | |
998 | return false; | |
999 | } | |
1000 | ||
1001 | static int octeon_irq_ciu_map(struct irq_domain *d, | |
1002 | unsigned int virq, irq_hw_number_t hw) | |
1003 | { | |
1004 | unsigned int line = hw >> 6; | |
1005 | unsigned int bit = hw & 63; | |
1006 | ||
1007 | if (!octeon_irq_virq_in_range(virq)) | |
1008 | return -EINVAL; | |
1009 | ||
1010 | if (line > 1 || octeon_irq_ciu_to_irq[line][bit] != 0) | |
1011 | return -EINVAL; | |
1012 | ||
1013 | if (octeon_irq_ciu_is_edge(line, bit)) | |
88fd8589 | 1014 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, |
a0c16582 DD |
1015 | octeon_irq_ciu_chip, |
1016 | handle_edge_irq); | |
1017 | else | |
88fd8589 | 1018 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, |
a0c16582 DD |
1019 | octeon_irq_ciu_chip, |
1020 | handle_level_irq); | |
1021 | ||
1022 | return 0; | |
1023 | } | |
1024 | ||
88fd8589 DD |
1025 | static int octeon_irq_gpio_map_common(struct irq_domain *d, |
1026 | unsigned int virq, irq_hw_number_t hw, | |
1027 | int line_limit, struct irq_chip *chip) | |
a0c16582 | 1028 | { |
87161ccd DD |
1029 | struct octeon_irq_gpio_domain_data *gpiod = d->host_data; |
1030 | unsigned int line, bit; | |
a0c16582 DD |
1031 | |
1032 | if (!octeon_irq_virq_in_range(virq)) | |
1033 | return -EINVAL; | |
1034 | ||
d41d547a AS |
1035 | line = (hw + gpiod->base_hwirq) >> 6; |
1036 | bit = (hw + gpiod->base_hwirq) & 63; | |
88fd8589 | 1037 | if (line > line_limit || octeon_irq_ciu_to_irq[line][bit] != 0) |
a0c16582 DD |
1038 | return -EINVAL; |
1039 | ||
88fd8589 DD |
1040 | octeon_irq_set_ciu_mapping(virq, line, bit, hw, |
1041 | chip, octeon_irq_handle_gpio); | |
a0c16582 DD |
1042 | return 0; |
1043 | } | |
1044 | ||
88fd8589 DD |
1045 | static int octeon_irq_gpio_map(struct irq_domain *d, |
1046 | unsigned int virq, irq_hw_number_t hw) | |
1047 | { | |
1048 | return octeon_irq_gpio_map_common(d, virq, hw, 1, octeon_irq_gpio_chip); | |
1049 | } | |
1050 | ||
a0c16582 DD |
1051 | static struct irq_domain_ops octeon_irq_domain_ciu_ops = { |
1052 | .map = octeon_irq_ciu_map, | |
1053 | .xlate = octeon_irq_ciu_xlat, | |
1054 | }; | |
1055 | ||
1056 | static struct irq_domain_ops octeon_irq_domain_gpio_ops = { | |
1057 | .map = octeon_irq_gpio_map, | |
1058 | .xlate = octeon_irq_gpio_xlat, | |
1059 | }; | |
1060 | ||
1a7e68f2 | 1061 | static void octeon_irq_ip2_ciu(void) |
cd847b78 | 1062 | { |
0c326387 DD |
1063 | const unsigned long core_id = cvmx_get_core_num(); |
1064 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INTX_SUM0(core_id * 2)); | |
1065 | ||
1066 | ciu_sum &= __get_cpu_var(octeon_irq_ciu0_en_mirror); | |
1067 | if (likely(ciu_sum)) { | |
1068 | int bit = fls64(ciu_sum) - 1; | |
1069 | int irq = octeon_irq_ciu_to_irq[0][bit]; | |
1070 | if (likely(irq)) | |
1071 | do_IRQ(irq); | |
1072 | else | |
1073 | spurious_interrupt(); | |
1074 | } else { | |
1075 | spurious_interrupt(); | |
5aae1fd4 | 1076 | } |
cd847b78 | 1077 | } |
cd847b78 | 1078 | |
1a7e68f2 | 1079 | static void octeon_irq_ip3_ciu(void) |
dbb103b2 | 1080 | { |
0c326387 DD |
1081 | u64 ciu_sum = cvmx_read_csr(CVMX_CIU_INT_SUM1); |
1082 | ||
1083 | ciu_sum &= __get_cpu_var(octeon_irq_ciu1_en_mirror); | |
1084 | if (likely(ciu_sum)) { | |
1085 | int bit = fls64(ciu_sum) - 1; | |
1086 | int irq = octeon_irq_ciu_to_irq[1][bit]; | |
1087 | if (likely(irq)) | |
1088 | do_IRQ(irq); | |
1089 | else | |
1090 | spurious_interrupt(); | |
1091 | } else { | |
1092 | spurious_interrupt(); | |
1093 | } | |
dbb103b2 DD |
1094 | } |
1095 | ||
88fd8589 DD |
1096 | static bool octeon_irq_use_ip4; |
1097 | ||
1098 | static void __cpuinit octeon_irq_local_enable_ip4(void *arg) | |
1099 | { | |
1100 | set_c0_status(STATUSF_IP4); | |
1101 | } | |
1102 | ||
0c326387 | 1103 | static void octeon_irq_ip4_mask(void) |
cd847b78 | 1104 | { |
0c326387 DD |
1105 | clear_c0_status(STATUSF_IP4); |
1106 | spurious_interrupt(); | |
5b3b1688 DD |
1107 | } |
1108 | ||
0c326387 DD |
1109 | static void (*octeon_irq_ip2)(void); |
1110 | static void (*octeon_irq_ip3)(void); | |
1111 | static void (*octeon_irq_ip4)(void); | |
5b3b1688 | 1112 | |
0c326387 | 1113 | void __cpuinitdata (*octeon_irq_setup_secondary)(void); |
5aae1fd4 | 1114 | |
88fd8589 DD |
1115 | void __cpuinit octeon_irq_set_ip4_handler(octeon_irq_ip4_handler_t h) |
1116 | { | |
1117 | octeon_irq_ip4 = h; | |
1118 | octeon_irq_use_ip4 = true; | |
1119 | on_each_cpu(octeon_irq_local_enable_ip4, NULL, 1); | |
1120 | } | |
1121 | ||
0c326387 DD |
1122 | static void __cpuinit octeon_irq_percpu_enable(void) |
1123 | { | |
1124 | irq_cpu_online(); | |
1125 | } | |
1126 | ||
1127 | static void __cpuinit octeon_irq_init_ciu_percpu(void) | |
1128 | { | |
1129 | int coreid = cvmx_get_core_num(); | |
1a7e68f2 DD |
1130 | |
1131 | ||
1132 | __get_cpu_var(octeon_irq_ciu0_en_mirror) = 0; | |
1133 | __get_cpu_var(octeon_irq_ciu1_en_mirror) = 0; | |
1134 | wmb(); | |
1135 | raw_spin_lock_init(&__get_cpu_var(octeon_irq_ciu_spinlock)); | |
5b3b1688 | 1136 | /* |
0c326387 DD |
1137 | * Disable All CIU Interrupts. The ones we need will be |
1138 | * enabled later. Read the SUM register so we know the write | |
1139 | * completed. | |
5b3b1688 | 1140 | */ |
0c326387 DD |
1141 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2)), 0); |
1142 | cvmx_write_csr(CVMX_CIU_INTX_EN0((coreid * 2 + 1)), 0); | |
1143 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2)), 0); | |
1144 | cvmx_write_csr(CVMX_CIU_INTX_EN1((coreid * 2 + 1)), 0); | |
1145 | cvmx_read_csr(CVMX_CIU_INTX_SUM0((coreid * 2))); | |
5b3b1688 | 1146 | } |
cd847b78 | 1147 | |
88fd8589 DD |
1148 | static void octeon_irq_init_ciu2_percpu(void) |
1149 | { | |
1150 | u64 regx, ipx; | |
1151 | int coreid = cvmx_get_core_num(); | |
1152 | u64 base = CVMX_CIU2_EN_PPX_IP2_WRKQ(coreid); | |
1153 | ||
1154 | /* | |
1155 | * Disable All CIU2 Interrupts. The ones we need will be | |
1156 | * enabled later. Read the SUM register so we know the write | |
1157 | * completed. | |
1158 | * | |
1159 | * There are 9 registers and 3 IPX levels with strides 0x1000 | |
1160 | * and 0x200 respectivly. Use loops to clear them. | |
1161 | */ | |
1162 | for (regx = 0; regx <= 0x8000; regx += 0x1000) { | |
1163 | for (ipx = 0; ipx <= 0x400; ipx += 0x200) | |
1164 | cvmx_write_csr(base + regx + ipx, 0); | |
1165 | } | |
1166 | ||
1167 | cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(coreid)); | |
1168 | } | |
1169 | ||
0c326387 | 1170 | static void __cpuinit octeon_irq_setup_secondary_ciu(void) |
cd847b78 | 1171 | { |
0c326387 DD |
1172 | octeon_irq_init_ciu_percpu(); |
1173 | octeon_irq_percpu_enable(); | |
5b3b1688 | 1174 | |
0c326387 DD |
1175 | /* Enable the CIU lines */ |
1176 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1177 | clear_c0_status(STATUSF_IP4); | |
1178 | } | |
5aae1fd4 | 1179 | |
88fd8589 DD |
1180 | static void octeon_irq_setup_secondary_ciu2(void) |
1181 | { | |
1182 | octeon_irq_init_ciu2_percpu(); | |
1183 | octeon_irq_percpu_enable(); | |
1184 | ||
1185 | /* Enable the CIU lines */ | |
1186 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1187 | if (octeon_irq_use_ip4) | |
1188 | set_c0_status(STATUSF_IP4); | |
1189 | else | |
1190 | clear_c0_status(STATUSF_IP4); | |
1191 | } | |
1192 | ||
0c326387 DD |
1193 | static void __init octeon_irq_init_ciu(void) |
1194 | { | |
1195 | unsigned int i; | |
1196 | struct irq_chip *chip; | |
0c326387 DD |
1197 | struct irq_chip *chip_mbox; |
1198 | struct irq_chip *chip_wd; | |
a0c16582 DD |
1199 | struct device_node *gpio_node; |
1200 | struct device_node *ciu_node; | |
87161ccd | 1201 | struct irq_domain *ciu_domain = NULL; |
0c326387 DD |
1202 | |
1203 | octeon_irq_init_ciu_percpu(); | |
1204 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu; | |
5aae1fd4 | 1205 | |
1a7e68f2 DD |
1206 | octeon_irq_ip2 = octeon_irq_ip2_ciu; |
1207 | octeon_irq_ip3 = octeon_irq_ip3_ciu; | |
0c326387 DD |
1208 | if (OCTEON_IS_MODEL(OCTEON_CN58XX_PASS2_X) || |
1209 | OCTEON_IS_MODEL(OCTEON_CN56XX_PASS2_X) || | |
1210 | OCTEON_IS_MODEL(OCTEON_CN52XX_PASS2_X) || | |
1211 | OCTEON_IS_MODEL(OCTEON_CN6XXX)) { | |
0c326387 | 1212 | chip = &octeon_irq_chip_ciu_v2; |
0c326387 DD |
1213 | chip_mbox = &octeon_irq_chip_ciu_mbox_v2; |
1214 | chip_wd = &octeon_irq_chip_ciu_wd_v2; | |
a0c16582 | 1215 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio_v2; |
0c326387 | 1216 | } else { |
0c326387 | 1217 | chip = &octeon_irq_chip_ciu; |
0c326387 DD |
1218 | chip_mbox = &octeon_irq_chip_ciu_mbox; |
1219 | chip_wd = &octeon_irq_chip_ciu_wd; | |
a0c16582 | 1220 | octeon_irq_gpio_chip = &octeon_irq_chip_ciu_gpio; |
0c326387 | 1221 | } |
a0c16582 | 1222 | octeon_irq_ciu_chip = chip; |
0c326387 DD |
1223 | octeon_irq_ip4 = octeon_irq_ip4_mask; |
1224 | ||
1225 | /* Mips internal */ | |
1226 | octeon_irq_init_core(); | |
1227 | ||
a0c16582 DD |
1228 | gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio"); |
1229 | if (gpio_node) { | |
1230 | struct octeon_irq_gpio_domain_data *gpiod; | |
1231 | ||
1232 | gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL); | |
1233 | if (gpiod) { | |
1234 | /* gpio domain host_data is the base hwirq number. */ | |
1235 | gpiod->base_hwirq = 16; | |
1236 | irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_gpio_ops, gpiod); | |
1237 | of_node_put(gpio_node); | |
1238 | } else | |
1239 | pr_warn("Cannot allocate memory for GPIO irq_domain.\n"); | |
1240 | } else | |
1241 | pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n"); | |
1242 | ||
1243 | ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-ciu"); | |
1244 | if (ciu_node) { | |
87161ccd | 1245 | ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu_ops, NULL); |
c9f0f0c0 | 1246 | irq_set_default_host(ciu_domain); |
a0c16582 DD |
1247 | of_node_put(ciu_node); |
1248 | } else | |
87161ccd DD |
1249 | panic("Cannot find device node for cavium,octeon-3860-ciu."); |
1250 | ||
1251 | /* CIU_0 */ | |
1252 | for (i = 0; i < 16; i++) | |
1253 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i + 0); | |
1254 | ||
88fd8589 DD |
1255 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX0, 0, 32, 0, chip_mbox, handle_percpu_irq); |
1256 | octeon_irq_set_ciu_mapping(OCTEON_IRQ_MBOX1, 0, 33, 0, chip_mbox, handle_percpu_irq); | |
87161ccd DD |
1257 | |
1258 | for (i = 0; i < 4; i++) | |
1259 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 0, i + 36); | |
1260 | for (i = 0; i < 4; i++) | |
1261 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 0, i + 40); | |
1262 | ||
1263 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_RML, 0, 46); | |
1264 | for (i = 0; i < 4; i++) | |
1265 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 0, i + 52); | |
1266 | ||
1267 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 0, 56); | |
87161ccd DD |
1268 | |
1269 | /* CIU_1 */ | |
1270 | for (i = 0; i < 16; i++) | |
88fd8589 | 1271 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i + 0, 0, chip_wd, handle_level_irq); |
87161ccd DD |
1272 | |
1273 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB1, 1, 17); | |
a0c16582 | 1274 | |
0c326387 DD |
1275 | /* Enable the CIU lines */ |
1276 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1277 | clear_c0_status(STATUSF_IP4); | |
1278 | } | |
5aae1fd4 | 1279 | |
88fd8589 DD |
1280 | /* |
1281 | * Watchdog interrupts are special. They are associated with a single | |
1282 | * core, so we hardwire the affinity to that core. | |
1283 | */ | |
1284 | static void octeon_irq_ciu2_wd_enable(struct irq_data *data) | |
1285 | { | |
1286 | u64 mask; | |
1287 | u64 en_addr; | |
1288 | int coreid = data->irq - OCTEON_IRQ_WDOG0; | |
1289 | union octeon_ciu_chip_data cd; | |
1290 | ||
1291 | cd.p = irq_data_get_irq_chip_data(data); | |
1292 | mask = 1ull << (cd.s.bit); | |
1293 | ||
1294 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | |
1295 | cvmx_write_csr(en_addr, mask); | |
1296 | ||
1297 | } | |
1298 | ||
1299 | static void octeon_irq_ciu2_enable(struct irq_data *data) | |
1300 | { | |
1301 | u64 mask; | |
1302 | u64 en_addr; | |
1303 | int cpu = next_cpu_for_irq(data); | |
1304 | int coreid = octeon_coreid_for_cpu(cpu); | |
1305 | union octeon_ciu_chip_data cd; | |
1306 | ||
1307 | cd.p = irq_data_get_irq_chip_data(data); | |
1308 | mask = 1ull << (cd.s.bit); | |
1309 | ||
1310 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | |
1311 | cvmx_write_csr(en_addr, mask); | |
1312 | } | |
1313 | ||
1314 | static void octeon_irq_ciu2_enable_local(struct irq_data *data) | |
1315 | { | |
1316 | u64 mask; | |
1317 | u64 en_addr; | |
1318 | int coreid = cvmx_get_core_num(); | |
1319 | union octeon_ciu_chip_data cd; | |
1320 | ||
1321 | cd.p = irq_data_get_irq_chip_data(data); | |
1322 | mask = 1ull << (cd.s.bit); | |
1323 | ||
1324 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(coreid) + (0x1000ull * cd.s.line); | |
1325 | cvmx_write_csr(en_addr, mask); | |
1326 | ||
1327 | } | |
1328 | ||
1329 | static void octeon_irq_ciu2_disable_local(struct irq_data *data) | |
1330 | { | |
1331 | u64 mask; | |
1332 | u64 en_addr; | |
1333 | int coreid = cvmx_get_core_num(); | |
1334 | union octeon_ciu_chip_data cd; | |
1335 | ||
1336 | cd.p = irq_data_get_irq_chip_data(data); | |
1337 | mask = 1ull << (cd.s.bit); | |
1338 | ||
1339 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(coreid) + (0x1000ull * cd.s.line); | |
1340 | cvmx_write_csr(en_addr, mask); | |
1341 | ||
1342 | } | |
1343 | ||
1344 | static void octeon_irq_ciu2_ack(struct irq_data *data) | |
1345 | { | |
1346 | u64 mask; | |
1347 | u64 en_addr; | |
1348 | int coreid = cvmx_get_core_num(); | |
1349 | union octeon_ciu_chip_data cd; | |
1350 | ||
1351 | cd.p = irq_data_get_irq_chip_data(data); | |
1352 | mask = 1ull << (cd.s.bit); | |
1353 | ||
1354 | en_addr = CVMX_CIU2_RAW_PPX_IP2_WRKQ(coreid) + (0x1000ull * cd.s.line); | |
1355 | cvmx_write_csr(en_addr, mask); | |
1356 | ||
1357 | } | |
1358 | ||
1359 | static void octeon_irq_ciu2_disable_all(struct irq_data *data) | |
1360 | { | |
1361 | int cpu; | |
1362 | u64 mask; | |
1363 | union octeon_ciu_chip_data cd; | |
1364 | ||
1365 | cd.p = irq_data_get_irq_chip_data(data); | |
1366 | mask = 1ull << (cd.s.bit); | |
1367 | ||
1368 | for_each_online_cpu(cpu) { | |
1369 | u64 en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | |
1370 | cvmx_write_csr(en_addr, mask); | |
1371 | } | |
1372 | } | |
1373 | ||
1374 | static void octeon_irq_ciu2_mbox_enable_all(struct irq_data *data) | |
1375 | { | |
1376 | int cpu; | |
1377 | u64 mask; | |
1378 | ||
1379 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | |
1380 | ||
1381 | for_each_online_cpu(cpu) { | |
1382 | u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(octeon_coreid_for_cpu(cpu)); | |
1383 | cvmx_write_csr(en_addr, mask); | |
1384 | } | |
1385 | } | |
1386 | ||
1387 | static void octeon_irq_ciu2_mbox_disable_all(struct irq_data *data) | |
1388 | { | |
1389 | int cpu; | |
1390 | u64 mask; | |
1391 | ||
1392 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | |
1393 | ||
1394 | for_each_online_cpu(cpu) { | |
1395 | u64 en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(octeon_coreid_for_cpu(cpu)); | |
1396 | cvmx_write_csr(en_addr, mask); | |
1397 | } | |
1398 | } | |
1399 | ||
1400 | static void octeon_irq_ciu2_mbox_enable_local(struct irq_data *data) | |
1401 | { | |
1402 | u64 mask; | |
1403 | u64 en_addr; | |
1404 | int coreid = cvmx_get_core_num(); | |
1405 | ||
1406 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | |
1407 | en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1S(coreid); | |
1408 | cvmx_write_csr(en_addr, mask); | |
1409 | } | |
1410 | ||
1411 | static void octeon_irq_ciu2_mbox_disable_local(struct irq_data *data) | |
1412 | { | |
1413 | u64 mask; | |
1414 | u64 en_addr; | |
1415 | int coreid = cvmx_get_core_num(); | |
1416 | ||
1417 | mask = 1ull << (data->irq - OCTEON_IRQ_MBOX0); | |
1418 | en_addr = CVMX_CIU2_EN_PPX_IP3_MBOX_W1C(coreid); | |
1419 | cvmx_write_csr(en_addr, mask); | |
1420 | } | |
1421 | ||
1422 | #ifdef CONFIG_SMP | |
1423 | static int octeon_irq_ciu2_set_affinity(struct irq_data *data, | |
1424 | const struct cpumask *dest, bool force) | |
1425 | { | |
1426 | int cpu; | |
1427 | bool enable_one = !irqd_irq_disabled(data) && !irqd_irq_masked(data); | |
1428 | u64 mask; | |
1429 | union octeon_ciu_chip_data cd; | |
1430 | ||
1431 | if (!enable_one) | |
1432 | return 0; | |
1433 | ||
1434 | cd.p = irq_data_get_irq_chip_data(data); | |
1435 | mask = 1ull << cd.s.bit; | |
1436 | ||
1437 | for_each_online_cpu(cpu) { | |
1438 | u64 en_addr; | |
1439 | if (cpumask_test_cpu(cpu, dest) && enable_one) { | |
1440 | enable_one = false; | |
1441 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1S(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | |
1442 | } else { | |
1443 | en_addr = CVMX_CIU2_EN_PPX_IP2_WRKQ_W1C(octeon_coreid_for_cpu(cpu)) + (0x1000ull * cd.s.line); | |
1444 | } | |
1445 | cvmx_write_csr(en_addr, mask); | |
1446 | } | |
1447 | ||
1448 | return 0; | |
1449 | } | |
1450 | #endif | |
1451 | ||
1452 | static void octeon_irq_ciu2_enable_gpio(struct irq_data *data) | |
1453 | { | |
1454 | octeon_irq_gpio_setup(data); | |
1455 | octeon_irq_ciu2_enable(data); | |
1456 | } | |
1457 | ||
1458 | static void octeon_irq_ciu2_disable_gpio(struct irq_data *data) | |
1459 | { | |
1460 | union octeon_ciu_chip_data cd; | |
1461 | cd.p = irq_data_get_irq_chip_data(data); | |
1462 | ||
1463 | cvmx_write_csr(CVMX_GPIO_BIT_CFGX(cd.s.gpio_line), 0); | |
1464 | ||
1465 | octeon_irq_ciu2_disable_all(data); | |
1466 | } | |
1467 | ||
1468 | static struct irq_chip octeon_irq_chip_ciu2 = { | |
1469 | .name = "CIU2-E", | |
1470 | .irq_enable = octeon_irq_ciu2_enable, | |
1471 | .irq_disable = octeon_irq_ciu2_disable_all, | |
1472 | .irq_ack = octeon_irq_ciu2_ack, | |
1473 | .irq_mask = octeon_irq_ciu2_disable_local, | |
1474 | .irq_unmask = octeon_irq_ciu2_enable, | |
1475 | #ifdef CONFIG_SMP | |
1476 | .irq_set_affinity = octeon_irq_ciu2_set_affinity, | |
1477 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
1478 | #endif | |
1479 | }; | |
1480 | ||
1481 | static struct irq_chip octeon_irq_chip_ciu2_mbox = { | |
1482 | .name = "CIU2-M", | |
1483 | .irq_enable = octeon_irq_ciu2_mbox_enable_all, | |
1484 | .irq_disable = octeon_irq_ciu2_mbox_disable_all, | |
1485 | .irq_ack = octeon_irq_ciu2_mbox_disable_local, | |
1486 | .irq_eoi = octeon_irq_ciu2_mbox_enable_local, | |
1487 | ||
1488 | .irq_cpu_online = octeon_irq_ciu2_mbox_enable_local, | |
1489 | .irq_cpu_offline = octeon_irq_ciu2_mbox_disable_local, | |
1490 | .flags = IRQCHIP_ONOFFLINE_ENABLED, | |
1491 | }; | |
1492 | ||
1493 | static struct irq_chip octeon_irq_chip_ciu2_wd = { | |
1494 | .name = "CIU2-W", | |
1495 | .irq_enable = octeon_irq_ciu2_wd_enable, | |
1496 | .irq_disable = octeon_irq_ciu2_disable_all, | |
1497 | .irq_mask = octeon_irq_ciu2_disable_local, | |
1498 | .irq_unmask = octeon_irq_ciu2_enable_local, | |
1499 | }; | |
1500 | ||
1501 | static struct irq_chip octeon_irq_chip_ciu2_gpio = { | |
1502 | .name = "CIU-GPIO", | |
1503 | .irq_enable = octeon_irq_ciu2_enable_gpio, | |
1504 | .irq_disable = octeon_irq_ciu2_disable_gpio, | |
1505 | .irq_ack = octeon_irq_ciu_gpio_ack, | |
1506 | .irq_mask = octeon_irq_ciu2_disable_local, | |
1507 | .irq_unmask = octeon_irq_ciu2_enable, | |
1508 | .irq_set_type = octeon_irq_ciu_gpio_set_type, | |
1509 | #ifdef CONFIG_SMP | |
1510 | .irq_set_affinity = octeon_irq_ciu2_set_affinity, | |
1511 | .irq_cpu_offline = octeon_irq_cpu_offline_ciu, | |
1512 | #endif | |
1513 | .flags = IRQCHIP_SET_TYPE_MASKED, | |
1514 | }; | |
1515 | ||
1516 | static int octeon_irq_ciu2_xlat(struct irq_domain *d, | |
1517 | struct device_node *node, | |
1518 | const u32 *intspec, | |
1519 | unsigned int intsize, | |
1520 | unsigned long *out_hwirq, | |
1521 | unsigned int *out_type) | |
1522 | { | |
1523 | unsigned int ciu, bit; | |
1524 | ||
1525 | ciu = intspec[0]; | |
1526 | bit = intspec[1]; | |
1527 | ||
1528 | /* Line 7 are the GPIO lines */ | |
1529 | if (ciu > 6 || bit > 63) | |
1530 | return -EINVAL; | |
1531 | ||
1532 | *out_hwirq = (ciu << 6) | bit; | |
1533 | *out_type = 0; | |
1534 | ||
1535 | return 0; | |
1536 | } | |
1537 | ||
1538 | static bool octeon_irq_ciu2_is_edge(unsigned int line, unsigned int bit) | |
1539 | { | |
1540 | bool edge = false; | |
1541 | ||
1542 | if (line == 3) /* MIO */ | |
1543 | switch (bit) { | |
70342287 | 1544 | case 2: /* IPD_DRP */ |
88fd8589 DD |
1545 | case 8 ... 11: /* Timers */ |
1546 | case 48: /* PTP */ | |
1547 | edge = true; | |
1548 | break; | |
1549 | default: | |
1550 | break; | |
1551 | } | |
1552 | else if (line == 6) /* PKT */ | |
1553 | switch (bit) { | |
1554 | case 52 ... 53: /* ILK_DRP */ | |
70342287 | 1555 | case 8 ... 12: /* GMX_DRP */ |
88fd8589 DD |
1556 | edge = true; |
1557 | break; | |
1558 | default: | |
1559 | break; | |
1560 | } | |
1561 | return edge; | |
1562 | } | |
1563 | ||
1564 | static int octeon_irq_ciu2_map(struct irq_domain *d, | |
1565 | unsigned int virq, irq_hw_number_t hw) | |
1566 | { | |
1567 | unsigned int line = hw >> 6; | |
1568 | unsigned int bit = hw & 63; | |
1569 | ||
1570 | if (!octeon_irq_virq_in_range(virq)) | |
1571 | return -EINVAL; | |
1572 | ||
1573 | /* Line 7 are the GPIO lines */ | |
1574 | if (line > 6 || octeon_irq_ciu_to_irq[line][bit] != 0) | |
1575 | return -EINVAL; | |
1576 | ||
1577 | if (octeon_irq_ciu2_is_edge(line, bit)) | |
1578 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, | |
1579 | &octeon_irq_chip_ciu2, | |
1580 | handle_edge_irq); | |
1581 | else | |
1582 | octeon_irq_set_ciu_mapping(virq, line, bit, 0, | |
1583 | &octeon_irq_chip_ciu2, | |
1584 | handle_level_irq); | |
1585 | ||
1586 | return 0; | |
1587 | } | |
1588 | static int octeon_irq_ciu2_gpio_map(struct irq_domain *d, | |
1589 | unsigned int virq, irq_hw_number_t hw) | |
1590 | { | |
1591 | return octeon_irq_gpio_map_common(d, virq, hw, 7, &octeon_irq_chip_ciu2_gpio); | |
1592 | } | |
1593 | ||
1594 | static struct irq_domain_ops octeon_irq_domain_ciu2_ops = { | |
1595 | .map = octeon_irq_ciu2_map, | |
1596 | .xlate = octeon_irq_ciu2_xlat, | |
1597 | }; | |
1598 | ||
1599 | static struct irq_domain_ops octeon_irq_domain_ciu2_gpio_ops = { | |
1600 | .map = octeon_irq_ciu2_gpio_map, | |
1601 | .xlate = octeon_irq_gpio_xlat, | |
1602 | }; | |
1603 | ||
1604 | static void octeon_irq_ciu2(void) | |
1605 | { | |
1606 | int line; | |
1607 | int bit; | |
1608 | int irq; | |
1609 | u64 src_reg, src, sum; | |
1610 | const unsigned long core_id = cvmx_get_core_num(); | |
1611 | ||
1612 | sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP2(core_id)) & 0xfful; | |
1613 | ||
1614 | if (unlikely(!sum)) | |
1615 | goto spurious; | |
1616 | ||
1617 | line = fls64(sum) - 1; | |
1618 | src_reg = CVMX_CIU2_SRC_PPX_IP2_WRKQ(core_id) + (0x1000 * line); | |
1619 | src = cvmx_read_csr(src_reg); | |
1620 | ||
1621 | if (unlikely(!src)) | |
1622 | goto spurious; | |
1623 | ||
1624 | bit = fls64(src) - 1; | |
1625 | irq = octeon_irq_ciu_to_irq[line][bit]; | |
1626 | if (unlikely(!irq)) | |
1627 | goto spurious; | |
1628 | ||
1629 | do_IRQ(irq); | |
1630 | goto out; | |
1631 | ||
1632 | spurious: | |
1633 | spurious_interrupt(); | |
1634 | out: | |
1635 | /* CN68XX pass 1.x has an errata that accessing the ACK registers | |
1636 | can stop interrupts from propagating */ | |
1637 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | |
1638 | cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY); | |
1639 | else | |
1640 | cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP2(core_id)); | |
1641 | return; | |
1642 | } | |
1643 | ||
1644 | static void octeon_irq_ciu2_mbox(void) | |
1645 | { | |
1646 | int line; | |
1647 | ||
1648 | const unsigned long core_id = cvmx_get_core_num(); | |
1649 | u64 sum = cvmx_read_csr(CVMX_CIU2_SUM_PPX_IP3(core_id)) >> 60; | |
1650 | ||
1651 | if (unlikely(!sum)) | |
1652 | goto spurious; | |
1653 | ||
1654 | line = fls64(sum) - 1; | |
1655 | ||
1656 | do_IRQ(OCTEON_IRQ_MBOX0 + line); | |
1657 | goto out; | |
1658 | ||
1659 | spurious: | |
1660 | spurious_interrupt(); | |
1661 | out: | |
1662 | /* CN68XX pass 1.x has an errata that accessing the ACK registers | |
1663 | can stop interrupts from propagating */ | |
1664 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) | |
1665 | cvmx_read_csr(CVMX_CIU2_INTR_CIU_READY); | |
1666 | else | |
1667 | cvmx_read_csr(CVMX_CIU2_ACK_PPX_IP3(core_id)); | |
1668 | return; | |
1669 | } | |
1670 | ||
1671 | static void __init octeon_irq_init_ciu2(void) | |
1672 | { | |
1673 | unsigned int i; | |
1674 | struct device_node *gpio_node; | |
1675 | struct device_node *ciu_node; | |
1676 | struct irq_domain *ciu_domain = NULL; | |
1677 | ||
1678 | octeon_irq_init_ciu2_percpu(); | |
1679 | octeon_irq_setup_secondary = octeon_irq_setup_secondary_ciu2; | |
1680 | ||
1681 | octeon_irq_ip2 = octeon_irq_ciu2; | |
1682 | octeon_irq_ip3 = octeon_irq_ciu2_mbox; | |
1683 | octeon_irq_ip4 = octeon_irq_ip4_mask; | |
1684 | ||
1685 | /* Mips internal */ | |
1686 | octeon_irq_init_core(); | |
1687 | ||
1688 | gpio_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-3860-gpio"); | |
1689 | if (gpio_node) { | |
1690 | struct octeon_irq_gpio_domain_data *gpiod; | |
1691 | ||
1692 | gpiod = kzalloc(sizeof(*gpiod), GFP_KERNEL); | |
1693 | if (gpiod) { | |
1694 | /* gpio domain host_data is the base hwirq number. */ | |
1695 | gpiod->base_hwirq = 7 << 6; | |
1696 | irq_domain_add_linear(gpio_node, 16, &octeon_irq_domain_ciu2_gpio_ops, gpiod); | |
1697 | of_node_put(gpio_node); | |
1698 | } else | |
1699 | pr_warn("Cannot allocate memory for GPIO irq_domain.\n"); | |
1700 | } else | |
1701 | pr_warn("Cannot find device node for cavium,octeon-3860-gpio.\n"); | |
1702 | ||
1703 | ciu_node = of_find_compatible_node(NULL, NULL, "cavium,octeon-6880-ciu2"); | |
1704 | if (ciu_node) { | |
1705 | ciu_domain = irq_domain_add_tree(ciu_node, &octeon_irq_domain_ciu2_ops, NULL); | |
c9f0f0c0 | 1706 | irq_set_default_host(ciu_domain); |
88fd8589 DD |
1707 | of_node_put(ciu_node); |
1708 | } else | |
1709 | panic("Cannot find device node for cavium,octeon-6880-ciu2."); | |
1710 | ||
1711 | /* CUI2 */ | |
1712 | for (i = 0; i < 64; i++) | |
1713 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_WORKQ0, 0, i); | |
1714 | ||
1715 | for (i = 0; i < 32; i++) | |
1716 | octeon_irq_set_ciu_mapping(i + OCTEON_IRQ_WDOG0, 1, i, 0, | |
1717 | &octeon_irq_chip_ciu2_wd, handle_level_irq); | |
1718 | ||
1719 | for (i = 0; i < 4; i++) | |
1720 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_TIMER0, 3, i + 8); | |
1721 | ||
1722 | octeon_irq_force_ciu_mapping(ciu_domain, OCTEON_IRQ_USB0, 3, 44); | |
1723 | ||
1724 | for (i = 0; i < 4; i++) | |
1725 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_INT0, 4, i); | |
1726 | ||
1727 | for (i = 0; i < 4; i++) | |
1728 | octeon_irq_force_ciu_mapping(ciu_domain, i + OCTEON_IRQ_PCI_MSI0, 4, i + 8); | |
1729 | ||
1730 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX0, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | |
1731 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX1, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | |
1732 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX2, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | |
1733 | irq_set_chip_and_handler(OCTEON_IRQ_MBOX3, &octeon_irq_chip_ciu2_mbox, handle_percpu_irq); | |
1734 | ||
1735 | /* Enable the CIU lines */ | |
1736 | set_c0_status(STATUSF_IP3 | STATUSF_IP2); | |
1737 | clear_c0_status(STATUSF_IP4); | |
1738 | } | |
1739 | ||
5b3b1688 DD |
1740 | void __init arch_init_irq(void) |
1741 | { | |
5b3b1688 DD |
1742 | #ifdef CONFIG_SMP |
1743 | /* Set the default affinity to the boot cpu. */ | |
1744 | cpumask_clear(irq_default_affinity); | |
1745 | cpumask_set_cpu(smp_processor_id(), irq_default_affinity); | |
1746 | #endif | |
88fd8589 DD |
1747 | if (OCTEON_IS_MODEL(OCTEON_CN68XX)) |
1748 | octeon_irq_init_ciu2(); | |
1749 | else | |
1750 | octeon_irq_init_ciu(); | |
5b3b1688 DD |
1751 | } |
1752 | ||
1753 | asmlinkage void plat_irq_dispatch(void) | |
1754 | { | |
5b3b1688 DD |
1755 | unsigned long cop0_cause; |
1756 | unsigned long cop0_status; | |
5b3b1688 DD |
1757 | |
1758 | while (1) { | |
1759 | cop0_cause = read_c0_cause(); | |
1760 | cop0_status = read_c0_status(); | |
1761 | cop0_cause &= cop0_status; | |
1762 | cop0_cause &= ST0_IM; | |
1763 | ||
0c326387 DD |
1764 | if (unlikely(cop0_cause & STATUSF_IP2)) |
1765 | octeon_irq_ip2(); | |
1766 | else if (unlikely(cop0_cause & STATUSF_IP3)) | |
1767 | octeon_irq_ip3(); | |
1768 | else if (unlikely(cop0_cause & STATUSF_IP4)) | |
1769 | octeon_irq_ip4(); | |
1770 | else if (likely(cop0_cause)) | |
5b3b1688 | 1771 | do_IRQ(fls(cop0_cause) - 9 + MIPS_CPU_IRQ_BASE); |
0c326387 | 1772 | else |
5b3b1688 | 1773 | break; |
5b3b1688 DD |
1774 | } |
1775 | } | |
773cb77d RB |
1776 | |
1777 | #ifdef CONFIG_HOTPLUG_CPU | |
773cb77d RB |
1778 | |
1779 | void fixup_irqs(void) | |
1780 | { | |
0c326387 | 1781 | irq_cpu_offline(); |
773cb77d RB |
1782 | } |
1783 | ||
1784 | #endif /* CONFIG_HOTPLUG_CPU */ |