Merge with git://www.denx.de/git/u-boot.git
[platform/kernel/u-boot.git] / cpu / ppc4xx / interrupts.c
index b6a956c..ca565cc 100644 (file)
@@ -5,6 +5,9 @@
  * (C) Copyright 2002 (440 port)
  * Scott McNutt, Artesyn Communication Producs, smcnutt@artsyncp.com
  *
+ * (C) Copyright 2003 (440GX port)
+ * Travis B. Sawyer, Sandburst Corporation, tsawyer@sandburst.com
+ *
  * See file CREDITS for list of people who contributed to this
  * project.
  *
@@ -33,9 +36,7 @@
 #include <commproc.h>
 #include "vecnum.h"
 
-/****************************************************************************/
-
-unsigned decrementer_count;            /* count value for 1e6/HZ microseconds */
+DECLARE_GLOBAL_DATA_PTR;
 
 /****************************************************************************/
 
@@ -49,28 +50,27 @@ struct      irq_action {
 };
 
 static struct irq_action irq_vecs[32];
+void uic0_interrupt( void * parms); /* UIC0 handler */
 
 #if defined(CONFIG_440)
 static struct irq_action irq_vecs1[32]; /* For UIC1 */
 
 void uic1_interrupt( void * parms); /* UIC1 handler */
-#endif
 
-/****************************************************************************/
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+static struct irq_action irq_vecs2[32]; /* For UIC2 */
+void uic2_interrupt( void * parms); /* UIC2 handler */
+#endif /* CONFIG_440GX CONFIG_440SPE */
 
-static __inline__ unsigned long get_msr(void)
-{
-       unsigned long msr;
+#if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+static struct irq_action irq_vecs3[32]; /* For UIC3 */
+void uic3_interrupt( void * parms); /* UIC3 handler */
+#endif /* CONFIG_440SPE */
 
-       asm volatile("mfmsr %0" : "=r" (msr) :);
-       return msr;
-}
-
-static __inline__ void set_msr(unsigned long msr)
-{
-       asm volatile("mtmsr %0" : : "r" (msr));
-}
+#endif /* CONFIG_440 */
 
+/****************************************************************************/
 #if defined(CONFIG_440)
 
 /* SPRN changed in 440 */
@@ -81,21 +81,6 @@ static __inline__ void set_evpr(unsigned long val)
 
 #else /* !defined(CONFIG_440) */
 
-static __inline__ unsigned long get_dec(void)
-{
-       unsigned long val;
-
-       asm volatile("mfdec %0" : "=r" (val) :);
-       return val;
-}
-
-
-static __inline__ void set_dec(unsigned long val)
-{
-       asm volatile("mtdec %0" : : "r" (val));
-}
-
-
 static __inline__ void set_pit(unsigned long val)
 {
        asm volatile("mtpit %0" : : "r" (val));
@@ -114,29 +99,16 @@ static __inline__ void set_evpr(unsigned long val)
 }
 #endif /* defined(CONFIG_440 */
 
-
-void enable_interrupts (void)
-{
-       set_msr (get_msr() | MSR_EE);
-}
-
-/* returns flag if MSR_EE was set before */
-int disable_interrupts (void)
-{
-       ulong msr = get_msr();
-       set_msr (msr & ~MSR_EE);
-       return ((msr & MSR_EE) != 0);
-}
-
 /****************************************************************************/
 
-int interrupt_init(void)
+int interrupt_init_cpu (unsigned *decrementer_count)
 {
-       DECLARE_GLOBAL_DATA_PTR;
-
        int vec;
        unsigned long val;
 
+       /* decrementer is automatically reloaded */
+       *decrementer_count = 0;
+
        /*
         * Mark all irqs as free
         */
@@ -148,6 +120,17 @@ int interrupt_init(void)
                irq_vecs1[vec].handler = NULL;
                irq_vecs1[vec].arg = NULL;
                irq_vecs1[vec].count = 0;
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+               irq_vecs2[vec].handler = NULL;
+               irq_vecs2[vec].arg = NULL;
+               irq_vecs2[vec].count = 0;
+#endif /* CONFIG_440GX */
+#if defined(CONFIG_440SPE) || defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+               irq_vecs3[vec].handler = NULL;
+               irq_vecs3[vec].arg = NULL;
+               irq_vecs3[vec].count = 0;
+#endif /* CONFIG_440SPE */
 #endif
        }
 
