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