fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / arch / mips / sibyte / sb1250 / irq.c
index e2216ee..82ce753 100644 (file)
@@ -15,7 +15,6 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
-#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <linux/linkage.h>
@@ -29,7 +28,6 @@
 #include <asm/errno.h>
 #include <asm/signal.h>
 #include <asm/system.h>
-#include <asm/ptrace.h>
 #include <asm/io.h>
 
 #include <asm/sibyte/sb1250_regs.h>
  */
 
 
-#define shutdown_sb1250_irq    disable_sb1250_irq
 static void end_sb1250_irq(unsigned int irq);
 static void enable_sb1250_irq(unsigned int irq);
 static void disable_sb1250_irq(unsigned int irq);
-static unsigned int startup_sb1250_irq(unsigned int irq);
 static void ack_sb1250_irq(unsigned int irq);
 #ifdef CONFIG_SMP
-static void sb1250_set_affinity(unsigned int irq, unsigned long mask);
+static void sb1250_set_affinity(unsigned int irq, cpumask_t mask);
 #endif
 
 #ifdef CONFIG_SIBYTE_HAS_LDT
@@ -61,19 +57,8 @@ extern unsigned long ldt_eoi_space;
 #endif
 
 #ifdef CONFIG_KGDB
-#include <asm/gdb-stub.h>
-extern void breakpoint(void);
 static int kgdb_irq;
 
-/* kgdb is on when configured.  Pass "nokgdb" kernel arg to turn it off */
-static int kgdb_flag = 1;
-static int __init nokgdb(char *str)
-{
-       kgdb_flag = 0;
-       return 1;
-}
-__setup("nokgdb", nokgdb);
-
 /* Default to UART1 */
 int kgdb_port = 1;
 #ifdef CONFIG_SIBYTE_SB1250_DUART
@@ -81,25 +66,22 @@ extern char sb1250_duart_present[];
 #endif
 #endif
 
-static struct hw_interrupt_type sb1250_irq_type = {
-       "SB1250-IMR",
-       startup_sb1250_irq,
-       shutdown_sb1250_irq,
-       enable_sb1250_irq,
-       disable_sb1250_irq,
-       ack_sb1250_irq,
-       end_sb1250_irq,
+static struct irq_chip sb1250_irq_type = {
+       .typename = "SB1250-IMR",
+       .ack = ack_sb1250_irq,
+       .mask = disable_sb1250_irq,
+       .mask_ack = ack_sb1250_irq,
+       .unmask = enable_sb1250_irq,
+       .end = end_sb1250_irq,
 #ifdef CONFIG_SMP
-       sb1250_set_affinity
-#else
-       NULL
+       .set_affinity = sb1250_set_affinity
 #endif
 };
 
 /* Store the CPU id (not the logical number) */
 int sb1250_irq_owner[SB1250_NR_IRQS];
 
-spinlock_t sb1250_imr_lock = SPIN_LOCK_UNLOCKED;
+DEFINE_SPINLOCK(sb1250_imr_lock);
 
 void sb1250_mask_irq(int cpu, int irq)
 {
@@ -107,9 +89,11 @@ void sb1250_mask_irq(int cpu, int irq)
        u64 cur_ints;
 
        spin_lock_irqsave(&sb1250_imr_lock, flags);
-       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        cur_ints |= (((u64) 1) << irq);
-       ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+       ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        spin_unlock_irqrestore(&sb1250_imr_lock, flags);
 }
 
@@ -119,30 +103,25 @@ void sb1250_unmask_irq(int cpu, int irq)
        u64 cur_ints;
 
        spin_lock_irqsave(&sb1250_imr_lock, flags);
-       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        cur_ints &= ~(((u64) 1) << irq);
-       ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+       ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        spin_unlock_irqrestore(&sb1250_imr_lock, flags);
 }
 
 #ifdef CONFIG_SMP