@@ -162,7 +145,7 @@ int interrupt_init(void)
        mtspr( dec, 0 );                /* Prevent exception after TSR clear*/
        mtspr( decar, 0 );              /* clear reload */
        mtspr( tsr, 0x08000000 );       /* clear DEC status */
-       val = gd->bd->bi_intfreq/100;   /* 10 msec */
+       val = gd->bd->bi_intfreq/1000;  /* 1 msec */
        mtspr( decar, val );            /* Set auto-reload value */
        mtspr( dec, val );              /* Set inital val */
 #else
@@ -190,14 +173,30 @@ int interrupt_init(void)
        set_evpr(0x00000000);
 
 #if defined(CONFIG_440)
+#if !defined(CONFIG_440GX)
        /* Install the UIC1 handlers */
        irq_install_handler(VECNUM_UIC1NC, uic1_interrupt, 0);
        irq_install_handler(VECNUM_UIC1C, uic1_interrupt, 0);
 #endif
-       /*
-        * Enable external interrupts (including PIT)
+#endif
+
+#if defined(CONFIG_440GX)
+       /* Take the GX out of compatibility mode
+        * Travis Sawyer, 9 Mar 2004
+        * NOTE: 440gx user manual inconsistency here
+        *       Compatibility mode and Ethernet Clock select are not
+        *       correct in the manual
         */
-       set_msr (get_msr() | MSR_EE);
+       mfsdr(sdr_mfr, val);
+       val &= ~0x10000000;
+       mtsdr(sdr_mfr,val);
+
+       /* Enable UIC interrupts via UIC Base Enable Register */
+       mtdcr(uicb0sr, UICB0_ALL);
+       mtdcr(uicb0er, 0x54000000);
+       /* None are critical */
+       mtdcr(uicb0cr, 0);
+#endif
 
        return (0);
 }
@@ -207,6 +206,88 @@ int interrupt_init(void)
 /*
  * Handle external interrupts
  */
+#if defined(CONFIG_440GX)
+void external_interrupt(struct pt_regs *regs)
+{
+       ulong uic_msr;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       /* 440 GX uses base uic register */
+       uic_msr = mfdcr(uicb0msr);
+
+       if ( (UICB0_UIC0CI & uic_msr) || (UICB0_UIC0NCI & uic_msr) )
+               uic0_interrupt(0);
+
+       if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
+               uic1_interrupt(0);
+
+       if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
+               uic2_interrupt(0);
+
+       mtdcr(uicb0sr, uic_msr);
+
+       return;
+
+} /* external_interrupt CONFIG_440GX */
+
+#elif defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+void external_interrupt(struct pt_regs *regs)
+{
+       ulong uic_msr;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       /* 440 SPe uses base uic register */
+       uic_msr = mfdcr(uic0msr);
+
+       if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
+               uic1_interrupt(0);
+
+       if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
+               uic2_interrupt(0);
+
+       if (uic_msr & ~(UICB0_ALL))
+               uic0_interrupt(0);
+
+       mtdcr(uic0sr, uic_msr);
+
+       return;
+
+} /* external_interrupt CONFIG_440EPX & CONFIG_440GRX */
+
+#elif defined(CONFIG_440SPE)
+void external_interrupt(struct pt_regs *regs)
+{
+       ulong uic_msr;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       /* 440 SPe uses base uic register */
+       uic_msr = mfdcr(uic0msr);
+
+       if ( (UICB0_UIC1CI & uic_msr) || (UICB0_UIC1NCI & uic_msr) )
+               uic1_interrupt(0);
+
+       if ( (UICB0_UIC2CI & uic_msr) || (UICB0_UIC2NCI & uic_msr) )
+               uic2_interrupt(0);
+
+       if ( (UICB0_UIC3CI & uic_msr) || (UICB0_UIC3NCI & uic_msr) )
+               uic3_interrupt(0);
+
+       if (uic_msr & ~(UICB0_ALL))
+               uic0_interrupt(0);
+
+       mtdcr(uic0sr, uic_msr);
+
+       return;
+} /* external_interrupt CONFIG_440SPE */
+
+#else
+
 void external_interrupt(struct pt_regs *regs)
 {
        ulong uic_msr;
@@ -248,6 +329,54 @@ void external_interrupt(struct pt_regs *regs)
                vec++;
        }
 }
