[PATCH] powerpc: cell interrupt controller updates
Jens Osterkamp [Wed, 22 Mar 2006 23:00:06 +0000 (00:00 +0100)]
The current interrupt controller setup on Cell is done
in a rather ad-hoc way with device tree properties
that are not standardized at all.

In an attempt to do something that follows the OF standard
(or at least the IBM extensions to it) more closely,
we have now come up with this patch. It still provides
a fallback to the old behaviour when we find older firmware,
that hack can not be removed until the existing customer
installations have upgraded.

Cc: hpenner@de.ibm.com
Cc: stk@de.ibm.com
Cc: Segher Boessenkool <segher@kernel.crashing.org>
Cc: Milton Miller <miltonm@bga.com>
Cc: benh@kernel.crashing.org
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>

arch/powerpc/platforms/cell/interrupt.c
arch/powerpc/platforms/cell/interrupt.h
arch/powerpc/platforms/cell/spider-pic.c

index 63aa52a..9d41e07 100644 (file)
@@ -123,7 +123,7 @@ static int iic_external_get_irq(struct iic_pending_bits pending)
                    pending.class != 2)
                        break;
                irq = IIC_EXT_OFFSET
-                       + spider_get_irq(pending.prio + node * IIC_NODE_STRIDE)
+                       + spider_get_irq(node)
                        + node * IIC_NODE_STRIDE;
                break;
        case 0x01 ... 0x04:
@@ -174,38 +174,102 @@ int iic_get_irq(struct pt_regs *regs)
        return irq;
 }
 
-static int setup_iic(int cpu, struct iic *iic)
+/* hardcoded part to be compatible with older firmware */
+
+static int setup_iic_hardcoded(void)
 {
        struct device_node *np;
-       int nodeid = cpu / 2;
+       int nodeid, cpu;
        unsigned long regs;
+       struct iic *iic;
 
-       for (np = of_find_node_by_type(NULL, "cpu");
-            np;
-            np = of_find_node_by_type(np, "cpu")) {
-               if (nodeid == *(int *)get_property(np, "node-id", NULL))
-                       break;
+       for_each_cpu(cpu) {
+               iic = &per_cpu(iic, cpu);
+               nodeid = cpu/2;
+
+               for (np = of_find_node_by_type(NULL, "cpu");
+                    np;
+                    np = of_find_node_by_type(np, "cpu")) {
+                       if (nodeid == *(int *)get_property(np, "node-id", NULL))
+                               break;
+                       }
+
+               if (!np) {
+                       printk(KERN_WARNING "IIC: CPU %d not found\n", cpu);
+                       iic->regs = NULL;
+                       iic->target_id = 0xff;
+                       return -ENODEV;
+                       }
+
+               regs = *(long *)get_property(np, "iic", NULL);
+
+               /* hack until we have decided on the devtree info */
+               regs += 0x400;
+               if (cpu & 1)
+                       regs += 0x20;
+
+               printk(KERN_INFO "IIC for CPU %d at %lx\n", cpu, regs);
+               iic->regs = __ioremap(regs, sizeof(struct iic_regs),
+                                     _PAGE_NO_CACHE);
+
+               iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
        }
 
-       if (!np) {
-               printk(KERN_WARNING "IIC: CPU %d not found\n", cpu);
-               iic->regs = NULL;
-               iic->target_id = 0xff;
-               return -ENODEV;
-       }
+       return 0;
+}
 
-       regs = *(long *)get_property(np, "iic", NULL);
+static int setup_iic(void)
+{
+       struct device_node *dn;
+       unsigned long *regs;
+       char *compatible;
+       unsigned *np, found = 0;
+       struct iic *iic = NULL;
+
+       for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) {
+               compatible = (char *)get_property(dn, "compatible", NULL);
+
+               if (!compatible) {
+                       printk(KERN_WARNING "no compatible property found !\n");
+                       continue;
+               }
 
-       /* hack until we have decided on the devtree info */
-       regs += 0x400;
-       if (cpu & 1)
-               regs += 0x20;
+               if (strstr(compatible, "IBM,CBEA-Internal-Interrupt-Controller"))
+                       regs = (unsigned long *)get_property(dn,"reg", NULL);
+               else
+                       continue;
 
-       printk(KERN_DEBUG "IIC for CPU %d at %lx\n", cpu, regs);
-       iic->regs = __ioremap(regs, sizeof(struct iic_regs),
-                                        _PAGE_NO_CACHE);
-       iic->target_id = (nodeid << 4) + ((cpu & 1) ? 0xf : 0xe);
-       return 0;
+               if (!regs)
+                       printk(KERN_WARNING "IIC: no reg property\n");
+
+               np = (unsigned int *)get_property(dn, "ibm,interrupt-server-ranges", NULL);
+
+               if (!np) {
+                       printk(KERN_WARNING "IIC: CPU association not found\n");
+                       iic->regs = NULL;
+                       iic->target_id = 0xff;
+                       return -ENODEV;
+               }
+
+               iic = &per_cpu(iic, np[0]);
+               iic->regs = __ioremap(regs[0], sizeof(struct iic_regs),
+                                     _PAGE_NO_CACHE);
+               iic->target_id = ((np[0] & 2) << 3) + ((np[0] & 1) ? 0xf : 0xe);
+               printk("IIC for CPU %d at %lx mapped to %p\n", np[0], regs[0], iic->regs);
+
+               iic = &per_cpu(iic, np[1]);
+               iic->regs = __ioremap(regs[2], sizeof(struct iic_regs),
+                                     _PAGE_NO_CACHE);
+               iic->target_id = ((np[1] & 2) << 3) + ((np[1] & 1) ? 0xf : 0xe);
+               printk("IIC for CPU %d at %lx mapped to %p\n", np[1], regs[2], iic->regs);
+
+               found++;
+       }
+
+       if (found)
+               return 0;
+       else
+               return -ENODEV;
 }
 
 #ifdef CONFIG_SMP