-static void sb1250_set_affinity(unsigned int irq, unsigned long mask)
+static void sb1250_set_affinity(unsigned int irq, cpumask_t mask)
 {
        int i = 0, old_cpu, cpu, int_on;
        u64 cur_ints;
-       irq_desc_t *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_desc + irq;
        unsigned long flags;
 
-       while (mask) {
-               if (mask & 1) {
-                       mask >>= 1;
-                       break;
-               }
-               mask >>= 1;
-               i++;
-       }
+       i = first_cpu(mask);
 
-       if (mask) {
+       if (cpus_weight(mask) > 1) {
                printk("attempted to set irq affinity for irq %d to multiple CPUs\n", irq);
                return;
        }
@@ -156,39 +135,31 @@ static void sb1250_set_affinity(unsigned int irq, unsigned long mask)
 
        /* Swizzle each CPU's IMR (but leave the IP selection alone) */
        old_cpu = sb1250_irq_owner[irq];
-       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(old_cpu) + R_IMR_INTERRUPT_MASK));
+       cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(old_cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        int_on = !(cur_ints & (((u64) 1) << irq));
        if (int_on) {
                /* If it was on, mask it */
                cur_ints |= (((u64) 1) << irq);
-               ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) + R_IMR_INTERRUPT_MASK));
+               ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        }
        sb1250_irq_owner[irq] = cpu;
        if (int_on) {
                /* unmask for the new CPU */
-               cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+               cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
                cur_ints &= ~(((u64) 1) << irq);
-               ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) + R_IMR_INTERRUPT_MASK));
+               ____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
+                                       R_IMR_INTERRUPT_MASK));
        }
        spin_unlock(&sb1250_imr_lock);
        spin_unlock_irqrestore(&desc->lock, flags);
 }
 #endif
 
-
-/* Defined in arch/mips/sibyte/sb1250/irq_handler.S */
-extern void sb1250_irq_handler(void);
-
 /*****************************************************************************/
 
-static unsigned int startup_sb1250_irq(unsigned int irq)
-{
-       sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
-
-       return 0;               /* never anything pending */
-}
-
-
 static void disable_sb1250_irq(unsigned int irq)
 {
        sb1250_mask_irq(sb1250_irq_owner[irq], irq);
@@ -227,8 +198,9 @@ static void ack_sb1250_irq(unsigned int irq)
                         * Clear for all CPUs so an affinity switch
                         * doesn't find an old status
                         */
-                       __raw_writeq(pending, 
-                                    IOADDR(A_IMR_REGISTER(cpu, R_IMR_LDT_INTERRUPT_CLR)));
+                       __raw_writeq(pending,
+                                    IOADDR(A_IMR_REGISTER(cpu,
+                                               R_IMR_LDT_INTERRUPT_CLR)));
                }
 
                /*
@@ -256,22 +228,14 @@ void __init init_sb1250_irqs(void)
 {
        int i;
 
-       for (i = 0; i < NR_IRQS; i++) {
-               irq_desc[i].status = IRQ_DISABLED;
-               irq_desc[i].action = 0;
-               irq_desc[i].depth = 1;
-               if (i < SB1250_NR_IRQS) {
-                       irq_desc[i].handler = &sb1250_irq_type;
-                       sb1250_irq_owner[i] = 0;
-               } else {
-                       irq_desc[i].handler = &no_irq_type;
-               }
+       for (i = 0; i < SB1250_NR_IRQS; i++) {
+               set_irq_chip(i, &sb1250_irq_type);
+               sb1250_irq_owner[i] = 0;
        }
 }
 
 
-static irqreturn_t  sb1250_dummy_handler(int irq, void *dev_id,
-       struct pt_regs *regs)
+static irqreturn_t  sb1250_dummy_handler(int irq, void *dev_id)
 {
        return IRQ_NONE;
 }
@@ -279,7 +243,7 @@ static irqreturn_t  sb1250_dummy_handler(int irq, void *dev_id,
 static struct irqaction sb1250_dummy_action = {
        .handler = sb1250_dummy_handler,
        .flags   = 0,
-       .mask    = 0,
+       .mask    = CPU_MASK_NONE,
        .name    = "sb1250-private",
        .next    = NULL,
        .dev_id  = 0
@@ -287,7 +251,7 @@ static struct irqaction sb1250_dummy_action = {
 
 int sb1250_steal_irq(int irq)
 {
-       irq_desc_t *desc = irq_desc + irq;
+       struct irq_desc *desc = irq_desc + irq;
        unsigned long flags;
        int retval = 0;
 
@@ -307,10 +271,10 @@ int sb1250_steal_irq(int irq)
 }
 
 /*
- *  init_IRQ is called early in the boot sequence from init/main.c.  It
- *  is responsible for setting up the interrupt mapper and installing the
- *  handler that will be responsible for dispatching interrupts to the
- *  "right" place.
+ *  arch_init_irq is called early in the boot sequence from init/main.c via
+ *  init_IRQ.  It is responsible for setting up the interrupt mapper and
+ *  installing the handler that will be responsible for dispatching interrupts
+ *  to the "right" place.
  */
 /*
  * For now, map all interrupts to IP[2].  We could save
@@ -332,7 +296,7 @@ int sb1250_steal_irq(int irq)
 #define IMR_IP5_VAL    K_INT_MAP_I3
 #define IMR_IP6_VAL    K_INT_MAP_I4
 
-void __init init_IRQ(void)
+void __init arch_init_irq(void)
 {
 
        unsigned int i;
@@ -348,7 +312,7 @@ void __init init_IRQ(void)
                                    (i << 3)));
                __raw_writeq(IMR_IP2_VAL,
                             IOADDR(A_IMR_REGISTER(1,
-                                                   R_IMR_INTERRUPT_MAP_BASE) +
+                                                  R_IMR_INTERRUPT_MAP_BASE) +
                                    (i << 3)));
        }
 
@@ -359,15 +323,17 @@ void __init init_IRQ(void)
         * inter-cpu messages
         */
        /* Was I1 */