+#endif
+
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+/* Handler for UIC0 interrupt */
+void uic0_interrupt( void * parms)
+{
+       ulong uic_msr;
+       ulong msr_shift;
+       int vec;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       uic_msr = mfdcr(uicmsr);
+       msr_shift = uic_msr;
+       vec = 0;
+
+       while (msr_shift != 0) {
+               if (msr_shift & 0x80000000) {
+                       /*
+                        * Increment irq counter (for debug purpose only)
+                        */
+                       irq_vecs[vec].count++;
+
+                       if (irq_vecs[vec].handler != NULL) {
+                               /* call isr */
+                               (*irq_vecs[vec].handler)(irq_vecs[vec].arg);
+                       } else {
+                               mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> vec));
+                               printf ("Masking bogus interrupt vector (uic0) 0x%x\n", vec);
+                       }
+
+                       /*
+                        * After servicing the interrupt, we have to remove the status indicator.
+                        */
+                       mtdcr(uicsr, (0x80000000 >> vec));
+               }
+
+               /*
+                * Shift msr to next position and increment vector
+                */
+               msr_shift <<= 1;
+               vec++;
+       }
+}
+
+#endif /* CONFIG_440GX */
 
 #if defined(CONFIG_440)
 /* Handler for UIC1 interrupt */
@@ -294,50 +423,171 @@ void uic1_interrupt( void * parms)
 }
 #endif /* defined(CONFIG_440) */
 
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+/* Handler for UIC2 interrupt */
+void uic2_interrupt( void * parms)
+{
+       ulong uic2_msr;
+       ulong msr_shift;
+       int vec;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       uic2_msr = mfdcr(uic2msr);
+       msr_shift = uic2_msr;
+       vec = 0;
+
+       while (msr_shift != 0) {
+               if (msr_shift & 0x80000000) {
+                       /*
+                        * Increment irq counter (for debug purpose only)
+                        */
+                       irq_vecs2[vec].count++;
+
+                       if (irq_vecs2[vec].handler != NULL) {
+                               /* call isr */
+                               (*irq_vecs2[vec].handler)(irq_vecs2[vec].arg);
+                       } else {
+                               mtdcr(uic2er, mfdcr(uic2er) & ~(0x80000000 >> vec));
+                               printf ("Masking bogus interrupt vector (uic2) 0x%x\n", vec);
+                       }
+
+                       /*
+                        * After servicing the interrupt, we have to remove the status indicator.
+                        */
+                       mtdcr(uic2sr, (0x80000000 >> vec));
+               }
+
+               /*
+                * Shift msr to next position and increment vector
+                */
+               msr_shift <<= 1;
+               vec++;
+       }
+}
+#endif /* defined(CONFIG_440GX) */
+
+#if defined(CONFIG_440SPE)
+/* Handler for UIC3 interrupt */
+void uic3_interrupt( void * parms)
+{
+       ulong uic3_msr;
+       ulong msr_shift;
+       int vec;
+
+       /*
+        * Read masked interrupt status register to determine interrupt source
+        */
+       uic3_msr = mfdcr(uic3msr);
+       msr_shift = uic3_msr;
+       vec = 0;
+
+       while (msr_shift != 0) {
+               if (msr_shift & 0x80000000) {
+                       /*
+                        * Increment irq counter (for debug purpose only)
+                        */
+                       irq_vecs3[vec].count++;
+
+                       if (irq_vecs3[vec].handler != NULL) {
+                               /* call isr */
+                               (*irq_vecs3[vec].handler)(irq_vecs3[vec].arg);
+                       } else {
+                               mtdcr(uic3er, mfdcr(uic3er) & ~(0x80000000 >> vec));
+                               printf ("Masking bogus interrupt vector (uic3) 0x%x\n", vec);
+                       }
+
+                       /*
+                        * After servicing the interrupt, we have to remove the status indicator.
+                        */
+                       mtdcr(uic3sr, (0x80000000 >> vec));
+               }
+
+               /*
+                * Shift msr to next position and increment vector
+                */
+               msr_shift <<= 1;
+               vec++;
+       }
+}
+#endif /* defined(CONFIG_440SPE) */
+
 /****************************************************************************/
 
 /*
  * Install and free a interrupt handler.
  */
 