@@ -283,10 +347,12 @@ void iic_init_IRQ(void)
        int cpu, irq_offset;
        struct iic *iic;
 
+       if (setup_iic() < 0)
+               setup_iic_hardcoded();
+
        irq_offset = 0;
        for_each_cpu(cpu) {
                iic = &per_cpu(iic, cpu);
-               setup_iic(cpu, iic);
                if (iic->regs)
                        out_be64(&iic->regs->prio, 0xff);
        }
index a14bd38..799f77d 100644 (file)
@@ -57,7 +57,7 @@ extern void iic_local_disable(void);
 extern u8 iic_get_target_id(int cpu);
 
 extern void spider_init_IRQ(void);
-extern int spider_get_irq(unsigned long int_pending);
+extern int spider_get_irq(int node);
 
 #endif
 #endif /* ASM_CELL_PIC_H */
index e741321..0e9bb0b 100644 (file)
@@ -84,10 +84,11 @@ static void __iomem *spider_get_irq_config(int irq)
 
 static void spider_enable_irq(unsigned int irq)
 {
+       int nodeid = (irq / IIC_NODE_STRIDE) * 0x10;
        void __iomem *cfg = spider_get_irq_config(irq);
        irq = spider_get_nr(irq);
 
-       out_be32(cfg, in_be32(cfg) | 0x3107000eu);
+       out_be32(cfg, in_be32(cfg) | 0x3107000eu | nodeid);
        out_be32(cfg + 4, in_be32(cfg + 4) | 0x00020000u | irq);
 }
 
@@ -131,61 +132,108 @@ static struct hw_interrupt_type spider_pic = {
        .end = spider_end_irq,
 };
 
-
-int spider_get_irq(unsigned long int_pending)
+int spider_get_irq(int node)
 {
-       void __iomem *regs = spider_get_pic(int_pending);
        unsigned long cs;
-       int irq;
-
-       cs = in_be32(regs + TIR_CS);
+       void __iomem *regs = spider_pics[node];
 
-       irq = cs >> 24;
-       if (irq != 63)
-               return irq;
+       cs = in_be32(regs + TIR_CS) >> 24;
 
-       return -1;
+       if (cs == 63)
+               return -1;
+       else
+               return cs;
 }
