]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/mips/bcm63xx/irq.c
MIPS: BCM63xx: Wire up the second cpu's irq line
[karo-tx-linux.git] / arch / mips / bcm63xx / irq.c
1 /*
2  * This file is subject to the terms and conditions of the GNU General Public
3  * License.  See the file "COPYING" in the main directory of this archive
4  * for more details.
5  *
6  * Copyright (C) 2008 Maxime Bizon <mbizon@freebox.fr>
7  * Copyright (C) 2008 Nicolas Schichan <nschichan@freebox.fr>
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/init.h>
12 #include <linux/interrupt.h>
13 #include <linux/module.h>
14 #include <linux/irq.h>
15 #include <linux/spinlock.h>
16 #include <asm/irq_cpu.h>
17 #include <asm/mipsregs.h>
18 #include <bcm63xx_cpu.h>
19 #include <bcm63xx_regs.h>
20 #include <bcm63xx_io.h>
21 #include <bcm63xx_irq.h>
22
23
24 static DEFINE_SPINLOCK(ipic_lock);
25 static DEFINE_SPINLOCK(epic_lock);
26
27 static u32 irq_stat_addr[2];
28 static u32 irq_mask_addr[2];
29 static void (*dispatch_internal)(int cpu);
30 static int is_ext_irq_cascaded;
31 static unsigned int ext_irq_count;
32 static unsigned int ext_irq_start, ext_irq_end;
33 static unsigned int ext_irq_cfg_reg1, ext_irq_cfg_reg2;
34 static void (*internal_irq_mask)(unsigned int irq);
35 static void (*internal_irq_unmask)(unsigned int irq);
36
37
38 static inline u32 get_ext_irq_perf_reg(int irq)
39 {
40         if (irq < 4)
41                 return ext_irq_cfg_reg1;
42         return ext_irq_cfg_reg2;
43 }
44
45 static inline void handle_internal(int intbit)
46 {
47         if (is_ext_irq_cascaded &&
48             intbit >= ext_irq_start && intbit <= ext_irq_end)
49                 do_IRQ(intbit - ext_irq_start + IRQ_EXTERNAL_BASE);
50         else
51                 do_IRQ(intbit + IRQ_INTERNAL_BASE);
52 }
53
54 /*
55  * dispatch internal devices IRQ (uart, enet, watchdog, ...). do not
56  * prioritize any interrupt relatively to another. the static counter
57  * will resume the loop where it ended the last time we left this
58  * function.
59  */
60
61 #define BUILD_IPIC_INTERNAL(width)                                      \
62 void __dispatch_internal_##width(int cpu)                               \
63 {                                                                       \
64         u32 pending[width / 32];                                        \
65         unsigned int src, tgt;                                          \
66         bool irqs_pending = false;                                      \
67         static unsigned int i[2];                                       \
68         unsigned int *next = &i[cpu];                                   \
69         unsigned long flags;                                            \
70                                                                         \
71         /* read registers in reverse order */                           \
72         spin_lock_irqsave(&ipic_lock, flags);                           \
73         for (src = 0, tgt = (width / 32); src < (width / 32); src++) {  \
74                 u32 val;                                                \
75                                                                         \
76                 val = bcm_readl(irq_stat_addr[cpu] + src * sizeof(u32)); \
77                 val &= bcm_readl(irq_mask_addr[cpu] + src * sizeof(u32)); \
78                 pending[--tgt] = val;                                   \
79                                                                         \
80                 if (val)                                                \
81                         irqs_pending = true;                            \
82         }                                                               \
83         spin_unlock_irqrestore(&ipic_lock, flags);                      \
84                                                                         \
85         if (!irqs_pending)                                              \
86                 return;                                                 \
87                                                                         \
88         while (1) {                                                     \
89                 unsigned int to_call = *next;                           \
90                                                                         \
91                 *next = (*next + 1) & (width - 1);                      \
92                 if (pending[to_call / 32] & (1 << (to_call & 0x1f))) {  \
93                         handle_internal(to_call);                       \
94                         break;                                          \
95                 }                                                       \
96         }                                                               \
97 }                                                                       \
98                                                                         \
99 static void __internal_irq_mask_##width(unsigned int irq)               \
100 {                                                                       \
101         u32 val;                                                        \
102         unsigned reg = (irq / 32) ^ (width/32 - 1);                     \
103         unsigned bit = irq & 0x1f;                                      \
104         unsigned long flags;                                            \
105         int cpu;                                                        \
106                                                                         \
107         spin_lock_irqsave(&ipic_lock, flags);                           \
108         for_each_present_cpu(cpu) {                                     \
109                 if (!irq_mask_addr[cpu])                                \
110                         break;                                          \
111                                                                         \
112                 val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
113                 val &= ~(1 << bit);                                     \
114                 bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
115         }                                                               \
116         spin_unlock_irqrestore(&ipic_lock, flags);                      \
117 }                                                                       \
118                                                                         \
119 static void __internal_irq_unmask_##width(unsigned int irq)             \
120 {                                                                       \
121         u32 val;                                                        \
122         unsigned reg = (irq / 32) ^ (width/32 - 1);                     \
123         unsigned bit = irq & 0x1f;                                      \
124         unsigned long flags;                                            \
125         int cpu;                                                        \
126                                                                         \
127         spin_lock_irqsave(&ipic_lock, flags);                           \
128         for_each_present_cpu(cpu) {                                     \
129                 if (!irq_mask_addr[cpu])                                \
130                         break;                                          \
131                                                                         \
132                 val = bcm_readl(irq_mask_addr[cpu] + reg * sizeof(u32));\
133                 if (cpu_online(cpu))                                    \
134                         val |= (1 << bit);                              \
135                 else                                                    \
136                         val &= ~(1 << bit);                             \
137                 bcm_writel(val, irq_mask_addr[cpu] + reg * sizeof(u32));\
138         }                                                               \
139         spin_unlock_irqrestore(&ipic_lock, flags);                      \
140 }
141
142 BUILD_IPIC_INTERNAL(32);
143 BUILD_IPIC_INTERNAL(64);
144
145 asmlinkage void plat_irq_dispatch(void)
146 {
147         u32 cause;
148
149         do {
150                 cause = read_c0_cause() & read_c0_status() & ST0_IM;
151
152                 if (!cause)
153                         break;
154
155                 if (cause & CAUSEF_IP7)
156                         do_IRQ(7);
157                 if (cause & CAUSEF_IP0)
158                         do_IRQ(0);
159                 if (cause & CAUSEF_IP1)
160                         do_IRQ(1);
161                 if (cause & CAUSEF_IP2)
162                         dispatch_internal(0);
163                 if (is_ext_irq_cascaded) {
164                         if (cause & CAUSEF_IP3)
165                                 dispatch_internal(1);
166                 } else {
167                         if (cause & CAUSEF_IP3)
168                                 do_IRQ(IRQ_EXT_0);
169                         if (cause & CAUSEF_IP4)
170                                 do_IRQ(IRQ_EXT_1);
171                         if (cause & CAUSEF_IP5)
172                                 do_IRQ(IRQ_EXT_2);
173                         if (cause & CAUSEF_IP6)
174                                 do_IRQ(IRQ_EXT_3);
175                 }
176         } while (1);
177 }
178
179 /*
180  * internal IRQs operations: only mask/unmask on PERF irq mask
181  * register.
182  */
183 static void bcm63xx_internal_irq_mask(struct irq_data *d)
184 {
185         internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
186 }
187
188 static void bcm63xx_internal_irq_unmask(struct irq_data *d)
189 {
190         internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
191 }
192
193 /*
194  * external IRQs operations: mask/unmask and clear on PERF external
195  * irq control register.
196  */
197 static void bcm63xx_external_irq_mask(struct irq_data *d)
198 {
199         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
200         u32 reg, regaddr;
201         unsigned long flags;
202
203         regaddr = get_ext_irq_perf_reg(irq);
204         spin_lock_irqsave(&epic_lock, flags);
205         reg = bcm_perf_readl(regaddr);
206
207         if (BCMCPU_IS_6348())
208                 reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
209         else
210                 reg &= ~EXTIRQ_CFG_MASK(irq % 4);
211
212         bcm_perf_writel(reg, regaddr);
213         spin_unlock_irqrestore(&epic_lock, flags);
214
215         if (is_ext_irq_cascaded)
216                 internal_irq_mask(irq + ext_irq_start);
217 }
218
219 static void bcm63xx_external_irq_unmask(struct irq_data *d)
220 {
221         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
222         u32 reg, regaddr;
223         unsigned long flags;
224
225         regaddr = get_ext_irq_perf_reg(irq);
226         spin_lock_irqsave(&epic_lock, flags);
227         reg = bcm_perf_readl(regaddr);
228
229         if (BCMCPU_IS_6348())
230                 reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
231         else
232                 reg |= EXTIRQ_CFG_MASK(irq % 4);
233
234         bcm_perf_writel(reg, regaddr);
235         spin_unlock_irqrestore(&epic_lock, flags);
236
237         if (is_ext_irq_cascaded)
238                 internal_irq_unmask(irq + ext_irq_start);
239 }
240
241 static void bcm63xx_external_irq_clear(struct irq_data *d)
242 {
243         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
244         u32 reg, regaddr;
245         unsigned long flags;
246
247         regaddr = get_ext_irq_perf_reg(irq);
248         spin_lock_irqsave(&epic_lock, flags);
249         reg = bcm_perf_readl(regaddr);
250
251         if (BCMCPU_IS_6348())
252                 reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
253         else
254                 reg |= EXTIRQ_CFG_CLEAR(irq % 4);
255
256         bcm_perf_writel(reg, regaddr);
257         spin_unlock_irqrestore(&epic_lock, flags);
258 }
259
260 static int bcm63xx_external_irq_set_type(struct irq_data *d,
261                                          unsigned int flow_type)
262 {
263         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
264         u32 reg, regaddr;
265         int levelsense, sense, bothedge;
266         unsigned long flags;
267
268         flow_type &= IRQ_TYPE_SENSE_MASK;
269
270         if (flow_type == IRQ_TYPE_NONE)
271                 flow_type = IRQ_TYPE_LEVEL_LOW;
272
273         levelsense = sense = bothedge = 0;
274         switch (flow_type) {
275         case IRQ_TYPE_EDGE_BOTH:
276                 bothedge = 1;
277                 break;
278
279         case IRQ_TYPE_EDGE_RISING:
280                 sense = 1;
281                 break;
282
283         case IRQ_TYPE_EDGE_FALLING:
284                 break;
285
286         case IRQ_TYPE_LEVEL_HIGH:
287                 levelsense = 1;
288                 sense = 1;
289                 break;
290
291         case IRQ_TYPE_LEVEL_LOW:
292                 levelsense = 1;
293                 break;
294
295         default:
296                 printk(KERN_ERR "bogus flow type combination given !\n");
297                 return -EINVAL;
298         }
299
300         regaddr = get_ext_irq_perf_reg(irq);
301         spin_lock_irqsave(&epic_lock, flags);
302         reg = bcm_perf_readl(regaddr);
303         irq %= 4;
304
305         switch (bcm63xx_get_cpu_id()) {
306         case BCM6348_CPU_ID:
307                 if (levelsense)
308                         reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
309                 else
310                         reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
311                 if (sense)
312                         reg |= EXTIRQ_CFG_SENSE_6348(irq);
313                 else
314                         reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
315                 if (bothedge)
316                         reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
317                 else
318                         reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
319                 break;
320
321         case BCM3368_CPU_ID:
322         case BCM6328_CPU_ID:
323         case BCM6338_CPU_ID:
324         case BCM6345_CPU_ID:
325         case BCM6358_CPU_ID:
326         case BCM6362_CPU_ID:
327         case BCM6368_CPU_ID:
328                 if (levelsense)
329                         reg |= EXTIRQ_CFG_LEVELSENSE(irq);
330                 else
331                         reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
332                 if (sense)
333                         reg |= EXTIRQ_CFG_SENSE(irq);
334                 else
335                         reg &= ~EXTIRQ_CFG_SENSE(irq);
336                 if (bothedge)
337                         reg |= EXTIRQ_CFG_BOTHEDGE(irq);
338                 else
339                         reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
340                 break;
341         default:
342                 BUG();
343         }
344
345         bcm_perf_writel(reg, regaddr);
346         spin_unlock_irqrestore(&epic_lock, flags);
347
348         irqd_set_trigger_type(d, flow_type);
349         if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
350                 __irq_set_handler_locked(d->irq, handle_level_irq);
351         else
352                 __irq_set_handler_locked(d->irq, handle_edge_irq);
353
354         return IRQ_SET_MASK_OK_NOCOPY;
355 }
356
357 static struct irq_chip bcm63xx_internal_irq_chip = {
358         .name           = "bcm63xx_ipic",
359         .irq_mask       = bcm63xx_internal_irq_mask,
360         .irq_unmask     = bcm63xx_internal_irq_unmask,
361 };
362
363 static struct irq_chip bcm63xx_external_irq_chip = {
364         .name           = "bcm63xx_epic",
365         .irq_ack        = bcm63xx_external_irq_clear,
366
367         .irq_mask       = bcm63xx_external_irq_mask,
368         .irq_unmask     = bcm63xx_external_irq_unmask,
369
370         .irq_set_type   = bcm63xx_external_irq_set_type,
371 };
372
373 static struct irqaction cpu_ip2_cascade_action = {
374         .handler        = no_action,
375         .name           = "cascade_ip2",
376         .flags          = IRQF_NO_THREAD,
377 };
378
379 #ifdef CONFIG_SMP
380 static struct irqaction cpu_ip3_cascade_action = {
381         .handler        = no_action,
382         .name           = "cascade_ip3",
383         .flags          = IRQF_NO_THREAD,
384 };
385 #endif
386
387 static struct irqaction cpu_ext_cascade_action = {
388         .handler        = no_action,
389         .name           = "cascade_extirq",
390         .flags          = IRQF_NO_THREAD,
391 };
392
393 static void bcm63xx_init_irq(void)
394 {
395         int irq_bits;
396
397         irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF);
398         irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF);
399         irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF);
400         irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF);
401
402         switch (bcm63xx_get_cpu_id()) {
403         case BCM3368_CPU_ID:
404                 irq_stat_addr[0] += PERF_IRQSTAT_3368_REG;
405                 irq_mask_addr[0] += PERF_IRQMASK_3368_REG;
406                 irq_stat_addr[1] = 0;
407                 irq_stat_addr[1] = 0;
408                 irq_bits = 32;
409                 ext_irq_count = 4;
410                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
411                 break;
412         case BCM6328_CPU_ID:
413                 irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0);
414                 irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0);
415                 irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1);
416                 irq_stat_addr[1] += PERF_IRQMASK_6328_REG(1);
417                 irq_bits = 64;
418                 ext_irq_count = 4;
419                 is_ext_irq_cascaded = 1;
420                 ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
421                 ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
422                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
423                 break;
424         case BCM6338_CPU_ID:
425                 irq_stat_addr[0] += PERF_IRQSTAT_6338_REG;
426                 irq_mask_addr[0] += PERF_IRQMASK_6338_REG;
427                 irq_stat_addr[1] = 0;
428                 irq_mask_addr[1] = 0;
429                 irq_bits = 32;
430                 ext_irq_count = 4;
431                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
432                 break;
433         case BCM6345_CPU_ID:
434                 irq_stat_addr[0] += PERF_IRQSTAT_6345_REG;
435                 irq_mask_addr[0] += PERF_IRQMASK_6345_REG;
436                 irq_stat_addr[1] = 0;
437                 irq_mask_addr[1] = 0;
438                 irq_bits = 32;
439                 ext_irq_count = 4;
440                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
441                 break;
442         case BCM6348_CPU_ID:
443                 irq_stat_addr[0] += PERF_IRQSTAT_6348_REG;
444                 irq_mask_addr[0] += PERF_IRQMASK_6348_REG;
445                 irq_stat_addr[1] = 0;
446                 irq_mask_addr[1] = 0;
447                 irq_bits = 32;
448                 ext_irq_count = 4;
449                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
450                 break;
451         case BCM6358_CPU_ID:
452                 irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0);
453                 irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0);
454                 irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1);
455                 irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1);
456                 irq_bits = 32;
457                 ext_irq_count = 4;
458                 is_ext_irq_cascaded = 1;
459                 ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
460                 ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
461                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
462                 break;
463         case BCM6362_CPU_ID:
464                 irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0);
465                 irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0);
466                 irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1);
467                 irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1);
468                 irq_bits = 64;
469                 ext_irq_count = 4;
470                 is_ext_irq_cascaded = 1;
471                 ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
472                 ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
473                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
474                 break;
475         case BCM6368_CPU_ID:
476                 irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0);
477                 irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0);
478                 irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1);
479                 irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1);
480                 irq_bits = 64;
481                 ext_irq_count = 6;
482                 is_ext_irq_cascaded = 1;
483                 ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
484                 ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
485                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
486                 ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
487                 break;
488         default:
489                 BUG();
490         }
491
492         if (irq_bits == 32) {
493                 dispatch_internal = __dispatch_internal_32;
494                 internal_irq_mask = __internal_irq_mask_32;
495                 internal_irq_unmask = __internal_irq_unmask_32;
496         } else {
497                 dispatch_internal = __dispatch_internal_64;
498                 internal_irq_mask = __internal_irq_mask_64;
499                 internal_irq_unmask = __internal_irq_unmask_64;
500         }
501 }
502
503 void __init arch_init_irq(void)
504 {
505         int i;
506
507         bcm63xx_init_irq();
508         mips_cpu_irq_init();
509         for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
510                 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
511                                          handle_level_irq);
512
513         for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
514                 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
515                                          handle_edge_irq);
516
517         if (!is_ext_irq_cascaded) {
518                 for (i = 3; i < 3 + ext_irq_count; ++i)
519                         setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
520         }
521
522         setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
523 #ifdef CONFIG_SMP
524         if (is_ext_irq_cascaded)
525                 setup_irq(MIPS_CPU_IRQ_BASE + 3, &cpu_ip3_cascade_action);
526 #endif
527 }