]> git.karo-electronics.de Git - karo-tx-linux.git/blob - arch/mips/bcm63xx/irq.c
MIPS: BCM63xx: Protect irq register accesses
[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                                                                         \
106         spin_lock_irqsave(&ipic_lock, flags);                           \
107         val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32));          \
108         val &= ~(1 << bit);                                             \
109         bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32));          \
110         spin_unlock_irqrestore(&ipic_lock, flags);                      \
111 }                                                                       \
112                                                                         \
113 static void __internal_irq_unmask_##width(unsigned int irq)             \
114 {                                                                       \
115         u32 val;                                                        \
116         unsigned reg = (irq / 32) ^ (width/32 - 1);                     \
117         unsigned bit = irq & 0x1f;                                      \
118         unsigned long flags;                                            \
119                                                                         \
120         spin_lock_irqsave(&ipic_lock, flags);                           \
121         val = bcm_readl(irq_mask_addr[0] + reg * sizeof(u32));          \
122         val |= (1 << bit);                                              \
123         bcm_writel(val, irq_mask_addr[0] + reg * sizeof(u32));          \
124         spin_unlock_irqrestore(&ipic_lock, flags);                      \
125 }
126
127 BUILD_IPIC_INTERNAL(32);
128 BUILD_IPIC_INTERNAL(64);
129
130 asmlinkage void plat_irq_dispatch(void)
131 {
132         u32 cause;
133
134         do {
135                 cause = read_c0_cause() & read_c0_status() & ST0_IM;
136
137                 if (!cause)
138                         break;
139
140                 if (cause & CAUSEF_IP7)
141                         do_IRQ(7);
142                 if (cause & CAUSEF_IP0)
143                         do_IRQ(0);
144                 if (cause & CAUSEF_IP1)
145                         do_IRQ(1);
146                 if (cause & CAUSEF_IP2)
147                         dispatch_internal(0);
148                 if (!is_ext_irq_cascaded) {
149                         if (cause & CAUSEF_IP3)
150                                 do_IRQ(IRQ_EXT_0);
151                         if (cause & CAUSEF_IP4)
152                                 do_IRQ(IRQ_EXT_1);
153                         if (cause & CAUSEF_IP5)
154                                 do_IRQ(IRQ_EXT_2);
155                         if (cause & CAUSEF_IP6)
156                                 do_IRQ(IRQ_EXT_3);
157                 }
158         } while (1);
159 }
160
161 /*
162  * internal IRQs operations: only mask/unmask on PERF irq mask
163  * register.
164  */
165 static void bcm63xx_internal_irq_mask(struct irq_data *d)
166 {
167         internal_irq_mask(d->irq - IRQ_INTERNAL_BASE);
168 }
169
170 static void bcm63xx_internal_irq_unmask(struct irq_data *d)
171 {
172         internal_irq_unmask(d->irq - IRQ_INTERNAL_BASE);
173 }
174
175 /*
176  * external IRQs operations: mask/unmask and clear on PERF external
177  * irq control register.
178  */
179 static void bcm63xx_external_irq_mask(struct irq_data *d)
180 {
181         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
182         u32 reg, regaddr;
183         unsigned long flags;
184
185         regaddr = get_ext_irq_perf_reg(irq);
186         spin_lock_irqsave(&epic_lock, flags);
187         reg = bcm_perf_readl(regaddr);
188
189         if (BCMCPU_IS_6348())
190                 reg &= ~EXTIRQ_CFG_MASK_6348(irq % 4);
191         else
192                 reg &= ~EXTIRQ_CFG_MASK(irq % 4);
193
194         bcm_perf_writel(reg, regaddr);
195         spin_unlock_irqrestore(&epic_lock, flags);
196
197         if (is_ext_irq_cascaded)
198                 internal_irq_mask(irq + ext_irq_start);
199 }
200
201 static void bcm63xx_external_irq_unmask(struct irq_data *d)
202 {
203         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
204         u32 reg, regaddr;
205         unsigned long flags;
206
207         regaddr = get_ext_irq_perf_reg(irq);
208         spin_lock_irqsave(&epic_lock, flags);
209         reg = bcm_perf_readl(regaddr);
210
211         if (BCMCPU_IS_6348())
212                 reg |= EXTIRQ_CFG_MASK_6348(irq % 4);
213         else
214                 reg |= EXTIRQ_CFG_MASK(irq % 4);
215
216         bcm_perf_writel(reg, regaddr);
217         spin_unlock_irqrestore(&epic_lock, flags);
218
219         if (is_ext_irq_cascaded)
220                 internal_irq_unmask(irq + ext_irq_start);
221 }
222
223 static void bcm63xx_external_irq_clear(struct irq_data *d)
224 {
225         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
226         u32 reg, regaddr;
227         unsigned long flags;
228
229         regaddr = get_ext_irq_perf_reg(irq);
230         spin_lock_irqsave(&epic_lock, flags);
231         reg = bcm_perf_readl(regaddr);
232
233         if (BCMCPU_IS_6348())
234                 reg |= EXTIRQ_CFG_CLEAR_6348(irq % 4);
235         else
236                 reg |= EXTIRQ_CFG_CLEAR(irq % 4);
237
238         bcm_perf_writel(reg, regaddr);
239         spin_unlock_irqrestore(&epic_lock, flags);
240 }
241
242 static int bcm63xx_external_irq_set_type(struct irq_data *d,
243                                          unsigned int flow_type)
244 {
245         unsigned int irq = d->irq - IRQ_EXTERNAL_BASE;
246         u32 reg, regaddr;
247         int levelsense, sense, bothedge;
248         unsigned long flags;
249
250         flow_type &= IRQ_TYPE_SENSE_MASK;
251
252         if (flow_type == IRQ_TYPE_NONE)
253                 flow_type = IRQ_TYPE_LEVEL_LOW;
254
255         levelsense = sense = bothedge = 0;
256         switch (flow_type) {
257         case IRQ_TYPE_EDGE_BOTH:
258                 bothedge = 1;
259                 break;
260
261         case IRQ_TYPE_EDGE_RISING:
262                 sense = 1;
263                 break;
264
265         case IRQ_TYPE_EDGE_FALLING:
266                 break;
267
268         case IRQ_TYPE_LEVEL_HIGH:
269                 levelsense = 1;
270                 sense = 1;
271                 break;
272
273         case IRQ_TYPE_LEVEL_LOW:
274                 levelsense = 1;
275                 break;
276
277         default:
278                 printk(KERN_ERR "bogus flow type combination given !\n");
279                 return -EINVAL;
280         }
281
282         regaddr = get_ext_irq_perf_reg(irq);
283         spin_lock_irqsave(&epic_lock, flags);
284         reg = bcm_perf_readl(regaddr);
285         irq %= 4;
286
287         switch (bcm63xx_get_cpu_id()) {
288         case BCM6348_CPU_ID:
289                 if (levelsense)
290                         reg |= EXTIRQ_CFG_LEVELSENSE_6348(irq);
291                 else
292                         reg &= ~EXTIRQ_CFG_LEVELSENSE_6348(irq);
293                 if (sense)
294                         reg |= EXTIRQ_CFG_SENSE_6348(irq);
295                 else
296                         reg &= ~EXTIRQ_CFG_SENSE_6348(irq);
297                 if (bothedge)
298                         reg |= EXTIRQ_CFG_BOTHEDGE_6348(irq);
299                 else
300                         reg &= ~EXTIRQ_CFG_BOTHEDGE_6348(irq);
301                 break;
302
303         case BCM3368_CPU_ID:
304         case BCM6328_CPU_ID:
305         case BCM6338_CPU_ID:
306         case BCM6345_CPU_ID:
307         case BCM6358_CPU_ID:
308         case BCM6362_CPU_ID:
309         case BCM6368_CPU_ID:
310                 if (levelsense)
311                         reg |= EXTIRQ_CFG_LEVELSENSE(irq);
312                 else
313                         reg &= ~EXTIRQ_CFG_LEVELSENSE(irq);
314                 if (sense)
315                         reg |= EXTIRQ_CFG_SENSE(irq);
316                 else
317                         reg &= ~EXTIRQ_CFG_SENSE(irq);
318                 if (bothedge)
319                         reg |= EXTIRQ_CFG_BOTHEDGE(irq);
320                 else
321                         reg &= ~EXTIRQ_CFG_BOTHEDGE(irq);
322                 break;
323         default:
324                 BUG();
325         }
326
327         bcm_perf_writel(reg, regaddr);
328         spin_unlock_irqrestore(&epic_lock, flags);
329
330         irqd_set_trigger_type(d, flow_type);
331         if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH))
332                 __irq_set_handler_locked(d->irq, handle_level_irq);
333         else
334                 __irq_set_handler_locked(d->irq, handle_edge_irq);
335
336         return IRQ_SET_MASK_OK_NOCOPY;
337 }
338
339 static struct irq_chip bcm63xx_internal_irq_chip = {
340         .name           = "bcm63xx_ipic",
341         .irq_mask       = bcm63xx_internal_irq_mask,
342         .irq_unmask     = bcm63xx_internal_irq_unmask,
343 };
344
345 static struct irq_chip bcm63xx_external_irq_chip = {
346         .name           = "bcm63xx_epic",
347         .irq_ack        = bcm63xx_external_irq_clear,
348
349         .irq_mask       = bcm63xx_external_irq_mask,
350         .irq_unmask     = bcm63xx_external_irq_unmask,
351
352         .irq_set_type   = bcm63xx_external_irq_set_type,
353 };
354
355 static struct irqaction cpu_ip2_cascade_action = {
356         .handler        = no_action,
357         .name           = "cascade_ip2",
358         .flags          = IRQF_NO_THREAD,
359 };
360
361 static struct irqaction cpu_ext_cascade_action = {
362         .handler        = no_action,
363         .name           = "cascade_extirq",
364         .flags          = IRQF_NO_THREAD,
365 };
366
367 static void bcm63xx_init_irq(void)
368 {
369         int irq_bits;
370
371         irq_stat_addr[0] = bcm63xx_regset_address(RSET_PERF);
372         irq_mask_addr[0] = bcm63xx_regset_address(RSET_PERF);
373         irq_stat_addr[1] = bcm63xx_regset_address(RSET_PERF);
374         irq_mask_addr[1] = bcm63xx_regset_address(RSET_PERF);
375
376         switch (bcm63xx_get_cpu_id()) {
377         case BCM3368_CPU_ID:
378                 irq_stat_addr[0] += PERF_IRQSTAT_3368_REG;
379                 irq_mask_addr[0] += PERF_IRQMASK_3368_REG;
380                 irq_stat_addr[1] = 0;
381                 irq_stat_addr[1] = 0;
382                 irq_bits = 32;
383                 ext_irq_count = 4;
384                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_3368;
385                 break;
386         case BCM6328_CPU_ID:
387                 irq_stat_addr[0] += PERF_IRQSTAT_6328_REG(0);
388                 irq_mask_addr[0] += PERF_IRQMASK_6328_REG(0);
389                 irq_stat_addr[1] += PERF_IRQSTAT_6328_REG(1);
390                 irq_stat_addr[1] += PERF_IRQMASK_6328_REG(1);
391                 irq_bits = 64;
392                 ext_irq_count = 4;
393                 is_ext_irq_cascaded = 1;
394                 ext_irq_start = BCM_6328_EXT_IRQ0 - IRQ_INTERNAL_BASE;
395                 ext_irq_end = BCM_6328_EXT_IRQ3 - IRQ_INTERNAL_BASE;
396                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6328;
397                 break;
398         case BCM6338_CPU_ID:
399                 irq_stat_addr[0] += PERF_IRQSTAT_6338_REG;
400                 irq_mask_addr[0] += PERF_IRQMASK_6338_REG;
401                 irq_stat_addr[1] = 0;
402                 irq_mask_addr[1] = 0;
403                 irq_bits = 32;
404                 ext_irq_count = 4;
405                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6338;
406                 break;
407         case BCM6345_CPU_ID:
408                 irq_stat_addr[0] += PERF_IRQSTAT_6345_REG;
409                 irq_mask_addr[0] += PERF_IRQMASK_6345_REG;
410                 irq_stat_addr[1] = 0;
411                 irq_mask_addr[1] = 0;
412                 irq_bits = 32;
413                 ext_irq_count = 4;
414                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6345;
415                 break;
416         case BCM6348_CPU_ID:
417                 irq_stat_addr[0] += PERF_IRQSTAT_6348_REG;
418                 irq_mask_addr[0] += PERF_IRQMASK_6348_REG;
419                 irq_stat_addr[1] = 0;
420                 irq_mask_addr[1] = 0;
421                 irq_bits = 32;
422                 ext_irq_count = 4;
423                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6348;
424                 break;
425         case BCM6358_CPU_ID:
426                 irq_stat_addr[0] += PERF_IRQSTAT_6358_REG(0);
427                 irq_mask_addr[0] += PERF_IRQMASK_6358_REG(0);
428                 irq_stat_addr[1] += PERF_IRQSTAT_6358_REG(1);
429                 irq_mask_addr[1] += PERF_IRQMASK_6358_REG(1);
430                 irq_bits = 32;
431                 ext_irq_count = 4;
432                 is_ext_irq_cascaded = 1;
433                 ext_irq_start = BCM_6358_EXT_IRQ0 - IRQ_INTERNAL_BASE;
434                 ext_irq_end = BCM_6358_EXT_IRQ3 - IRQ_INTERNAL_BASE;
435                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6358;
436                 break;
437         case BCM6362_CPU_ID:
438                 irq_stat_addr[0] += PERF_IRQSTAT_6362_REG(0);
439                 irq_mask_addr[0] += PERF_IRQMASK_6362_REG(0);
440                 irq_stat_addr[1] += PERF_IRQSTAT_6362_REG(1);
441                 irq_mask_addr[1] += PERF_IRQMASK_6362_REG(1);
442                 irq_bits = 64;
443                 ext_irq_count = 4;
444                 is_ext_irq_cascaded = 1;
445                 ext_irq_start = BCM_6362_EXT_IRQ0 - IRQ_INTERNAL_BASE;
446                 ext_irq_end = BCM_6362_EXT_IRQ3 - IRQ_INTERNAL_BASE;
447                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6362;
448                 break;
449         case BCM6368_CPU_ID:
450                 irq_stat_addr[0] += PERF_IRQSTAT_6368_REG(0);
451                 irq_mask_addr[0] += PERF_IRQMASK_6368_REG(0);
452                 irq_stat_addr[1] += PERF_IRQSTAT_6368_REG(1);
453                 irq_mask_addr[1] += PERF_IRQMASK_6368_REG(1);
454                 irq_bits = 64;
455                 ext_irq_count = 6;
456                 is_ext_irq_cascaded = 1;
457                 ext_irq_start = BCM_6368_EXT_IRQ0 - IRQ_INTERNAL_BASE;
458                 ext_irq_end = BCM_6368_EXT_IRQ5 - IRQ_INTERNAL_BASE;
459                 ext_irq_cfg_reg1 = PERF_EXTIRQ_CFG_REG_6368;
460                 ext_irq_cfg_reg2 = PERF_EXTIRQ_CFG_REG2_6368;
461                 break;
462         default:
463                 BUG();
464         }
465
466         if (irq_bits == 32) {
467                 dispatch_internal = __dispatch_internal_32;
468                 internal_irq_mask = __internal_irq_mask_32;
469                 internal_irq_unmask = __internal_irq_unmask_32;
470         } else {
471                 dispatch_internal = __dispatch_internal_64;
472                 internal_irq_mask = __internal_irq_mask_64;
473                 internal_irq_unmask = __internal_irq_unmask_64;
474         }
475 }
476
477 void __init arch_init_irq(void)
478 {
479         int i;
480
481         bcm63xx_init_irq();
482         mips_cpu_irq_init();
483         for (i = IRQ_INTERNAL_BASE; i < NR_IRQS; ++i)
484                 irq_set_chip_and_handler(i, &bcm63xx_internal_irq_chip,
485                                          handle_level_irq);
486
487         for (i = IRQ_EXTERNAL_BASE; i < IRQ_EXTERNAL_BASE + ext_irq_count; ++i)
488                 irq_set_chip_and_handler(i, &bcm63xx_external_irq_chip,
489                                          handle_edge_irq);
490
491         if (!is_ext_irq_cascaded) {
492                 for (i = 3; i < 3 + ext_irq_count; ++i)
493                         setup_irq(MIPS_CPU_IRQ_BASE + i, &cpu_ext_cascade_action);
494         }
495
496         setup_irq(MIPS_CPU_IRQ_BASE + 2, &cpu_ip2_cascade_action);
497 }