Split up brcm63xx into files/
[librecmc/librecmc.git] / target / linux / brcm63xx-2.6 / files / arch / mips / 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 static void irq_dispatch_int(struct pt_regs *regs)
42 {
43         unsigned int pendingIrqs;
44         static unsigned int irqBit;
45         static unsigned int isrNumber = 31;
46
47         pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
48         if (!pendingIrqs) {
49                 return;
50         }
51
52         while (1) {
53         irqBit <<= 1;
54         isrNumber++;
55         if (isrNumber == 32) {
56                 isrNumber = 0;
57                 irqBit = 0x1;
58         }
59         if (pendingIrqs & irqBit) {
60                         PERF->IrqMask &= ~irqBit; // mask
61                         do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
62                 break;
63         }
64         }
65 }
66
67 static void irq_dispatch_ext(uint32 irq)
68 {
69         if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
70         printk("**** Ext IRQ mask. Should not dispatch ****\n");
71         }
72         /* disable and clear interrupt in the controller */
73         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
74         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
75         do_IRQ(irq);
76 }
77
78
79 extern void brcm_timer_interrupt(struct pt_regs *regs);
80
81 asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
82 {
83         u32 cause;
84         while((cause = (read_c0_cause()& CAUSEF_IP))) {
85                 if (cause & CAUSEF_IP7)
86                         brcm_timer_interrupt(regs);
87                 else if (cause & CAUSEF_IP2)
88                         irq_dispatch_int(regs);
89                 else if (cause & CAUSEF_IP3)
90                         irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
91                 else if (cause & CAUSEF_IP4)
92                         irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
93                 else if (cause & CAUSEF_IP5)
94                         irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
95                 else if (cause & CAUSEF_IP6)
96                         irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
97                 local_irq_disable();
98         }
99 }
100
101
102 void enable_brcm_irq(unsigned int irq)
103 {
104         unsigned long flags;
105
106         local_irq_save(flags);
107         if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
108         PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
109         }
110         else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
111         /* enable and clear interrupt in the controller */
112         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
113         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
114         }
115         local_irq_restore(flags);
116 }
117
118 void disable_brcm_irq(unsigned int irq)
119 {
120         unsigned long flags;
121
122         local_irq_save(flags);
123         if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
124         PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
125         }
126         else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
127         /* disable interrupt in the controller */
128         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
129         }
130         local_irq_restore(flags);
131 }
132
133 void ack_brcm_irq(unsigned int irq)
134 {
135         /* Already done in brcm_irq_dispatch */
136 }
137
138 unsigned int startup_brcm_irq(unsigned int irq)
139 {
140         enable_brcm_irq(irq);
141
142         return 0; /* never anything pending */
143 }
144
145 unsigned int startup_brcm_none(unsigned int irq)
146 {
147         return 0;
148 }
149
150 void end_brcm_irq(unsigned int irq)
151 {
152         if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
153                 enable_brcm_irq(irq);
154 }
155
156 void end_brcm_none(unsigned int irq)
157 {
158 }
159
160 static struct hw_interrupt_type brcm_irq_type = {
161         .typename       = "MIPS",
162         .startup        = startup_brcm_irq,
163         .shutdown       = disable_brcm_irq,
164         .enable = enable_brcm_irq,
165         .disable        = disable_brcm_irq,
166         .ack    = ack_brcm_irq,
167         .end    = end_brcm_irq,
168         .set_affinity = NULL
169 };
170
171 static struct hw_interrupt_type brcm_irq_no_end_type = {
172         .typename       = "MIPS",
173         .startup        = startup_brcm_none,
174         .shutdown       = disable_brcm_irq,
175         .enable = enable_brcm_irq,
176         .disable        = disable_brcm_irq,
177         .ack    = ack_brcm_irq,
178         .end    = end_brcm_none,
179         .set_affinity = NULL
180 };
181
182 void __init arch_init_irq(void)
183 {
184         int i;
185
186         clear_c0_status(ST0_BEV);
187         change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
188
189         for (i = 0; i < NR_IRQS; i++) {
190                 irq_desc[i].status = IRQ_DISABLED;
191                 irq_desc[i].action = 0;
192                 irq_desc[i].depth = 1;
193                 irq_desc[i].chip = &brcm_irq_type;
194         }
195 }
196
197 int request_external_irq(unsigned int irq, 
198         FN_HANDLER handler,
199                 unsigned long irqflags, 
200                 const char * devname,
201                 void *dev_id)
202 {
203         unsigned long flags;
204
205         local_irq_save(flags);
206
207         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
208         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));      // Mask
209         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
210         PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
211         PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
212
213         local_irq_restore(flags);
214
215         return( request_irq(irq, handler, irqflags, devname, dev_id) );
216 }
217
218 /* VxWorks compatibility function(s). */
219
220 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
221         unsigned int interruptId)
222 {
223         int nRet = -1;
224         char *devname;
225
226         devname = kmalloc(16, GFP_KERNEL);
227         if (devname)
228                 sprintf( devname, "brcm_%d", interruptId );
229
230         /* Set the IRQ description to not automatically enable the interrupt at
231          * the end of an ISR.  The driver that handles the interrupt must
232          * explicitly call BcmHalInterruptEnable or enable_brcm_irq.  This behavior
233          * is consistent with interrupt handling on VxWorks.
234          */
235         irq_desc[interruptId].chip = &brcm_irq_no_end_type;
236
237         if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
238         {
239                 nRet = request_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
240                         devname, (void *) param );
241         }
242         else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
243         {
244                 nRet = request_external_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
245                         devname, (void *) param );
246         }
247
248         return( nRet );
249 }
250
251
252 EXPORT_SYMBOL(enable_brcm_irq);
253 EXPORT_SYMBOL(disable_brcm_irq);
254 EXPORT_SYMBOL(request_external_irq);
255 EXPORT_SYMBOL(BcmHalMapInterrupt);
256