Split up brcm63xx into files/
[openwrt.git] / target / linux / brcm63xx-2.6 / files / arch / mips / bcm963xx / irq.c
diff --git a/target/linux/brcm63xx-2.6/files/arch/mips/bcm963xx/irq.c b/target/linux/brcm63xx-2.6/files/arch/mips/bcm963xx/irq.c
new file mode 100644 (file)
index 0000000..7ee7526
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+<:copyright-gpl 
+ Copyright 2002 Broadcom Corp. All Rights Reserved. 
+ This program is free software; you can distribute it and/or modify it 
+ under the terms of the GNU General Public License (Version 2) as 
+ published by the Free Software Foundation. 
+ This program is distributed in the hope it will be useful, but WITHOUT 
+ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
+ for more details. 
+ You should have received a copy of the GNU General Public License along 
+ with this program; if not, write to the Free Software Foundation, Inc., 
+ 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. 
+:>
+*/
+/*
+ * Interrupt control functions for Broadcom 963xx MIPS boards
+ */
+
+#include <asm/atomic.h>
+
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/signal.h>
+#include <bcm_map_part.h>
+#include <bcm_intr.h>
+
+static void irq_dispatch_int(struct pt_regs *regs)
+{
+       unsigned int pendingIrqs;
+       static unsigned int irqBit;
+       static unsigned int isrNumber = 31;
+
+       pendingIrqs = PERF->IrqStatus & PERF->IrqMask;
+       if (!pendingIrqs) {
+               return;
+       }
+
+       while (1) {
+       irqBit <<= 1;
+       isrNumber++;
+       if (isrNumber == 32) {
+               isrNumber = 0;
+               irqBit = 0x1;
+       }
+       if (pendingIrqs & irqBit) {
+                       PERF->IrqMask &= ~irqBit; // mask
+                       do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET);
+               break;
+       }
+       }
+}
+
+static void irq_dispatch_ext(uint32 irq)
+{
+       if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
+       printk("**** Ext IRQ mask. Should not dispatch ****\n");
+       }
+       /* disable and clear interrupt in the controller */
+       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
+       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
+       do_IRQ(irq);
+}
+
+
+extern void brcm_timer_interrupt(struct pt_regs *regs);
+
+asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
+{
+       u32 cause;
+       while((cause = (read_c0_cause()& CAUSEF_IP))) {
+               if (cause & CAUSEF_IP7)
+                       brcm_timer_interrupt(regs);
+               else if (cause & CAUSEF_IP2)
+                       irq_dispatch_int(regs);
+               else if (cause & CAUSEF_IP3)
+                       irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0);
+               else if (cause & CAUSEF_IP4)
+                       irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1);
+               else if (cause & CAUSEF_IP5)
+                       irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2);
+               else if (cause & CAUSEF_IP6)
+                       irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3);
+               local_irq_disable();
+       }
+}
+
+
+void enable_brcm_irq(unsigned int irq)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
+       PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
+       }
+       else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
+       /* enable and clear interrupt in the controller */
+       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));
+       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
+       }
+       local_irq_restore(flags);
+}
+
+void disable_brcm_irq(unsigned int irq)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+       if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
+       PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
+       }
+       else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
+       /* disable interrupt in the controller */
+       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
+       }
+       local_irq_restore(flags);
+}
+
+void ack_brcm_irq(unsigned int irq)
+{
+       /* Already done in brcm_irq_dispatch */
+}
+
+unsigned int startup_brcm_irq(unsigned int irq)
+{
+       enable_brcm_irq(irq);
+
+       return 0; /* never anything pending */
+}
+
+unsigned int startup_brcm_none(unsigned int irq)
+{
+       return 0;
+}
+
+void end_brcm_irq(unsigned int irq)
+{
+       if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+               enable_brcm_irq(irq);
+}
+
+void end_brcm_none(unsigned int irq)
+{
+}
+
+static struct hw_interrupt_type brcm_irq_type = {
+       .typename       = "MIPS",
+       .startup        = startup_brcm_irq,
+       .shutdown       = disable_brcm_irq,
+       .enable = enable_brcm_irq,
+       .disable        = disable_brcm_irq,
+       .ack    = ack_brcm_irq,
+       .end    = end_brcm_irq,
+       .set_affinity = NULL
+};
+
+static struct hw_interrupt_type brcm_irq_no_end_type = {
+       .typename       = "MIPS",
+       .startup        = startup_brcm_none,
+       .shutdown       = disable_brcm_irq,
+       .enable = enable_brcm_irq,
+       .disable        = disable_brcm_irq,
+       .ack    = ack_brcm_irq,
+       .end    = end_brcm_none,
+       .set_affinity = NULL
+};
+
+void __init arch_init_irq(void)
+{
+       int i;
+
+       clear_c0_status(ST0_BEV);
+       change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4));
+
+       for (i = 0; i < NR_IRQS; i++) {
+               irq_desc[i].status = IRQ_DISABLED;
+               irq_desc[i].action = 0;
+               irq_desc[i].depth = 1;
+               irq_desc[i].chip = &brcm_irq_type;
+       }
+}
+
+int request_external_irq(unsigned int irq, 
+       FN_HANDLER handler,
+               unsigned long irqflags, 
+               const char * devname,
+               void *dev_id)
+{
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
+       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));      // Mask
+       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT));    // Edge insesnsitive
+       PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT));      // Level triggered
+       PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT));     // Low level
+
+       local_irq_restore(flags);
+
+       return( request_irq(irq, handler, irqflags, devname, dev_id) );
+}
+
+/* VxWorks compatibility function(s). */
+
+unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
+       unsigned int interruptId)
+{
+       int nRet = -1;
+       char *devname;
+
+       devname = kmalloc(16, GFP_KERNEL);
+       if (devname)
+               sprintf( devname, "brcm_%d", interruptId );
+
+       /* Set the IRQ description to not automatically enable the interrupt at
+        * the end of an ISR.  The driver that handles the interrupt must
+        * explicitly call BcmHalInterruptEnable or enable_brcm_irq.  This behavior
+        * is consistent with interrupt handling on VxWorks.
+        */
+       irq_desc[interruptId].chip = &brcm_irq_no_end_type;
+
+       if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
+       {
+               nRet = request_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
+                       devname, (void *) param );
+       }
+       else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
+       {
+               nRet = request_external_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
+                       devname, (void *) param );
+       }
+
+       return( nRet );
+}
+
+
+EXPORT_SYMBOL(enable_brcm_irq);
+EXPORT_SYMBOL(disable_brcm_irq);
+EXPORT_SYMBOL(request_external_irq);
+EXPORT_SYMBOL(BcmHalMapInterrupt);
+