-void
-irq_install_handler(int vec, interrupt_handler_t *handler, void *arg)
+void irq_install_handler (int vec, interrupt_handler_t * handler, void *arg)
 {
        struct irq_action *irqa = irq_vecs;
-       int   i = vec;
+       int i = vec;
 
 #if defined(CONFIG_440)
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+       if ((vec > 31) && (vec < 64)) {
+               i = vec - 32;
+               irqa = irq_vecs1;
+       } else if (vec > 63) {
+               i = vec - 64;
+               irqa = irq_vecs2;
+       }
+#else  /* CONFIG_440GX */
        if (vec > 31) {
                i = vec - 32;
                irqa = irq_vecs1;
        }
-#endif
+#endif /* CONFIG_440GX */
+#endif /* CONFIG_440 */
 
-       if (irqa[i].handler != NULL) {
+       /*
+        * print warning when replacing with a different irq vector
+        */
+       if ((irqa[i].handler != NULL) && (irqa[i].handler != handler)) {
                printf ("Interrupt vector %d: handler 0x%x replacing 0x%x\n",
-                       vec, (uint)handler, (uint)irqa[i].handler);
+                       vec, (uint) handler, (uint) irqa[i].handler);
        }
        irqa[i].handler = handler;
-       irqa[i].arg     = arg;
+       irqa[i].arg = arg;
 
 #if defined(CONFIG_440)
-       if( vec > 31 )
-               mtdcr(uic1er, mfdcr(uic1er) | (0x80000000 >> i));
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+       if ((vec > 31) && (vec < 64))
+               mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
+       else if (vec > 63)
+               mtdcr (uic2er, mfdcr (uic2er) | (0x80000000 >> i));
+       else
+#endif /* CONFIG_440GX */
+       if (vec > 31)
+               mtdcr (uic1er, mfdcr (uic1er) | (0x80000000 >> i));
        else
 #endif
-               mtdcr(uicer, mfdcr(uicer) | (0x80000000 >> i));
+               mtdcr (uicer, mfdcr (uicer) | (0x80000000 >> i));
 #if 0
        printf ("Install interrupt for vector %d ==> %p\n", vec, handler);
 #endif
 }
 
