Merge to Fedora kernel-2.6.18-1.2224_FC5 patched with stable patch-2.6.18.1-vs2.0...
[linux-2.6.git] / arch / mips / au1000 / common / irq.c
index 5104328..316722e 100644 (file)
@@ -26,7 +26,6 @@
  *  with this program; if not, write  to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
-#include <linux/config.h>
 #include <linux/errno.h>
 #include <linux/init.h>
 #include <linux/irq.h>
@@ -41,8 +40,8 @@
 #include <linux/slab.h>
 #include <linux/random.h>
 #include <linux/delay.h>
+#include <linux/bitops.h>
 
-#include <asm/bitops.h>
 #include <asm/bootinfo.h>
 #include <asm/io.h>
 #include <asm/mipsregs.h>
 #define EXT_INTC1_REQ1 5 /* IP 5 */
 #define MIPS_TIMER_IP  7 /* IP 7 */
 
-#ifdef CONFIG_KGDB
-extern void breakpoint(void);
-#endif
-
-extern asmlinkage void au1000_IRQ(void);
 extern void set_debug_traps(void);
 extern irq_cpustat_t irq_stat [NR_CPUS];
+extern void mips_timer_interrupt(struct pt_regs *regs);
 
 static void setup_local_irq(unsigned int irq, int type, int int_req);
 static unsigned int startup_irq(unsigned int irq);
@@ -84,14 +79,13 @@ static inline void mask_and_ack_either_edge_irq(unsigned int irq_nr);
 inline void local_enable_irq(unsigned int irq_nr);
 inline void local_disable_irq(unsigned int irq_nr);
 
-extern void __init init_generic_irq(void);
 void   (*board_init_irq)(void);
 
 #ifdef CONFIG_PM
-extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs);
+extern irqreturn_t counter0_irq(int irq, void *dev_id, struct pt_regs *regs);
 #endif
 
-static spinlock_t irq_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(irq_lock);
 
 
 static unsigned int startup_irq(unsigned int irq_nr)
@@ -257,53 +251,73 @@ void restore_local_and_enable(int controller, unsigned long mask)
 }
 
 
