www.usr.com/support/gpl/USR9113_release1.0.tar.gz
[bcm963xx.git] / kernel / linux / arch / mips / brcm-boards / bcm963xx / irq.c
index ce5ad41..33458ba 100755 (executable)
@@ -1,16 +1,16 @@
 /*
 <:copyright-gpl 
  Copyright 2002 Broadcom Corp. All Rights Reserved. 
 /*
 <: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 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. 
  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. 
  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. 
@@ -52,48 +52,72 @@ static void irq_dispatch_int(struct pt_regs *regs)
     }
 
     while (1) {
     }
 
     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, regs);
-               break;
-       }
+        irqBit <<= 1;
+        isrNumber++;
+        if (isrNumber == 32) {
+            isrNumber = 0;
+            irqBit = 0x1;
+        }
+        if (pendingIrqs & irqBit) {
+            unsigned int irq = isrNumber + INTERNAL_ISR_TABLE_OFFSET;
+#if defined(CONFIG_BCM96358)
+            if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
+                PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
+            }
+            else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
+                PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT));      // Clear
+            }
+#endif
+            PERF->IrqMask &= ~irqBit; // mask
+            do_IRQ(irq, regs);
+            break;
+        }
     }
 }
 
     }
 }
 
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
 static void irq_dispatch_ext(uint32 irq, struct pt_regs *regs)
 {
     if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
 static void irq_dispatch_ext(uint32 irq, struct pt_regs *regs)
 {
     if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) {
-       printk("**** Ext IRQ mask. Should not dispatch ****\n");
+        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, regs);
 }
     }
     /* 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, regs);
 }
+#endif
+
+static void irq_dispatch_sw(uint32 irq, struct pt_regs *regs)
+{
+    clear_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
+    do_IRQ(irq, regs);
+}
+
 
 void brcm_irq_dispatch(struct pt_regs *regs)
 {
     u32 cause;
 
 void brcm_irq_dispatch(struct pt_regs *regs)
 {
     u32 cause;
-    while((cause = (read_c0_cause()& CAUSEF_IP))) {
-       if (cause & CAUSEF_IP7)
-               do_IRQ(MIPS_TIMER_INT, regs);
-       else if (cause & CAUSEF_IP2)
-               irq_dispatch_int(regs);
-       else if (cause & CAUSEF_IP3)
-               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0, regs);
-       else if (cause & CAUSEF_IP4)
-               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1, regs);
-       else if (cause & CAUSEF_IP5)
-               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2, regs);
-       else if (cause & CAUSEF_IP6)
-               irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3, regs);
-       cli();
+    while((cause = (read_c0_cause() & read_c0_status() & CAUSEF_IP))) {
+        if (cause & CAUSEF_IP7)
+            do_IRQ(MIPS_TIMER_INT, regs);
+        else if (cause & CAUSEF_IP2)
+            irq_dispatch_int(regs);
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
+        else if (cause & CAUSEF_IP3)
+            irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0, regs);
+        else if (cause & CAUSEF_IP4)
+            irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1, regs);
+        else if (cause & CAUSEF_IP5)
+            irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2, regs);
+        else if (cause & CAUSEF_IP6)
+            irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3, regs);
+#endif
+        else if (cause & CAUSEF_IP0)
+            irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_0, regs);
+        else if (cause & CAUSEF_IP1)
+            irq_dispatch_sw(INTERRUPT_ID_SOFTWARE_1, regs);
+        cli();
     }
 }
 
     }
 }
 
@@ -104,12 +128,17 @@ void enable_brcm_irq(unsigned int irq)
 
     local_irq_save(flags);
     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
 
     local_irq_save(flags);
     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
-       PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
+        PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
     }
     }
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
     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));
+        /* 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));
+    }
+#endif
+    else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
+        set_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
     }
     local_irq_restore(flags);
 }
     }
     local_irq_restore(flags);
 }
@@ -120,11 +149,16 @@ void disable_brcm_irq(unsigned int irq)
 
     local_irq_save(flags);
     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
 
     local_irq_save(flags);
     if( irq >= INTERNAL_ISR_TABLE_OFFSET ) {
-       PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
+        PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET));
     }
     }
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
     else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
     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));
+        /* disable interrupt in the controller */
+        PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));
+    }
+#endif
+    else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1)) {
+        clear_c0_status(0x1 << (STATUSB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
     }
     local_irq_restore(flags);
 }
     }
     local_irq_restore(flags);
 }
@@ -156,18 +190,22 @@ void end_brcm_none(unsigned int irq)
 {
 }
 
 {
 }
 
+#if defined (CONFIG_BCM96358)
+#define ALLINTS_NOTIMER IE_IRQ0
+#else
 #define ALLINTS_NOTIMER (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
 #define ALLINTS_NOTIMER (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
