ce5ad41c34a2d560d73b0ece52f8f12bfacdf802
[bcm963xx.git] / kernel / linux / arch / mips / brcm-boards / bcm963xx / irq.c
1 /*
2 <:copyright-gpl 
3  Copyright 2002 Broadcom Corp. All Rights Reserved. 
4  
5  This program is free software; you can distribute it and/or modify it 
6  under the terms of the GNU General Public License (Version 2) as 
7  published by the Free Software Foundation. 
8  
9  This program is distributed in the hope it will be useful, but WITHOUT 
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
11  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
12  for more details. 
13  
14  You should have received a copy of the GNU General Public License along 
15  with this program; if not, write to the Free Software Foundation, Inc., 
16  59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 
17 :>
18 */
19 /*
20  * Interrupt control functions for Broadcom 963xx MIPS boards
21  */
22
23 #include <asm/atomic.h>
24
25 #include <linux/delay.h>
26 #include <linux/init.h>
27 #include <linux/ioport.h>
28 #include <linux/irq.h>
29 #include <linux/interrupt.h>
30 #include <linux/kernel.h>
31 #include <linux/slab.h>
32 #include <linux/module.h>
33
34 #include <asm/irq.h>
35 #include <asm/mipsregs.h>
36 #include <asm/addrspace.h>
37 #include <asm/signal.h>
38 #include <bcm_map_part.h>
39 #include <bcm_intr.h>
40
41 extern asmlinkage unsigned int do_IRQ(int irq, struct pt_regs *regs);
42
43 static void irq_dispatch_int(struct pt_regs *regs)
44 {
45     unsigned int pendingIrqs;
46     static unsigned int irqBit;
47     static unsigned int isrNumber = 31;
48
49     pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
50     if (!pendingIrqs) {
51         return;
52     }
53
54     while (1) {
55         irqBit <<= 1;
56         isrNumber++;
57         if (isrNumber == 32) {
58                 isrNumber = 0;
59                 irqBit = 0x1;
60         }
61         if (pendingIrqs & irqBit) {
62                 PERF->IrqMask &= ~irqBit; // mask
63                 do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET, regs);
64                 break;
65         }
66     }
67 }
68
69 static void irq_dispatch_ext(uint32 irq, struct pt_regs *regs)
70 {
71     if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
72         printk("**** Ext IRQ mask. Should not dispatch ****\n");
73     }
74     /* disable and clear interrupt in the controller */
75     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
76     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
77     do_IRQ(irq, regs);
78 }
79
80 void brcm_irq_dispatch(struct pt_regs *regs)
81 {
82     u32 cause;
83     while((cause = (read_c0_cause()& CAUSEF_IP))) {
84         if (cause & CAUSEF_IP7)
85                 do_IRQ(MIPS_TIMER_INT, regs);
86         else if (cause & CAUSEF_IP2)
87                 irq_dispatch_int(regs);
88         else if (cause & CAUSEF_IP3)
89                 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0, regs);
90         else if (cause & CAUSEF_IP4)
91                 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1, regs);
92         else if (cause & CAUSEF_IP5)
93                 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2, regs);
94         else if (cause & CAUSEF_IP6)
95                 irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3, regs);
96         cli();
97     }
98 }
99
100
101 void enable_brcm_irq(unsigned int irq)
102 {
103     unsigned long flags;
104
105     local_irq_save(flags);
106     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
107         PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
108     }
109     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
110     /* enable and clear interrupt in the controller */
111         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
112         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
113     }
114     local_irq_restore(flags);
115 }
116
117 void disable_brcm_irq(unsigned int irq)
118 {
119     unsigned long flags;
120
121     local_irq_save(flags);
122     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
123         PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
124     }
125     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
126     /* disable interrupt in the controller */
127         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
128     }
129     local_irq_restore(flags);
130 }
131
132 void ack_brcm_irq(unsigned int irq)
133 {
134     /* Already done in brcm_irq_dispatch */
135 }
136
137 unsigned int startup_brcm_irq(unsigned int irq)
138 {
139     enable_brcm_irq(irq);
140
141     return 0; /* never anything pending */
142 }
143
144 unsigned int startup_brcm_none(unsigned int irq)
145 {
146     return 0;
147 }
148
149 void end_brcm_irq(unsigned int irq)
150 {
151     if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
152         enable_brcm_irq(irq);
153 }
154
155 void end_brcm_none(unsigned int irq)
156 {
157 }
158
159 #define ALLINTS_NOTIMER (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
160
161 static void __init brcm_irq_setup(void)
162 {
163         extern asmlinkage void brcmIRQ(void);
164
165         clear_c0_status(ST0_BEV);
166         set_except_vector(0, brcmIRQ);
167         change_c0_status(ST0_IM, ALLINTS_NOTIMER);
168
169 #ifdef CONFIG_REMOTE_DEBUG
170         rs_kgdb_hook(0);
171 #endif
172 }
173
174 static struct hw_interrupt_type brcm_irq_type = {
175     .typename   = "MIPS",
176     .startup    = startup_brcm_irq,
177     .shutdown   = disable_brcm_irq,
178     .enable     = enable_brcm_irq,
179     .disable    = disable_brcm_irq,
180     .ack        = ack_brcm_irq,
181     .end        = end_brcm_irq,
182     .set_affinity = NULL
183 };
184
185 static struct hw_interrupt_type brcm_irq_no_end_type = {
186     .typename   = "MIPS",
187     .startup    = startup_brcm_none,
188     .shutdown   = disable_brcm_irq,
189     .enable     = enable_brcm_irq,
190     .disable    = disable_brcm_irq,
191     .ack        = ack_brcm_irq,
192     .end        = end_brcm_none,
193     .set_affinity = NULL
194 };
195
196 void __init arch_init_irq(void)
197 {
198     int i;
199
200     for (i = 0; i < NR_IRQS; i++) {
201         irq_desc[i].status = IRQ_DISABLED;
202         irq_desc[i].action = 0;
203         irq_desc[i].depth = 1;
204         irq_desc[i].handler = &brcm_irq_type;
205     }
206
207     brcm_irq_setup();
208 }
209
210 int request_external_irq(unsigned int irq, 
211         FN_HANDLER handler,
212         unsigned long irqflags, 
213         const char * devname,
214         void *dev_id)
215 {
216     unsigned long flags;
217
218     local_irq_save(flags);
219
220     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
221     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));      // Mask
222     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
223     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
224     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
225
226     local_irq_restore(flags);
227
228     return( request_irq(irq, handler, irqflags, devname, dev_id) );
229 }
230
231 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
232     unsigned int interruptId)
233 {
234     int nRet = -1;
235     char *devname;
236
237     devname = kmalloc(16, GFP_KERNEL);
238     if (devname)
239         sprintf( devname, "brcm_%d", interruptId );
240
241     /* Set the IRQ description to not automatically enable the interrupt at
242      * the end of an ISR.  The driver that handles the interrupt must
243      * explicitly call BcmHalInterruptEnable or enable_brcm_irq.  This behavior
244      * is consistent with interrupt handling on VxWorks.
245      */
246     irq_desc[interruptId].handler = &brcm_irq_no_end_type;
247
248     if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
249     {
250         nRet = request_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
251                 devname, (void *) param );
252             
253     }
254     else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
255     {
256         nRet = request_external_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
257             devname, (void *) param );
258     }
259
260     return( nRet );
261 }
262
263 /* Debug function. */
264
265 void dump_intr_regs(void)
266 {
267     printk("PERF->ExtIrqCfg [%08x]\n", *(&(PERF->ExtIrqCfg)));
268
269
270 EXPORT_SYMBOL(enable_brcm_irq);
271 EXPORT_SYMBOL(disable_brcm_irq);
272 EXPORT_SYMBOL(request_external_irq);
273 EXPORT_SYMBOL(BcmHalMapInterrupt);
274