-       __raw_writeq(IMR_IP3_VAL, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
-                                        (K_INT_MBOX_0 << 3)));
-       __raw_writeq(IMR_IP3_VAL, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) +
-                                        (K_INT_MBOX_0 << 3)));
+       __raw_writeq(IMR_IP3_VAL,
+                    IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
+                           (K_INT_MBOX_0 << 3)));
+       __raw_writeq(IMR_IP3_VAL,
+                    IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) +
+                           (K_INT_MBOX_0 << 3)));
 
        /* Clear the mailboxes.  The firmware may leave them dirty */
-       __raw_writeq(0xffffffffffffffff,
+       __raw_writeq(0xffffffffffffffffULL,
                     IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)));
-       __raw_writeq(0xffffffffffffffff,
+       __raw_writeq(0xffffffffffffffffULL,
                     IOADDR(A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU)));
 
        /* Mask everything except the mailbox registers for both cpus */
@@ -379,7 +345,7 @@ void __init init_IRQ(void)
 
        /*
         * Note that the timer interrupts are also mapped, but this is
-        * done in sb1250_time_init().  Also, the profiling driver 
+        * done in sb1250_time_init().  Also, the profiling driver
         * does its own management of IP7.
         */
 
@@ -388,27 +354,24 @@ void __init init_IRQ(void)
 #endif
        /* Enable necessary IPs, disable the rest */
        change_c0_status(ST0_IM, imask);