-void spider_init_IRQ(void)
+
+/* hardcoded part to be compatible with older firmware */
+
+void spider_init_IRQ_hardcoded(void)
 {
        int node;
-       struct device_node *dn;
-       unsigned int *property;
        long spiderpic;
+       long pics[] = { 0x24000008000, 0x34000008000 };
        int n;
 
-/* FIXME: detect multiple PICs as soon as the device tree has them */
-       for (node = 0; node < 1; node++) {
-               dn = of_find_node_by_path("/");
-               n = prom_n_addr_cells(dn);
-               property = (unsigned int *) get_property(dn,
-                               "platform-spider-pic", NULL);
+       pr_debug("%s(%d): Using hardcoded defaults\n", __FUNCTION__, __LINE__);
 
-               if (!property)
-                       continue;
-               for (spiderpic = 0; n > 0; --n)
-                       spiderpic = (spiderpic << 32) + *property++;
+       for (node = 0; node < num_present_cpus()/2; node++) {
+               spiderpic = pics[node];
                printk(KERN_DEBUG "SPIDER addr: %lx\n", spiderpic);
                spider_pics[node] = __ioremap(spiderpic, 0x800, _PAGE_NO_CACHE);
                for (n = 0; n < IIC_NUM_EXT; n++) {
                        int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
                        get_irq_desc(irq)->handler = &spider_pic;
+               }
 
                /* do not mask any interrupts because of level */
                out_be32(spider_pics[node] + TIR_MSK, 0x0);
-               
+
                /* disable edge detection clear */
                /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
-               
+
                /* enable interrupt packets to be output */
                out_be32(spider_pics[node] + TIR_PIEN,
                        in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
-               
+
                /* Enable the interrupt detection enable bit. Do this last! */
                out_be32(spider_pics[node] + TIR_DEN,
-                       in_be32(spider_pics[node] +TIR_DEN) | 0x1);
+                       in_be32(spider_pics[node] + TIR_DEN) | 0x1);
+       }
+}
+
+void spider_init_IRQ(void)
+{
+       long spider_reg;
+       struct device_node *dn;
+       char *compatible;
+       int n, node = 0;
+
+       for (dn = NULL; (dn = of_find_node_by_name(dn, "interrupt-controller"));) {
+               compatible = (char *)get_property(dn, "compatible", NULL);
 
+               if (!compatible)
+                       continue;
+
+               if (strstr(compatible, "CBEA,platform-spider-pic"))
+                       spider_reg = *(long *)get_property(dn,"reg", NULL);
+               else if (strstr(compatible, "sti,platform-spider-pic")) {
+                       spider_init_IRQ_hardcoded();
+                       return;
+               } else
+                       continue;
+
+               if (!spider_reg)
+                       printk("interrupt controller does not have reg property !\n");
+
+               n = prom_n_addr_cells(dn);
+
+               if ( n != 2)
+                       printk("reg property with invalid number of elements \n");
+
+               spider_pics[node] = __ioremap(spider_reg, 0x800, _PAGE_NO_CACHE);
+
+               printk("SPIDER addr: %lx with %i addr_cells mapped to %p\n",
+                      spider_reg, n, spider_pics[node]);
+
+               for (n = 0; n < IIC_NUM_EXT; n++) {
+                       int irq = n + IIC_EXT_OFFSET + node * IIC_NODE_STRIDE;
+                       get_irq_desc(irq)->handler = &spider_pic;
                }
+
+               /* do not mask any interrupts because of level */
+               out_be32(spider_pics[node] + TIR_MSK, 0x0);
+
+               /* disable edge detection clear */
+               /* out_be32(spider_pics[node] + TIR_EDC, 0x0); */
+
+               /* enable interrupt packets to be output */
+               out_be32(spider_pics[node] + TIR_PIEN,
+                       in_be32(spider_pics[node] + TIR_PIEN) | 0x1);
+
+               /* Enable the interrupt detection enable bit. Do this last! */
+               out_be32(spider_pics[node] + TIR_DEN,
+                       in_be32(spider_pics[node] + TIR_DEN) | 0x1);
+
+               node++;
        }
 }