-void
-irq_free_handler(int vec)
+void irq_free_handler (int vec)
 {
        struct irq_action *irqa = irq_vecs;
-       int   i = vec;
+       int i = vec;
 
 #if defined(CONFIG_440)
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+       if ((vec > 31) && (vec < 64)) {
+               irqa = irq_vecs1;
+               i = vec - 32;
+       } else if (vec > 63) {
+               irqa = irq_vecs2;
+               i = vec - 64;
+       }
+#endif /* CONFIG_440GX */
        if (vec > 31) {
                irqa = irq_vecs1;
                i = vec - 32;
@@ -350,60 +600,35 @@ irq_free_handler(int vec)
 #endif
 
 #if defined(CONFIG_440)
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+       if ((vec > 31) && (vec < 64))
+               mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
+       else if (vec > 63)
+               mtdcr (uic2er, mfdcr (uic2er) & ~(0x80000000 >> i));
+       else
+#endif /* CONFIG_440GX */
        if (vec > 31)
-               mtdcr(uic1er, mfdcr(uic1er) & ~(0x80000000 >> i));
+               mtdcr (uic1er, mfdcr (uic1er) & ~(0x80000000 >> i));
        else
 #endif
-               mtdcr(uicer, mfdcr(uicer) & ~(0x80000000 >> i));
+               mtdcr (uicer, mfdcr (uicer) & ~(0x80000000 >> i));
 
        irqa[i].handler = NULL;
-       irqa[i].arg     = NULL;
+       irqa[i].arg = NULL;
 }
 
 /****************************************************************************/
 
-
-volatile ulong timestamp = 0;
-
-/*
- * timer_interrupt - gets called when the decrementer overflows,
- * with interrupts disabled.
- * Trivial implementation - no need to be really accurate.
- */
-void timer_interrupt(struct pt_regs *regs)
+void timer_interrupt_cpu (struct pt_regs *regs)
 {
-#if 0
-       printf ("*** Timer Interrupt *** ");
-#endif
-       timestamp++;
-
-#if defined(CONFIG_WATCHDOG)
-       if ((timestamp % 1000) == 0)
-               reset_4xx_watchdog();
-#endif /* CONFIG_WATCHDOG */
+       /* nothing to do here */
+       return;
 }
 
 /****************************************************************************/
 
-void reset_timer (void)
-{
-       timestamp = 0;
-}
-
-ulong get_timer (ulong base)
-{
-       return (timestamp - base);
-}
-
-void set_timer (ulong t)
-{
-       timestamp = t;
-}
-
-/****************************************************************************/
-
-
-#if (CONFIG_COMMANDS & CFG_CMD_IRQ)
+#if defined(CONFIG_CMD_IRQ)
 
 /*******************************************************************************
  *
@@ -435,8 +660,7 @@ do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        printf ("\nUIC 1\n");
        printf ("Nr  Routine   Arg       Count\n");
 
-       for (vec=0; vec<32; vec++)
-       {
+       for (vec=0; vec<32; vec++) {
                if (irq_vecs1[vec].handler != NULL)
                        printf ("%02d  %08lx  %08lx  %d\n",
                                vec+31, (ulong)irq_vecs1[vec].handler,
@@ -444,8 +668,34 @@ do_irqinfo(cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
        }
        printf("\n");
 #endif
-       return 0;
-}
 
+#if defined(CONFIG_440GX) || defined(CONFIG_440SPE) || \
+    defined(CONFIG_440EPX) || defined(CONFIG_440GRX)
+       printf ("\nUIC 2\n");
+       printf ("Nr  Routine   Arg       Count\n");
 
-#endif  /* CONFIG_COMMANDS & CFG_CMD_IRQ */
+       for (vec=0; vec<32; vec++) {
+               if (irq_vecs2[vec].handler != NULL)
+                       printf ("%02d  %08lx  %08lx  %d\n",
+                               vec+63, (ulong)irq_vecs2[vec].handler,
+                               (ulong)irq_vecs2[vec].arg, irq_vecs2[vec].count);
+       }
+       printf("\n");
+#endif
+
+#if defined(CONFIG_440SPE)
+       printf ("\nUIC 3\n");
+       printf ("Nr  Routine   Arg       Count\n");
+
+       for (vec=0; vec<32; vec++) {
+               if (irq_vecs3[vec].handler != NULL)
+                       printf ("%02d  %08lx  %08lx  %d\n",
+                                       vec+63, (ulong)irq_vecs3[vec].handler,
+                                       (ulong)irq_vecs3[vec].arg, irq_vecs3[vec].count);
+       }
+       printf("\n");
+#endif
+
+       return 0;
+}
+#endif