powerpc: Fix i8259 interrupt driver kernel crash on ML510
[pandora-kernel.git] / arch / powerpc / sysdev / xilinx_intc.c
index c658b41..40edad5 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/of.h>
 #include <asm/io.h>
 #include <asm/processor.h>
+#include <asm/i8259.h>
 #include <asm/irq.h>
 
 /*
@@ -191,20 +192,14 @@ struct irq_host * __init
 xilinx_intc_init(struct device_node *np)
 {
        struct irq_host * irq;
-       struct resource res;
        void * regs;
-       int rc;
 
        /* Find and map the intc registers */
-       rc = of_address_to_resource(np, 0, &res);
-       if (rc) {
-               printk(KERN_ERR __FILE__ ": of_address_to_resource() failed\n");
+       regs = of_iomap(np, 0);
+       if (!regs) {
+               pr_err("xilinx_intc: could not map registers\n");
                return NULL;
        }
-       regs = ioremap(res.start, 32);
-
-       printk(KERN_INFO "Xilinx intc at 0x%08llx mapped to 0x%p\n",
-               (unsigned long long) res.start, regs);
 
        /* Setup interrupt controller */
        out_be32(regs + XINTC_IER, 0); /* disable all irqs */
@@ -217,6 +212,7 @@ xilinx_intc_init(struct device_node *np)
        if (!irq)
                panic(__FILE__ ": Cannot allocate IRQ host\n");
        irq->host_data = regs;
+
        return irq;
 }
 
@@ -227,23 +223,69 @@ int xilinx_intc_get_irq(void)
        return irq_linear_revmap(master_irqhost, in_be32(regs + XINTC_IVR));
 }
 
+#if defined(CONFIG_PPC_I8259)
+/*
+ * Support code for cascading to 8259 interrupt controllers
+ */
+static void xilinx_i8259_cascade(unsigned int irq, struct irq_desc *desc)
+{
+       unsigned int cascade_irq = i8259_irq();
+       if (cascade_irq)
+               generic_handle_irq(cascade_irq);
+
+       /* Let xilinx_intc end the interrupt */
+       desc->chip->unmask(irq);
+}
+
+static void __init xilinx_i8259_setup_cascade(void)
+{
+       struct device_node *cascade_node;
+       int cascade_irq;
+
+       /* Initialize i8259 controller */
+       cascade_node = of_find_compatible_node(NULL, NULL, "chrp,iic");
+       if (!cascade_node)
+               return;
+
+       cascade_irq = irq_of_parse_and_map(cascade_node, 0);
+       if (!cascade_irq) {
+               pr_err("virtex_ml510: Failed to map cascade interrupt\n");
+               goto out;
+       }
+
+       i8259_init(cascade_node, 0);
+       set_irq_chained_handler(cascade_irq, xilinx_i8259_cascade);
+
+       /* Program irq 7 (usb/audio), 14/15 (ide) to level sensitive */
+       /* This looks like a dirty hack to me --gcl */
+       outb(0xc0, 0x4d0);
+       outb(0xc0, 0x4d1);
+
+ out:
+       of_node_put(cascade_node);
+}
+#else
+static inline void xilinx_i8259_setup_cascade(void) { return; }
+#endif /* defined(CONFIG_PPC_I8259) */
+
+static struct of_device_id xilinx_intc_match[] __initconst = {
+       { .compatible = "xlnx,opb-intc-1.00.c", },
+       { .compatible = "xlnx,xps-intc-1.00.a", },
+       {}
+};
+
+/*
+ * Initialize master Xilinx interrupt controller
+ */
 void __init xilinx_intc_init_tree(void)
 {
        struct device_node *np;
 
        /* find top level interrupt controller */
-       for_each_compatible_node(np, NULL, "xlnx,opb-intc-1.00.c") {
+       for_each_matching_node(np, xilinx_intc_match) {
                if (!of_get_property(np, "interrupts", NULL))
                        break;
        }
-       if (!np) {
-               for_each_compatible_node(np, NULL, "xlnx,xps-intc-1.00.a") {
-                       if (!of_get_property(np, "interrupts", NULL))
-                               break;
-               }
-       }
-
-       /* xilinx interrupt controller needs to be top level */
        BUG_ON(!np);
 
        master_irqhost = xilinx_intc_init(np);
@@ -251,4 +293,6 @@ void __init xilinx_intc_init_tree(void)
 
        irq_set_default_host(master_irqhost);
        of_node_put(np);
+
+       xilinx_i8259_setup_cascade();
 }