add interrupt debugging; fix bug with the AP60 interrupt handler
[openwrt.git] / target / linux / aruba-2.6 / patches / 002-irq.patch
1 diff -Nur linux-2.6.15/arch/mips/aruba/idtIRQ.S linux-2.6.15-openwrt/arch/mips/aruba/idtIRQ.S
2 --- linux-2.6.15/arch/mips/aruba/idtIRQ.S       1970-01-01 01:00:00.000000000 +0100
3 +++ linux-2.6.15-openwrt/arch/mips/aruba/idtIRQ.S       2006-01-10 00:32:32.000000000 +0100
4 @@ -0,0 +1,87 @@
5 +/**************************************************************************
6 + *
7 + *  BRIEF MODULE DESCRIPTION
8 + *     Intterrupt dispatcher code for IDT boards
9 + *
10 + *  Copyright 2004 IDT Inc. (rischelp@idt.com)
11 + *         
12 + *  This program is free software; you can redistribute  it and/or modify it
13 + *  under  the terms of  the GNU General  Public License as published by the
14 + *  Free Software Foundation;  either version 2 of the  License, or (at your
15 + *  option) any later version.
16 + *
17 + *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
18 + *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
19 + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
20 + *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
21 + *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 + *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
23 + *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
24 + *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
25 + *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 + *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 + *
28 + *  You should have received a copy of the  GNU General Public License along
29 + *  with this program; if not, write  to the Free Software Foundation, Inc.,
30 + *  675 Mass Ave, Cambridge, MA 02139, USA.
31 + *
32 + *
33 + **************************************************************************
34 + * May 2004 rkt, neb
35 + *
36 + * Initial Release
37 + *
38 + * 
39 + *
40 + **************************************************************************
41 + */
42 +               
43 +       
44 +#include <asm/asm.h>
45 +#include <asm/mipsregs.h>
46 +#include <asm/regdef.h>
47 +#include <asm/stackframe.h>
48 +
49 +       .text
50 +       .set    noreorder
51 +       .set    noat
52 +       .align  5
53 +       NESTED(idtIRQ, PT_SIZE, sp)
54 +       .set noat
55 +       SAVE_ALL
56 +       CLI
57 +
58 +       .set    at
59 +       .set    noreorder
60 +
61 +       /* Get the pending interrupts */
62 +       mfc0    t0, CP0_CAUSE
63 +       nop
64 +                        
65 +       /* Isolate the allowed ones by anding the irq mask */
66 +       mfc0    t2, CP0_STATUS
67 +       move    a1, sp          /* need a nop here, hence we anticipate */
68 +       andi    t0, CAUSEF_IP
69 +       and     t0, t2
70 +                                                                 
71 +       /* check for r4k counter/timer IRQ. */
72 +       
73 +       andi    t1, t0, CAUSEF_IP7
74 +       beqz    t1, 1f
75 +       nop
76 +
77 +       jal     aruba_timer_interrupt   
78 +
79 +       li      a0, 7
80 +
81 +       j       ret_from_irq
82 +       nop
83 +1:
84 +       jal     aruba_irqdispatch
85 +       move    a0, t0
86 +       j       ret_from_irq
87 +       nop
88 +
89 +       END(idtIRQ)
90 +
91 +
92 diff -Nur linux-2.6.15/arch/mips/aruba/irq.c linux-2.6.15-openwrt/arch/mips/aruba/irq.c
93 --- linux-2.6.15/arch/mips/aruba/irq.c  1970-01-01 01:00:00.000000000 +0100
94 +++ linux-2.6.15-openwrt/arch/mips/aruba/irq.c  2006-01-10 00:32:32.000000000 +0100
95 @@ -0,0 +1,447 @@
96 +/**************************************************************************
97 + *
98 + *  BRIEF MODULE DESCRIPTION
99 + *     Interrupt routines for IDT EB434 boards / Atheros boards
100 + *     Modified by Aruba Networks
101 + *
102 + *  Copyright 2004 IDT Inc. (rischelp@idt.com)
103 + *         
104 + *  This program is free software; you can redistribute  it and/or modify it
105 + *  under  the terms of  the GNU General  Public License as published by the
106 + *  Free Software Foundation;  either version 2 of the  License, or (at your
107 + *  option) any later version.
108 + *
109 + *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
110 + *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
111 + *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
112 + *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
113 + *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
114 + *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
115 + *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
116 + *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
117 + *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
118 + *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
119 + *
120 + *  You should have received a copy of the  GNU General Public License along
121 + *  with this program; if not, write  to the Free Software Foundation, Inc.,
122 + *  675 Mass Ave, Cambridge, MA 02139, USA.
123 + *
124 + *
125 + **************************************************************************
126 + * May 2004 rkt, neb
127 + *
128 + * Initial Release
129 + *
130 + * 
131 + *
132 + **************************************************************************
133 + */
134 +
135 +#include <linux/errno.h>
136 +#include <linux/init.h>
137 +#include <linux/kernel_stat.h>
138 +#include <linux/module.h>
139 +#include <linux/signal.h>
140 +#include <linux/sched.h>
141 +#include <linux/types.h>
142 +#include <linux/interrupt.h>
143 +#include <linux/ioport.h>
144 +#include <linux/timex.h>
145 +#include <linux/slab.h>
146 +#include <linux/random.h>
147 +#include <linux/delay.h>
148 +
149 +#include <asm/bitops.h>
150 +#include <asm/bootinfo.h>
151 +#include <asm/io.h>
152 +#include <asm/mipsregs.h>
153 +#include <asm/system.h>
154 +#include <asm/idt-boards/rc32434/rc32434.h>
155 +#include <asm/idt-boards/rc32434/rc32434_gpio.h>
156 +
157 +#include <asm/irq.h>
158 +
159 +#undef DEBUG_IRQ
160 +#ifdef DEBUG_IRQ
161 +/* note: prints function name for you */
162 +#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
163 +#else
164 +#define DPRINTK(fmt, args...)
165 +#endif
166 +
167 +extern asmlinkage void idtIRQ(void);
168 +static unsigned int startup_irq(unsigned int irq);
169 +static void end_irq(unsigned int irq_nr);
170 +static void mask_and_ack_irq(unsigned int irq_nr);
171 +static void aruba_enable_irq(unsigned int irq_nr);
172 +static void aruba_disable_irq(unsigned int irq_nr);
173 +
174 +extern void __init init_generic_irq(void);
175 +
176 +typedef struct {
177 +       u32 mask;
178 +       volatile u32 *base_addr;
179 +} intr_group_t;
180 +
181 +static const intr_group_t intr_group_merlot[NUM_INTR_GROUPS] = {
182 +       {0xffffffff, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 0)},
183 +};
184 +
185 +#define READ_PEND_MERLOT(base) (*((volatile unsigned long *)(0xbc003010)))
186 +#define READ_MASK_MERLOT(base) (*((volatile unsigned long *)(0xbc003010 + 4)))
187 +#define WRITE_MASK_MERLOT(base, val) ((*((volatile unsigned long *)((0xbc003010) + 4))) = (val))
188 +
189 +static const intr_group_t intr_group_muscat[NUM_INTR_GROUPS] = {
190 +       {0x0000efff, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET)},
191 +       {0x00001fff, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET)},
192 +       {0x00000007, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET)},
193 +       {0x0003ffff, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET)},
194 +       {0xffffffff, (u32 *) KSEG1ADDR(IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET)}
195 +};
196 +
197 +#define READ_PEND_MUSCAT(base) (*(base))
198 +#define READ_MASK_MUSCAT(base) (*(base + 2))
199 +#define WRITE_MASK_MUSCAT(base, val) (*(base + 2) = (val))
200 +
201 +static inline int irq_to_group(unsigned int irq_nr)
202 +{
203 +       switch (mips_machtype) {
204 +               case MACH_ARUBA_AP70:
205 +                       return ((irq_nr - GROUP0_IRQ_BASE) >> 5);
206 +               case MACH_ARUBA_AP65:
207 +               case MACH_ARUBA_AP60:
208 +               default:
209 +                       return 0;
210 +       }
211 +}
212 +
213 +static inline int group_to_ip(unsigned int group)
214 +{
215 +       switch (mips_machtype) {
216 +               case MACH_ARUBA_AP70:
217 +                       return group + 2;
218 +               case MACH_ARUBA_AP65:
219 +               case MACH_ARUBA_AP60:
220 +               default:
221 +                       return 6;
222 +       }
223 +}
224 +
225 +static inline void enable_local_irq(unsigned int ip)
226 +{
227 +       int ipnum = 0x100 << ip;
228 +       clear_c0_cause(ipnum);
229 +       set_c0_status(ipnum);
230 +}
231 +
232 +static inline void disable_local_irq(unsigned int ip)
233 +{
234 +       int ipnum = 0x100 << ip;
235 +       clear_c0_status(ipnum);
236 +}
237 +
238 +static inline void ack_local_irq(unsigned int ip)
239 +{
240 +       int ipnum = 0x100 << ip;
241 +       clear_c0_cause(ipnum);
242 +}
243 +
244 +static void aruba_enable_irq(unsigned int irq_nr)
245 +{
246 +       unsigned long flags;
247 +       int ip = irq_nr - GROUP0_IRQ_BASE;
248 +       unsigned int group, intr_bit;
249 +       volatile unsigned int *addr;
250 +
251 +
252 +       local_irq_save(flags);
253 +       
254 +       if (ip < 0) {
255 +               enable_local_irq(irq_nr);
256 +       } else {
257 +               // calculate group
258 +               switch (mips_machtype) {
259 +                       case MACH_ARUBA_AP70:
260 +                               group = ip >> 5;
261 +                               break;
262 +                       case MACH_ARUBA_AP65:
263 +                       case MACH_ARUBA_AP60:
264 +                       default:
265 +                               group = 0;
266 +                               break;
267 +               }
268 +
269 +               // calc interrupt bit within group
270 +               ip -= (group << 5);
271 +               intr_bit = 1 << ip;
272 +
273 +               // first enable the IP mapped to this IRQ
274 +               enable_local_irq(group_to_ip(group));
275 +
276 +               switch (mips_machtype) {
277 +                       case MACH_ARUBA_AP70:
278 +                               addr = intr_group_muscat[group].base_addr;
279 +                               WRITE_MASK_MUSCAT(addr, READ_MASK_MUSCAT(addr) & ~intr_bit);
280 +                               break;
281 +                       case MACH_ARUBA_AP65:
282 +                       case MACH_ARUBA_AP60:
283 +                       default:
284 +                               addr = intr_group_merlot[group].base_addr;
285 +                               WRITE_MASK_MERLOT(addr, READ_MASK_MERLOT(addr) | intr_bit);
286 +                               break;
287 +               }
288 +       }
289 +
290 +       local_irq_restore(flags);
291 +
292 +}
293 +
294 +static void aruba_disable_irq(unsigned int irq_nr)
295 +{
296 +       unsigned long flags;
297 +       int ip = irq_nr - GROUP0_IRQ_BASE;
298 +       unsigned int group, intr_bit, mask;
299 +       volatile unsigned int *addr;
300 +
301 +       local_irq_save(flags);
302 +
303 +       if (ip < 0) {
304 +               disable_local_irq(irq_nr);
305 +       } else {
306 +               // calculate group
307 +               switch (mips_machtype) {
308 +                       case MACH_ARUBA_AP70:
309 +                               group = ip >> 5;
310 +                               break;
311 +                       case MACH_ARUBA_AP65:
312 +                       case MACH_ARUBA_AP60:
313 +                       default:
314 +                               group = 0;
315 +                               break;
316 +               }
317 +
318 +               // calc interrupt bit within group
319 +               ip -= group << 5;
320 +               intr_bit = 1 << ip;
321 +
322 +               switch (mips_machtype) {
323 +                       case MACH_ARUBA_AP70:
324 +                               addr = intr_group_muscat[group].base_addr;
325 +                               // mask intr within group
326 +                               mask = READ_MASK_MUSCAT(addr);
327 +                               mask |= intr_bit;
328 +                               WRITE_MASK_MUSCAT(addr, mask);
329 +       
330 +                               /*
331 +                                  if there are no more interrupts enabled in this
332 +                                  group, disable corresponding IP
333 +                                */
334 +                               if (mask == intr_group_muscat[group].mask)
335 +                                       disable_local_irq(group_to_ip(group));
336 +                               break;
337 +                       case MACH_ARUBA_AP65:
338 +                       case MACH_ARUBA_AP60:
339 +                       default:
340 +                               addr = intr_group_merlot[group].base_addr;
341 +                               // mask intr within group
342 +                               mask = READ_MASK_MERLOT(addr);
343 +                               mask &= ~intr_bit;
344 +                               WRITE_MASK_MERLOT(addr, mask);
345 +                               if (READ_MASK_MERLOT(addr))
346 +                                       disable_local_irq(group_to_ip(group));
347 +                               break;
348 +               }
349 +       }
350 +
351 +       local_irq_restore(flags);
352 +
353 +}
354 +
355 +static unsigned int startup_irq(unsigned int irq_nr)
356 +{
357 +       aruba_enable_irq(irq_nr);
358 +       return 0;
359 +}
360 +
361 +static void shutdown_irq(unsigned int irq_nr)
362 +{
363 +       aruba_disable_irq(irq_nr);
364 +       return;
365 +}
366 +
367 +static void mask_and_ack_irq(unsigned int irq_nr)
368 +{
369 +       aruba_disable_irq(irq_nr);
370 +       ack_local_irq(group_to_ip(irq_to_group(irq_nr)));
371 +}
372 +
373 +static void end_irq(unsigned int irq_nr)
374 +{
375 +
376 +       unsigned long flags;
377 +       int ip = irq_nr - GROUP0_IRQ_BASE;
378 +       unsigned int intr_bit, group;
379 +       volatile unsigned int *addr;
380 +
381 +
382 +       local_irq_save(flags);
383 +       if (irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS)) {
384 +               printk("warning: end_irq %d did not enable (%x)\n",
385 +                      irq_nr, irq_desc[irq_nr].status);
386 +       } else if (ip<0) {
387 +               enable_local_irq(irq_nr);
388 +       } else {
389 +
390 +               switch (mips_machtype) {
391 +                       case MACH_ARUBA_AP70:
392 +                               if (irq_nr == GROUP4_IRQ_BASE + 9)       idt_gpio->gpioistat &= 0xfffffdff;
393 +                               else if (irq_nr == GROUP4_IRQ_BASE + 10) idt_gpio->gpioistat &= 0xfffffbff;
394 +                               else if (irq_nr == GROUP4_IRQ_BASE + 11) idt_gpio->gpioistat &= 0xfffff7ff;
395 +                               else if (irq_nr == GROUP4_IRQ_BASE + 12) idt_gpio->gpioistat &= 0xffffefff;
396 +               
397 +                               group = ip >> 5;
398 +                       
399 +                               // calc interrupt bit within group
400 +                               ip -= (group << 5);
401 +                               intr_bit = 1 << ip;
402 +               
403 +                               // first enable the IP mapped to this IRQ
404 +                               enable_local_irq(group_to_ip(group));
405 +               
406 +                               addr = intr_group_muscat[group].base_addr;
407 +                               // unmask intr within group
408 +                               WRITE_MASK_MUSCAT(addr, READ_MASK_MUSCAT(addr) & ~intr_bit);
409 +                               break;
410 +
411 +                       case MACH_ARUBA_AP65:
412 +                               case MACH_ARUBA_AP60:
413 +                               group = 0;
414 +       
415 +                               // calc interrupt bit within group
416 +                               intr_bit = 1 << ip;
417 +       
418 +                               // first enable the IP mapped to this IRQ
419 +                               enable_local_irq(group_to_ip(group));
420 +       
421 +                               addr = intr_group_merlot[group].base_addr;
422 +                               // unmask intr within group
423 +                               WRITE_MASK_MERLOT(addr, READ_MASK_MERLOT(addr) | intr_bit);
424 +                               break;
425 +               }
426 +       }
427 +       local_irq_restore(flags);
428 +}
429 +
430 +static struct hw_interrupt_type aruba_irq_type = {
431 +       .typename = "IDT434",
432 +       .startup = startup_irq,
433 +       .shutdown = shutdown_irq,
434 +       .enable = aruba_enable_irq,
435 +       .disable = aruba_disable_irq,
436 +       .ack = mask_and_ack_irq,
437 +       .end = end_irq,
438 +};
439 +
440 +void __init arch_init_irq(void)
441 +{
442 +       int i;
443 +       printk("Initializing IRQ's: %d out of %d\n", RC32434_NR_IRQS, NR_IRQS);
444 +       memset(irq_desc, 0, sizeof(irq_desc));
445 +       set_except_vector(0, idtIRQ);
446 +
447 +
448 +       set_c0_status(0xFF00);
449 +
450 +       for (i = 0; i < RC32434_NR_IRQS; i++) {
451 +               irq_desc[i].status = IRQ_DISABLED;
452 +               irq_desc[i].action = NULL;
453 +               irq_desc[i].depth = 1;
454 +               irq_desc[i].handler = &aruba_irq_type;
455 +               spin_lock_init(&irq_desc[i].lock);
456 +       }
457 +
458 +       switch (mips_machtype) {
459 +               case MACH_ARUBA_AP70:
460 +                       break;
461 +               case MACH_ARUBA_AP65:
462 +               case MACH_ARUBA_AP60:
463 +               default:
464 +                       WRITE_MASK_MERLOT(intr_group_merlot[0].base_addr, 0);
465 +                       *((volatile unsigned long *)0xbc003014) = 0x10;
466 +                       break;
467 +       }
468 +}
469 +
470 +/* Main Interrupt dispatcher */
471 +void aruba_irqdispatch(unsigned long cp0_cause, struct pt_regs *regs)
472 +{
473 +       unsigned int pend, group, ip;
474 +       volatile unsigned int *addr;
475 +
476 +       if(cp0_cause == 0) {
477 +               printk("INTERRUPT(S) FIRED WHILE MASKED\n");
478 +
479 +               // debuging use -- figure out which interrupt(s) fired
480 +               cp0_cause = read_c0_cause() & CAUSEF_IP;
481 +               while (cp0_cause) {
482 +                       unsigned long intr_bit;
483 +                       unsigned int irq_nr;
484 +                       intr_bit = (31 - rc32434_clz(cp0_cause));
485 +                       irq_nr = intr_bit - GROUP0_IRQ_BASE;
486 +                       printk(" ---> MASKED IRQ %d\n",irq_nr);
487 +                       cp0_cause &= ~(1 << intr_bit);
488 +               }
489 +
490 +               return;
491 +       }
492 +
493 +       
494 +       switch (mips_machtype) {
495 +               case MACH_ARUBA_AP70:
496 +                       if ((ip = (cp0_cause & 0x7c00))) {
497 +                               group = 21 - rc32434_clz(ip);
498 +               
499 +                               addr = intr_group_muscat[group].base_addr;
500 +               
501 +                               pend = READ_PEND_MUSCAT(addr);
502 +                               pend &= ~READ_MASK_MUSCAT(addr);        // only unmasked interrupts
503 +                               pend = 39 - rc32434_clz(pend);
504 +                               do_IRQ((group << 5) + pend, regs);
505 +                       }
506 +                       break;
507 +               case MACH_ARUBA_AP65:
508 +               case MACH_ARUBA_AP60:
509 +               default:
510 +                       if (cp0_cause & 0x4000) { // 1 << (8 +6) == irq 6
511 +                               // Misc Interrupt
512 +                               group = 0;
513 +                               addr = intr_group_merlot[group].base_addr;
514 +                               pend = READ_PEND_MERLOT(addr);
515 +                               pend &= READ_MASK_MERLOT(addr); // only unmasked interrupts
516 +                               /* handle one misc interrupt at a time */
517 +                               while (pend) {
518 +                                       unsigned long intr_bit;
519 +                                       unsigned int irq_nr;
520 +
521 +                                       intr_bit = (31 - rc32434_clz(pend));
522 +                                       irq_nr = intr_bit + GROUP0_IRQ_BASE;
523 +
524 +                                       do_IRQ(irq_nr, regs);
525 +                                       pend &= ~(1 << intr_bit);
526 +                               }
527 +                       }
528 +                       if (cp0_cause & 0x3c00) { // irq 2-5
529 +                               while (cp0_cause) {
530 +                                       unsigned long intr_bit;
531 +                                       unsigned int irq_nr;
532 +
533 +                                       intr_bit = (31 - rc32434_clz(cp0_cause));
534 +                                       irq_nr = intr_bit - GROUP0_IRQ_BASE;
535 +
536 +                                       do_IRQ(irq_nr, regs);
537 +                                       cp0_cause &= ~(1 << intr_bit);
538 +                               }
539 +                       }
540 +                       break;
541 +       }
542 +}