+#endif
 
 static void __init brcm_irq_setup(void)
 {
 
 static void __init brcm_irq_setup(void)
 {
-       extern asmlinkage void brcmIRQ(void);
+    extern asmlinkage void brcmIRQ(void);
 
 
-       clear_c0_status(ST0_BEV);
-       set_except_vector(0, brcmIRQ);
-       change_c0_status(ST0_IM, ALLINTS_NOTIMER);
+    clear_c0_status(ST0_BEV);
+    set_except_vector(0, brcmIRQ);
+    change_c0_status(ST0_IM, ALLINTS_NOTIMER);
 
 #ifdef CONFIG_REMOTE_DEBUG
 
 #ifdef CONFIG_REMOTE_DEBUG
-       rs_kgdb_hook(0);
+    rs_kgdb_hook(0);
 #endif
 }
 
 #endif
 }
 
@@ -207,11 +245,12 @@ void __init arch_init_irq(void)
     brcm_irq_setup();
 }
 
     brcm_irq_setup();
 }
 
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
 int request_external_irq(unsigned int irq, 
 int request_external_irq(unsigned int irq, 
-       FN_HANDLER handler,
-        unsigned long irqflags, 
-        const char * devname,
-        void *dev_id)
+                         FN_HANDLER handler,
+                         unsigned long irqflags, 
+                         const char * devname,
+                         void *dev_id)
 {
     unsigned long flags;
 
 {
     unsigned long flags;
 
@@ -227,48 +266,97 @@ int request_external_irq(unsigned int irq,
 
     return( request_irq(irq, handler, irqflags, devname, dev_id) );
 }
 
     return( request_irq(irq, handler, irqflags, devname, dev_id) );
 }
+#endif
 
 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
 
 unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param,
-    unsigned int interruptId)
+                                unsigned int irq)
 {
     int nRet = -1;
     char *devname;
 
     devname = kmalloc(16, GFP_KERNEL);
     if (devname)
 {
     int nRet = -1;
     char *devname;
 
     devname = kmalloc(16, GFP_KERNEL);
     if (devname)
-        sprintf( devname, "brcm_%d", interruptId );
+        sprintf( devname, "brcm_%d", irq );
 
     /* 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.
      */
 
     /* 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].handler = &brcm_irq_no_end_type;
+    irq_desc[irq].handler = &brcm_irq_no_end_type;
+
+#if defined(CONFIG_BCM96348) || defined(CONFIG_BCM96358) 
+    if( irq == INTERRUPT_ID_MPI ) {
+        nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT | SA_SHIRQ,
+            devname, (void *) param );
 
 
-    if( interruptId >= INTERNAL_ISR_TABLE_OFFSET )
+    }
+    else 
+#endif
+    if( irq >= INTERNAL_ISR_TABLE_OFFSET )
     {
     {
-        nRet = request_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
-                devname, (void *) param );
-            
+#if defined(CONFIG_BCM96358) 
+        unsigned long flags;
+
+        local_irq_save(flags);
+        if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) {
+            PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT));      // Clear
+            PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT));       // Unmask
+            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
+        }
+        else if (irq >= INTERRUPT_ID_EXTERNAL_4 && irq <= INTERRUPT_ID_EXTERNAL_5) {
+            PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_CLEAR_SHFT));      // Clear
+            PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_MASK_SHFT));       // Unmask
+            PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_INSENS_SHFT));    // Edge insesnsitive
+            PERF->ExtIrqCfg1 |= (1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_LEVEL_SHFT));      // Level triggered
+            PERF->ExtIrqCfg1 &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_4 + EI_SENSE_SHFT));     // Low level
+        }
+        local_irq_restore(flags);
+#endif
+        nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
+            devname, (void *) param );
+
     }
     }
-    else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3)
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
+    else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3)
+    {
+        nRet = request_external_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
+            devname, (void *) param );
+    }
+#endif
+    else if ((irq == INTERRUPT_ID_SOFTWARE_0) || (irq == INTERRUPT_ID_SOFTWARE_1))
     {
     {
-        nRet = request_external_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
+        nRet = request_irq( irq, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT,
             devname, (void *) param );
     }
 
     return( nRet );
 }
 
             devname, (void *) param );
     }
 
     return( nRet );
 }
 
-/* Debug function. */
 
 
-void dump_intr_regs(void)
+//***************************************************************************
+//  void  BcmHalGenerateSoftInterrupt
+//
+//   Triggers a software interrupt.
+//
+//***************************************************************************
+void BcmHalGenerateSoftInterrupt( unsigned int irq )
 {
 {
-    printk("PERF->ExtIrqCfg [%08x]\n", *(&(PERF->ExtIrqCfg)));
-} 
+    unsigned long flags;
+
+    local_irq_save(flags);
+
+    set_c0_cause(0x1 << (CAUSEB_IP0 + irq - INTERRUPT_ID_SOFTWARE_0));
+
+    local_irq_restore(flags);
+}
 
 EXPORT_SYMBOL(enable_brcm_irq);
 EXPORT_SYMBOL(disable_brcm_irq);
 
 EXPORT_SYMBOL(enable_brcm_irq);
 EXPORT_SYMBOL(disable_brcm_irq);
+#if defined(CONFIG_BCM96338) || defined(CONFIG_BCM96348) 
 EXPORT_SYMBOL(request_external_irq);
 EXPORT_SYMBOL(request_external_irq);
+#endif
 EXPORT_SYMBOL(BcmHalMapInterrupt);
 EXPORT_SYMBOL(BcmHalMapInterrupt);
-
+EXPORT_SYMBOL(BcmHalGenerateSoftInterrupt);