http://downloads.netgear.com/files/GPL/GPL_Source_V361j_DM111PSP_series_consumer_rele...
[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             unsigned int irq = isrNumber + INTERNAL_ISR_TABLE_OFFSET;
63 #if defined(CONFIG_BCM96358)
64             if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
65                 PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
66             }
67             else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
68                 PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT));      // Clear
69             }
70 #endif
71             PERF->IrqMask &= ~irqBit; // mask
72             do_IRQ(irq, regs);
73             break;
74         }
75     }
76 }
77
78 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
79 static void irq_dispatch_ext(uint32 irq, struct pt_regs *regs)
80 {
81     if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
82         printk("**** Ext IRQ mask. Should not dispatch ****\n");
83     }
84     /* disable and clear interrupt in the controller */
85     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
86     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
87     do_IRQ(irq, regs);
88 }
89 #endif
90
91 static void irq_dispatch_sw(uint32 irq, struct pt_regs *regs)
92 {
93     clear_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
94     do_IRQ(irq, regs);
95 }
96
97
98 void brcm_irq_dispatch(struct pt_regs *regs)
99 {
100     u32 cause;
101     while((cause = (read_c0_cause() & read_c0_status() & CAUSEF_IP))) {
102         if (cause & CAUSEF_IP7)
103             do_IRQ(MIPS_TIMER_INT, regs);
104         else if (cause & CAUSEF_IP2)
105             irq_dispatch_int(regs);
106 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
107         else if (cause & CAUSEF_IP3)
108             irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0, regs);
109         else if (cause & CAUSEF_IP4)
110             irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1, regs);
111         else if (cause & CAUSEF_IP5)
112             irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2, regs);
113         else if (cause & CAUSEF_IP6)
114             irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3, regs);
115 #endif
116         else if (cause & CAUSEF_IP0)
117             irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_0, regs);
118         else if (cause & CAUSEF_IP1)
119             irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_1, regs);
120         cli();
121     }
122 }
123
124
125 void enable_brcm_irq(unsigned int irq)
126 {
127     unsigned long flags;
128
129     local_irq_save(flags);
130     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
131         PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
132     }
133 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
134     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
135         /* enable and clear interrupt in the controller */
136         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
137         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
138     }
139 #endif
140     else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
141         set_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
142     }
143     local_irq_restore(flags);
144 }
145
146 void disable_brcm_irq(unsigned int irq)
147 {
148     unsigned long flags;
149
150     local_irq_save(flags);
151     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
152         PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
153     }
154 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
155     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
156         /* disable interrupt in the controller */
157         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
158     }
159 #endif
160     else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
161         clear_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
162     }
163     local_irq_restore(flags);
164 }
165
166 void ack_brcm_irq(unsigned int irq)
167 {
168     /* Already done in brcm_irq_dispatch */
169 }
170
171 unsigned int startup_brcm_irq(unsigned int irq)
172 {
173     enable_brcm_irq(irq);
174
175     return 0; /* never anything pending */
176 }
177
178 unsigned int startup_brcm_none(unsigned int irq)
179 {
180     return 0;
181 }
182
183 void end_brcm_irq(unsigned int irq)
184 {
185     if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
186         enable_brcm_irq(irq);
187 }
188
189 void end_brcm_none(unsigned int irq)
190 {
191 }
192
193 #if defined (CONFIG_BCM96358)
194 #define ALLINTS_NOTIMER IE_IRQ0
195 #else
196 #define ALLINTS_NOTIMER (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
197 #endif
198
199 static void __init brcm_irq_setup(void)
200 {
201     extern asmlinkage void brcmIRQ(void);
202
203     clear_c0_status(ST0_BEV);
204     set_except_vector(0, brcmIRQ);
205     change_c0_status(ST0_IM, ALLINTS_NOTIMER);
206
207 #ifdef CONFIG_REMOTE_DEBUG
208     rs_kgdb_hook(0);
209 #endif
210 }
211
212 static struct hw_interrupt_type brcm_irq_type = {
213     .typename   = "MIPS",
214     .startup    = startup_brcm_irq,
215     .shutdown   = disable_brcm_irq,
216     .enable     = enable_brcm_irq,
217     .disable    = disable_brcm_irq,
218     .ack        = ack_brcm_irq,
219     .end        = end_brcm_irq,
220     .set_affinity = NULL
221 };
222
223 static struct hw_interrupt_type brcm_irq_no_end_type = {
224     .typename   = "MIPS",
225     .startup    = startup_brcm_none,
226     .shutdown   = disable_brcm_irq,
227     .enable     = enable_brcm_irq,
228     .disable    = disable_brcm_irq,
229     .ack        = ack_brcm_irq,
230     .end        = end_brcm_none,
231     .set_affinity = NULL
232 };
233
234 void __init arch_init_irq(void)
235 {
236     int i;
237
238     for (i = 0; i < NR_IRQS; i++) {
239         irq_desc[i].status = IRQ_DISABLED;
240         irq_desc[i].action = 0;
241         irq_desc[i].depth = 1;
242         irq_desc[i].handler = &brcm_irq_type;
243     }
244
245     brcm_irq_setup();
246 }
247
248 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
249 int request_external_irq(unsigned int irq, 
250                          FN_HANDLER handler,
251                          unsigned long irqflags, 
252                          const char * devname,
253                          void *dev_id)
254 {
255     unsigned long flags;
256
257     local_irq_save(flags);
258
259     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
260     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));      // Mask
261     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
262     PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
263     PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
264
265     local_irq_restore(flags);
266
267     return( request_irq(irq, handler, irqflags, devname, dev_id) );
268 }
269 #endif
270
271 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
272                                 unsigned int irq)
273 {
274     int nRet = -1;
275     char *devname;
276
277     devname = kmalloc(16, GFP_KERNEL);
278     if (devname)
279         sprintf( devname, "brcm_%d", irq );
280
281     /* Set the IRQ description to not automatically enable the interrupt at
282      * the end of an ISR.  The driver that handles the interrupt must
283      * explicitly call BcmHalInterruptEnable or enable_brcm_irq.  This behavior
284      * is consistent with interrupt handling on VxWorks.
285      */
286     irq_desc[irq].handler = &brcm_irq_no_end_type;
287
288 #if defined(CONFIG_BCM96348) || defined(CONFIG_BCM96358) 
289     if( irq == INTERRUPT_ID_MPI ) {
290         nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT | SA_SHIRQ,
291             devname, (void *) param );
292
293     }
294     else 
295 #endif
296     if( irq >= INTERNAL_ISR_TABLE_OFFSET )
297     {
298 #if defined(CONFIG_BCM96358) 
299         unsigned long flags;
300
301         local_irq_save(flags);
302         if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
303             PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
304             PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));       // Unmask
305             PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
306             PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
307             PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
308         }
309         else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
310             PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT));      // Clear
311             PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_MASK_SHFT));       // Unmask
312             PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_INSENS_SHFT));    // Edge insesnsitive
313             PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_LEVEL_SHFT));      // Level triggered
314             PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_SENSE_SHFT));     // Low level
315         }
316         local_irq_restore(flags);
317 #endif
318         nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
319             devname, (void *) param );
320
321     }
322 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
323     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3)
324     {
325         nRet = request_external_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
326             devname, (void *) param );
327     }
328 #endif
329     else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1))
330     {
331         nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
332             devname, (void *) param );
333     }
334
335     return( nRet );
336 }
337
338
339 //***************************************************************************
340 //  void  BcmHalGenerateSoftInterrupt
341 //
342 //   Triggers a software interrupt.
343 //
344 //***************************************************************************
345 void BcmHalGenerateSoftInterrupt( unsigned int irq )
346 {
347     unsigned long flags;
348
349     local_irq_save(flags);
350
351     set_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
352
353     local_irq_restore(flags);
354 }
355
356 EXPORT_SYMBOL(enable_brcm_irq);
357 EXPORT_SYMBOL(disable_brcm_irq);
358 #if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
359 EXPORT_SYMBOL(request_external_irq);
360 #endif
361 EXPORT_SYMBOL(BcmHalMapInterrupt);
362 EXPORT_SYMBOL(BcmHalGenerateSoftInterrupt);