-static struct hw_interrupt_type rise_edge_irq_type = {
-       "Au1000 Rise Edge",
-       startup_irq,
-       shutdown_irq,
-       local_enable_irq,
-       local_disable_irq,
-       mask_and_ack_rise_edge_irq,
-       end_irq,
-       NULL
+static struct irq_chip rise_edge_irq_type = {
+       .typename = "Au1000 Rise Edge",
+       .startup = startup_irq,
+       .shutdown = shutdown_irq,
+       .enable = local_enable_irq,
+       .disable = local_disable_irq,
+       .ack = mask_and_ack_rise_edge_irq,
+       .end = end_irq,
 };
 
-static struct hw_interrupt_type fall_edge_irq_type = {
-       "Au1000 Fall Edge",
-       startup_irq,
-       shutdown_irq,
-       local_enable_irq,
-       local_disable_irq,
-       mask_and_ack_fall_edge_irq,
-       end_irq,
-       NULL
+static struct irq_chip fall_edge_irq_type = {
+       .typename = "Au1000 Fall Edge",
+       .startup = startup_irq,
+       .shutdown = shutdown_irq,
+       .enable = local_enable_irq,
+       .disable = local_disable_irq,
+       .ack = mask_and_ack_fall_edge_irq,
+       .end = end_irq,
 };
 
-static struct hw_interrupt_type either_edge_irq_type = {
-       "Au1000 Rise or Fall Edge",
-       startup_irq,
-       shutdown_irq,
-       local_enable_irq,
-       local_disable_irq,
-       mask_and_ack_either_edge_irq,
-       end_irq,
-       NULL
+static struct irq_chip either_edge_irq_type = {
+       .typename = "Au1000 Rise or Fall Edge",
+       .startup = startup_irq,
+       .shutdown = shutdown_irq,
+       .enable = local_enable_irq,
+       .disable = local_disable_irq,
+       .ack = mask_and_ack_either_edge_irq,
+       .end = end_irq,
 };
 
-static struct hw_interrupt_type level_irq_type = {
-       "Au1000 Level",
-       startup_irq,
-       shutdown_irq,
-       local_enable_irq,
-       local_disable_irq,
-       mask_and_ack_level_irq,
-       end_irq,
-       NULL
+static struct irq_chip level_irq_type = {
+       .typename = "Au1000 Level",
+       .startup = startup_irq,
+       .shutdown = shutdown_irq,
+       .enable = local_enable_irq,
+       .disable = local_disable_irq,
+       .ack = mask_and_ack_level_irq,
+       .end = end_irq,
 };
 
 #ifdef CONFIG_PM
-void startup_match20_interrupt(void)
+void startup_match20_interrupt(irqreturn_t (*handler)(int, void *, struct pt_regs *))
 {
+       struct irq_desc *desc = &irq_desc[AU1000_TOY_MATCH2_INT];
+
+       static struct irqaction action;
+       memset(&action, 0, sizeof(struct irqaction));
+
+       /* This is a big problem.... since we didn't use request_irq
+        * when kernel/irq.c calls probe_irq_xxx this interrupt will
+        * be probed for usage. This will end up disabling the device :(
+        * Give it a bogus "action" pointer -- this will keep it from
+        * getting auto-probed!
+        *
+        * By setting the status to match that of request_irq() we
+        * can avoid it.  --cgray
+       */
+       action.dev_id = handler;
+       action.flags = IRQF_DISABLED;
+       cpus_clear(action.mask);
+       action.name = "Au1xxx TOY";
+       action.handler = handler;
+       action.next = NULL;
+
+       desc->action = &action;
+       desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
+
        local_enable_irq(AU1000_TOY_MATCH2_INT);
 }
 #endif
@@ -318,31 +332,31 @@ static void setup_local_irq(unsigned int irq_nr, int type, int int_req)
                                au_writel(1<<(irq_nr-32), IC1_CFG2CLR);
                                au_writel(1<<(irq_nr-32), IC1_CFG1CLR);
                                au_writel(1<<(irq_nr-32), IC1_CFG0SET);
-                               irq_desc[irq_nr].handler = &rise_edge_irq_type;
+                               irq_desc[irq_nr].chip = &rise_edge_irq_type;
                                break;
                        case INTC_INT_FALL_EDGE: /* 0:1:0 */
                                au_writel(1<<(irq_nr-32), IC1_CFG2CLR);
                                au_writel(1<<(irq_nr-32), IC1_CFG1SET);
                                au_writel(1<<(irq_nr-32), IC1_CFG0CLR);
-                               irq_desc[irq_nr].handler = &fall_edge_irq_type;
+                               irq_desc[irq_nr].chip = &fall_edge_irq_type;
                                break;
                        case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */
                                au_writel(1<<(irq_nr-32), IC1_CFG2CLR);
                                au_writel(1<<(irq_nr-32), IC1_CFG1SET);
                                au_writel(1<<(irq_nr-32), IC1_CFG0SET);
-                               irq_desc[irq_nr].handler = &either_edge_irq_type;
+                               irq_desc[irq_nr].chip = &either_edge_irq_type;
                                break;
                        case INTC_INT_HIGH_LEVEL: /* 1:0:1 */
                                au_writel(1<<(irq_nr-32), IC1_CFG2SET);
                                au_writel(1<<(irq_nr-32), IC1_CFG1CLR);
                                au_writel(1<<(irq_nr-32), IC1_CFG0SET);
-                               irq_desc[irq_nr].handler = &level_irq_type;
+                               irq_desc[irq_nr].chip = &level_irq_type;
                                break;
                        case INTC_INT_LOW_LEVEL: /* 1:1:0 */
                                au_writel(1<<(irq_nr-32), IC1_CFG2SET);
                                au_writel(1<<(irq_nr-32), IC1_CFG1SET);
                                au_writel(1<<(irq_nr-32), IC1_CFG0CLR);
-                               irq_desc[irq_nr].handler = &level_irq_type;
+                               irq_desc[irq_nr].chip = &level_irq_type;
                                break;
                        case INTC_INT_DISABLED: /* 0:0:0 */
                                au_writel(1<<(irq_nr-32), IC1_CFG0CLR);
@@ -370,31 +384,31 @@ static void setup_local_irq(unsigned int irq_nr, int type, int int_req)
                                au_writel(1<<irq_nr, IC0_CFG2CLR);
                                au_writel(1<<irq_nr, IC0_CFG1CLR);
                                au_writel(1<<irq_nr, IC0_CFG0SET);
-                               irq_desc[irq_nr].handler = &rise_edge_irq_type;
+                               irq_desc[irq_nr].chip = &rise_edge_irq_type;
                                break;
                        case INTC_INT_FALL_EDGE: /* 0:1:0 */
                                au_writel(1<<irq_nr, IC0_CFG2CLR);
                                au_writel(1<<irq_nr, IC0_CFG1SET);
                                au_writel(1<<irq_nr, IC0_CFG0CLR);
-                               irq_desc[irq_nr].handler = &fall_edge_irq_type;
+                               irq_desc[irq_nr].chip = &fall_edge_irq_type;
                                break;
                        case INTC_INT_RISE_AND_FALL_EDGE: /* 0:1:1 */
                                au_writel(1<<irq_nr, IC0_CFG2CLR);
                                au_writel(1<<irq_nr, IC0_CFG1SET);
                                au_writel(1<<irq_nr, IC0_CFG0SET);
-                               irq_desc[irq_nr].handler = &either_edge_irq_type;
+                               irq_desc[irq_nr].chip = &either_edge_irq_type;
                                break;
                        case INTC_INT_HIGH_LEVEL: /* 1:0:1 */
                                au_writel(1<<irq_nr, IC0_CFG2SET);
                                au_writel(1<<irq_nr, IC0_CFG1CLR);
                                au_writel(1<<irq_nr, IC0_CFG0SET);
-                               irq_desc[irq_nr].handler = &level_irq_type;
+                               irq_desc[irq_nr].chip = &level_irq_type;
                                break;
                        case INTC_INT_LOW_LEVEL: /* 1:1:0 */
                                au_writel(1<<irq_nr, IC0_CFG2SET);
                                au_writel(1<<irq_nr, IC0_CFG1SET);
                                au_writel(1<<irq_nr, IC0_CFG0CLR);
-                               irq_desc[irq_nr].handler = &level_irq_type;
+                               irq_desc[irq_nr].chip = &level_irq_type;
                                break;
                        case INTC_INT_DISABLED: /* 0:0:0 */
                                au_writel(1<<irq_nr, IC0_CFG0CLR);
@@ -420,7 +434,7 @@ static void setup_local_irq(unsigned int irq_nr, int type, int int_req)
 }
 
 
-void __init init_IRQ(void)
+void __init arch_init_irq(void)
 {
        int i;
        unsigned long cp0_status;
@@ -431,10 +445,6 @@ void __init init_IRQ(void)
        extern int au1xxx_ic0_nr_irqs;
 
        cp0_status = read_c0_status();
-       memset(irq_desc, 0, sizeof(irq_desc));
-       set_except_vector(0, au1000_IRQ);
-
-       init_generic_irq();
 
        /* Initialize interrupt controllers to a safe state.
        */
@@ -482,13 +492,6 @@ void __init init_IRQ(void)
        */
        if (board_init_irq)
                (*board_init_irq)();
-
-#ifdef CONFIG_KGDB
-       /* If local serial I/O used for debug port, enter kgdb at once */
-       puts("Waiting for kgdb to connect...");
-       set_debug_traps();
-       breakpoint();
-#endif
 }
 
 
@@ -506,7 +509,7 @@ void intc0_req0_irqdispatch(struct pt_regs *regs)
        intc0_req0 |= au_readl(IC0_REQ0INT);
 
        if (!intc0_req0) return;
-
+#ifdef AU1000_USB_DEV_REQ_INT
        /*
         * Because of the tight timing of SETUP token to reply
         * transactions, the USB devices-side packet complete
@@ -517,7 +520,7 @@ void intc0_req0_irqdispatch(struct pt_regs *regs)
                do_IRQ(AU1000_USB_DEV_REQ_INT, regs);
                return;
        }
-
+#endif
        irq = au_ffs(intc0_req0) - 1;
        intc0_req0 &= ~(1<<irq);
        do_IRQ(irq, regs);
@@ -535,17 +538,7 @@ void intc0_req1_irqdispatch(struct pt_regs *regs)
 
        irq = au_ffs(intc0_req1) - 1;
        intc0_req1 &= ~(1<<irq);
-#ifdef CONFIG_PM
-       if (irq == AU1000_TOY_MATCH2_INT) {
-               mask_and_ack_rise_edge_irq(irq);
-               counter0_irq(irq, NULL, regs);
-               local_enable_irq(irq);
-       }
-       else
-#endif
-       {
-               do_IRQ(irq, regs);
-       }
+       do_IRQ(irq, regs);
 }
 
 
@@ -591,13 +584,13 @@ void intc1_req1_irqdispatch(struct pt_regs *regs)
  * au_sleep function in power.c.....maybe I should just pm_register()
  * them instead?
  */
-static uint    sleep_intctl_config0[2];
-static uint    sleep_intctl_config1[2];
-static uint    sleep_intctl_config2[2];
-static uint    sleep_intctl_src[2];
-static uint    sleep_intctl_assign[2];
-static uint    sleep_intctl_wake[2];
-static uint    sleep_intctl_mask[2];
+static unsigned int    sleep_intctl_config0[2];
+static unsigned int    sleep_intctl_config1[2];
+static unsigned int    sleep_intctl_config2[2];
+static unsigned int    sleep_intctl_src[2];
+static unsigned int    sleep_intctl_assign[2];
+static unsigned int    sleep_intctl_wake[2];
+static unsigned int    sleep_intctl_mask[2];
 
 void
 save_au1xxx_intctl(void)
@@ -666,3 +659,21 @@ restore_au1xxx_intctl(void)
        au_writel(sleep_intctl_mask[0], IC0_MASKSET); au_sync();
 }
 #endif /* CONFIG_PM */
+
+asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
+{
+       unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
+
+       if (pending & CAUSEF_IP7)
+               mips_timer_interrupt(regs);
+       else if (pending & CAUSEF_IP2)
+               intc0_req0_irqdispatch(regs);
+       else if (pending & CAUSEF_IP3)
+               intc0_req1_irqdispatch(regs);
+       else if (pending & CAUSEF_IP4)
+               intc1_req0_irqdispatch(regs);
+       else if (pending  & CAUSEF_IP5)
+               intc1_req1_irqdispatch(regs);
+       else
+               spurious_interrupt(regs);
+}