ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / arch / arm / mach-iop3xx / iq80310-irq.c
1 /*
2  * linux/arch/arm/mach-iop3xx/iq80310-irq.c
3  *
4  * IRQ hadling/demuxing for IQ80310 board
5  *
6  * Author:  Nicolas Pitre
7  * Copyright:   (C) 2001 MontaVista Software Inc.
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License version 2 as
11  * published by the Free Software Foundation.
12  *
13  * 2.4.7-rmk1-iop310.1
14  *     Moved demux from asm to C - DS
15  *     Fixes for various revision boards - DS
16  */
17 #include <linux/init.h>
18 #include <linux/list.h>
19
20 #include <asm/irq.h>
21 #include <asm/mach/irq.h>
22 #include <asm/hardware.h>
23 #include <asm/system.h>
24
25 #include <asm/mach-types.h>
26
27 extern void iop310_init_irq(void);
28 extern void iop310_irq_demux(unsigned int, struct irqdesc *, struct pt_regs *);
29
30 static void iq80310_irq_mask(unsigned int irq)
31 {
32         *(volatile char *)IQ80310_INT_MASK |= (1 << (irq - IQ80310_IRQ_OFS));
33 }
34
35 static void iq80310_irq_unmask(unsigned int irq)
36 {
37         *(volatile char *)IQ80310_INT_MASK &= ~(1 << (irq - IQ80310_IRQ_OFS));
38 }
39
40 static struct irqchip iq80310_irq_chip = {
41         .ack    = iq80310_irq_mask,
42         .mask   = iq80310_irq_mask,
43         .unmask = iq80310_irq_unmask,
44 };
45
46 extern struct irqchip ext_chip;
47
48 static void
49 iq80310_cpld_irq_handler(unsigned int irq, struct irqdesc *desc,
50                          struct pt_regs *regs)
51 {
52         unsigned int irq_stat = *(volatile u8*)IQ80310_INT_STAT;
53         unsigned int irq_mask = *(volatile u8*)IQ80310_INT_MASK;
54         unsigned int i, handled = 0;
55         struct irqdesc *d;
56
57         desc->chip->ack(irq);
58
59         /*
60          * Mask out the interrupts which aren't enabled.
61          */
62         irq_stat &= 0x1f & ~irq_mask;
63
64         /*
65          * Test each IQ80310 CPLD interrupt
66          */
67         for (i = IRQ_IQ80310_TIMER, d = irq_desc + IRQ_IQ80310_TIMER;
68              irq_stat; i++, d++, irq_stat >>= 1)
69                 if (irq_stat & 1) {
70                         d->handle(i, d, regs);
71                         handled++;
72                 }
73
74         /*
75          * If running on a board later than REV D.1, we can
76          * decode the PCI interrupt status.
77          */
78         if (system_rev) {
79                 irq_stat = *((volatile u8*)IQ80310_PCI_INT_STAT) & 7;
80
81                 for (i = IRQ_IQ80310_INTA, d = irq_desc + IRQ_IQ80310_INTA;
82                      irq_stat; i++, d++, irq_stat >>= 1)
83                         if (irq_stat & 0x1) {
84                                 d->handle(i, d, regs);
85                                 handled++;
86                         }
87         }
88
89         /*
90          * If on a REV D.1 or lower board, we just assumed INTA
91          * since PCI is not routed, and it may actually be an
92          * on-chip interrupt.
93          *
94          * Note that we're giving on-chip interrupts slightly
95          * higher priority than PCI by handling them first.
96          *
97          * On boards later than REV D.1, if we didn't read a
98          * CPLD interrupt, we assume it's from a device on the
99          * chipset itself.
100          */
101         if (system_rev == 0 || handled == 0)
102                 iop310_irq_demux(irq, desc, regs);
103
104         desc->chip->unmask(irq);
105 }
106
107 void __init iq80310_init_irq(void)
108 {
109         volatile char *mask = (volatile char *)IQ80310_INT_MASK;
110         unsigned int i;
111
112         iop310_init_irq();
113
114         /*
115          * Setup PIRSR to route PCI interrupts into xs80200
116          */
117         *IOP310_PIRSR = 0xff;
118
119         /*
120          * Setup the IRQs in the FE820000/FE860000 registers
121          */
122         for (i = IQ80310_IRQ_OFS; i <= IRQ_IQ80310_INTD; i++) {
123                 set_irq_chip(i, &iq80310_irq_chip);
124                 set_irq_handler(i, do_level_IRQ);
125                 set_irq_flags(i, IRQF_VALID | IRQF_PROBE);
126         }
127
128         /*
129          * Setup the PCI IRQs
130          */
131         for (i = IRQ_IQ80310_INTA; i < IRQ_IQ80310_INTC; i++) {
132                 set_irq_chip(i, &ext_chip);
133                 set_irq_handler(i, do_level_IRQ);
134                 set_irq_flags(i, IRQF_VALID);
135         }
136
137         *mask = 0xff;  /* mask all sources */
138
139         set_irq_chained_handler(IRQ_XS80200_EXTIRQ,
140                                 &iq80310_cpld_irq_handler);
141 }