-       set_except_vector(0, sb1250_irq_handler);
 
 #ifdef CONFIG_KGDB
        if (kgdb_flag) {
                kgdb_irq = K_INT_UART_0 + kgdb_port;
 
-#ifdef CONFIG_SIBYTE_SB1250_DUART      
+#ifdef CONFIG_SIBYTE_SB1250_DUART
                sb1250_duart_present[kgdb_port] = 0;
 #endif
                /* Setup uart 1 settings, mapper */
-               __raw_writeq(M_DUART_IMR_BRK, IOADDR(A_DUART_IMRREG(kgdb_port)));
+               __raw_writeq(M_DUART_IMR_BRK,
+                            IOADDR(A_DUART_IMRREG(kgdb_port)));
 
                sb1250_steal_irq(kgdb_irq);
                __raw_writeq(IMR_IP6_VAL,
-                            IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
-                                   (kgdb_irq<<3)));
+                            IOADDR(A_IMR_REGISTER(0,
+                                                  R_IMR_INTERRUPT_MAP_BASE) +
+                                   (kgdb_irq << 3)));
                sb1250_unmask_irq(0, kgdb_irq);
-
-               prom_printf("Waiting for GDB on UART port %d\n", kgdb_port);
-               set_debug_traps();
-               breakpoint();
        }
 #endif
 }
@@ -420,7 +383,7 @@ void __init init_IRQ(void)
 #define duart_out(reg, val)     csr_out32(val, IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
 #define duart_in(reg)           csr_in32(IOADDR(A_DUART_CHANREG(kgdb_port,reg)))
 
-void sb1250_kgdb_interrupt(struct pt_regs *regs)
+static void sb1250_kgdb_interrupt(void)
 {
        /*
         * Clear break-change status (allow some time for the remote
@@ -431,7 +394,69 @@ void sb1250_kgdb_interrupt(struct pt_regs *regs)
        mdelay(500);
        duart_out(R_DUART_CMD, V_DUART_MISC_CMD_RESET_BREAK_INT |
                                M_DUART_RX_EN | M_DUART_TX_EN);
-       set_async_breakpoint(&regs->cp0_epc);
+       set_async_breakpoint(&get_irq_regs()->cp0_epc);
 }
 
 #endif         /* CONFIG_KGDB */
+
+extern void sb1250_timer_interrupt(void);
+extern void sb1250_mailbox_interrupt(void);
+
+asmlinkage void plat_irq_dispatch(void)
+{
+       unsigned int pending;
+
+#ifdef CONFIG_SIBYTE_SB1250_PROF
+       /* Set compare to count to silence count/compare timer interrupts */
+       write_c0_compare(read_c0_count());
+#endif
+
+       /*
+        * What a pain. We have to be really careful saving the upper 32 bits
+        * of any * register across function calls if we don't want them
+        * trashed--since were running in -o32, the calling routing never saves
+        * the full 64 bits of a register across a function call.  Being the
+        * interrupt handler, we're guaranteed that interrupts are disabled
+        * during this code so we don't have to worry about random interrupts
+        * blasting the high 32 bits.
+        */
+
+       pending = read_c0_cause() & read_c0_status();
+
+#ifdef CONFIG_SIBYTE_SB1250_PROF
+       if (pending & CAUSEF_IP7) /* Cpu performance counter interrupt */
+               sbprof_cpu_intr();
+       else
+#endif
+
+       if (pending & CAUSEF_IP4)
+               sb1250_timer_interrupt();
+
+#ifdef CONFIG_SMP
+       else if (pending & CAUSEF_IP3)
+               sb1250_mailbox_interrupt();
+#endif
+
+#ifdef CONFIG_KGDB
+       else if (pending & CAUSEF_IP6)                  /* KGDB (uart 1) */
+               sb1250_kgdb_interrupt();
+#endif
+
+       else if (pending & CAUSEF_IP2) {
+               unsigned long long mask;
+
+               /*
+                * Default...we've hit an IP[2] interrupt, which means we've
+                * got to check the 1250 interrupt registers to figure out what
+                * to do.  Need to detect which CPU we're on, now that
+                * smp_affinity is supported.
+                */
+               mask = __raw_readq(IOADDR(A_IMR_REGISTER(smp_processor_id(),
+                                             R_IMR_INTERRUPT_STATUS_BASE)));
+               if (mask)
+                       do_IRQ(fls64(mask) - 1);
+               else
+                       spurious_interrupt();
+       } else
+               spurious_interrupt();
+}