vserver 1.9.5.x5
[linux-2.6.git] / drivers / parisc / gsc.c
index 5a17a00..1796084 100644 (file)
 
 #include <asm/hardware.h>
 #include <asm/io.h>
-#include <asm/irq.h>
 
 #include "gsc.h"
 
-/* This sets the vmerge boundary and size, it's here because it has to
- * be available on all platforms (zero means no-virtual merging) */
-unsigned long parisc_vmerge_boundary = 0;
-unsigned long parisc_vmerge_max_size = 0;
-
 #undef DEBUG
 
 #ifdef DEBUG
@@ -61,7 +55,7 @@ int gsc_claim_irq(struct gsc_irq *i, int irq)
 {
        int c = irq;
 
-       irq += IRQ_FROM_REGION(CPU_IRQ_REGION); /* virtualize the IRQ first */
+       irq += CPU_IRQ_BASE; /* virtualize the IRQ first */
 
        irq = txn_claim_irq(irq);
        if (irq < 0) {
@@ -79,116 +73,146 @@ int gsc_claim_irq(struct gsc_irq *i, int irq)
 EXPORT_SYMBOL(gsc_alloc_irq);
 EXPORT_SYMBOL(gsc_claim_irq);
 
-/* IRQ bits must be numbered from Most Significant Bit */
-#define GSC_FIX_IRQ(x) (31-(x))
-#define GSC_MASK_IRQ(x)        (1<<(GSC_FIX_IRQ(x)))
-
 /* Common interrupt demultiplexer used by Asp, Lasi & Wax.  */
-irqreturn_t busdev_barked(int busdev_irq, void *dev, struct pt_regs *regs)
+irqreturn_t gsc_asic_intr(int gsc_asic_irq, void *dev, struct pt_regs *regs)
 {
-       unsigned long irq;
-       struct busdevice *busdev = (struct busdevice *) dev;
-
-       /* 
-           Don't need to protect OFFSET_IRR with spinlock since this is
-           the only place it's touched.
-           Protect busdev_region by disabling this region's interrupts,
-           modifying the region, and then re-enabling the region.
-       */
-
-       irq = gsc_readl(busdev->hpa+OFFSET_IRR);
-       if (irq == 0) {
-               printk(KERN_ERR "%s: barking without apparent reason.\n", busdev->name);
-       } else {
-               DEBPRINTK ("%s (0x%x) barked, mask=0x%x, irq=%d\n", 
-                   busdev->name, busdev->busdev_region->data.irqbase, 
-                   irq, GSC_FIX_IRQ(ffs(irq))+1 );
-
-               do_irq_mask(irq, busdev->busdev_region, regs);
-       }
+       unsigned long irr;
+       struct gsc_asic *gsc_asic = dev;
+
+       irr = gsc_readl(gsc_asic->hpa + OFFSET_IRR);
+       if (irr == 0)
+               return IRQ_NONE;
+
+       DEBPRINTK("%s intr, mask=0x%x\n", gsc_asic->name, irr);
+
+       do {
+               int local_irq = __ffs(irr);
+               unsigned int irq = gsc_asic->global_irq[local_irq];
+               __do_IRQ(irq, regs);
+               irr &= ~(1 << local_irq);
+       } while (irr);
+
        return IRQ_HANDLED;
 }
 
-static void
-busdev_disable_irq(void *irq_dev, int irq)
+int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit)
 {
-       /* Disable the IRQ line by clearing the bit in the IMR */
-       u32 imr = gsc_readl(BUSDEV_DEV(irq_dev)->hpa+OFFSET_IMR);
-       imr &= ~(GSC_MASK_IRQ(irq));
+       int local_irq;
 
-       DEBPRINTK( KERN_WARNING "%s(%p, %d) %s: IMR 0x%x\n", 
-                   __FUNCTION__, irq_dev, irq, BUSDEV_DEV(irq_dev)->name, imr);
+       for (local_irq = 0; local_irq < limit; local_irq++) {
+               if (global_irqs[local_irq] == irq)
+                       return local_irq;
+       }
 
-       gsc_writel(imr, BUSDEV_DEV(irq_dev)->hpa+OFFSET_IMR);
+       return NO_IRQ;
 }
 
+static void gsc_asic_disable_irq(unsigned int irq)
+{
+       struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+       int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
+       u32 imr;
+
+       DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __FUNCTION__, irq,
+                       irq_dev->name, imr);
 
-static void
-busdev_enable_irq(void *irq_dev, int irq)
+       /* Disable the IRQ line by clearing the bit in the IMR */
+       imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
+       imr &= ~(1 << local_irq);
+       gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
+}
+
+static void gsc_asic_enable_irq(unsigned int irq)
 {
-       /* Enable the IRQ line by setting the bit in the IMR */
-       unsigned long addr = BUSDEV_DEV(irq_dev)->hpa + OFFSET_IMR;
-       u32 imr = gsc_readl(addr);
-       imr |= GSC_MASK_IRQ(irq);
+       struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+       int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
+       u32 imr;
 
-       DEBPRINTK (KERN_WARNING "%s(%p, %d) %s: IMR 0x%x\n", 
-                   __FUNCTION__, irq_dev, irq, BUSDEV_DEV(irq_dev)->name, imr);
+       DEBPRINTK(KERN_DEBUG "%s(%d) %s: IMR 0x%x\n", __FUNCTION__, irq,
+                       irq_dev->name, imr);
 
-       gsc_writel(imr, addr);
-//     gsc_writel(~0L, addr);
+       /* Enable the IRQ line by setting the bit in the IMR */
+       imr = gsc_readl(irq_dev->hpa + OFFSET_IMR);
+       imr |= 1 << local_irq;
+       gsc_writel(imr, irq_dev->hpa + OFFSET_IMR);
+       /*
+        * FIXME: read IPR to make sure the IRQ isn't already pending.
+        *   If so, we need to read IRR and manually call do_irq().
+        */
+}
 
-/* FIXME: read IPR to make sure the IRQ isn't already pending.
-**   If so, we need to read IRR and manually call do_irq_mask().
-**   This code should be shared with busdev_unmask_irq().
-*/
+static unsigned int gsc_asic_startup_irq(unsigned int irq)
+{
+       gsc_asic_enable_irq(irq);
+       return 0;
 }
 
-static void
-busdev_mask_irq(void *irq_dev, int irq)
+static struct hw_interrupt_type gsc_asic_interrupt_type = {
+       .typename =     "GSC-ASIC",
+       .startup =      gsc_asic_startup_irq,
+       .shutdown =     gsc_asic_disable_irq,
+       .enable =       gsc_asic_enable_irq,
+       .disable =      gsc_asic_disable_irq,
+       .ack =          no_ack_irq,
+       .end =          no_end_irq,
+};
+
+int gsc_assign_irq(struct hw_interrupt_type *type, void *data)
 {
-/* FIXME: Clear the IMR bit in busdev for that IRQ */
+       static int irq = GSC_IRQ_BASE;
+
+       if (irq > GSC_IRQ_MAX)
+               return NO_IRQ;
+
+       irq_desc[irq].handler = type;
+       irq_desc[irq].handler_data = data;
+       return irq++;
 }
 
-static void
-busdev_unmask_irq(void *irq_dev, int irq)
+void gsc_asic_assign_irq(struct gsc_asic *asic, int local_irq, int *irqp)
 {
-/* FIXME: Read IPR. Set the IMR bit in busdev for that IRQ.
-   call do_irq_mask() if IPR is non-zero
-*/
+       int irq = gsc_assign_irq(&gsc_asic_interrupt_type, asic);
+       if (irq == NO_IRQ)
+               return;
+
+       *irqp = irq;
+       asic->global_irq[local_irq] = irq;
 }
 
-struct irq_region_ops busdev_irq_ops = {
-       .disable_irq =  busdev_disable_irq,
-       .enable_irq =   busdev_enable_irq,
-       .mask_irq =     busdev_mask_irq,
-       .unmask_irq =   busdev_unmask_irq
-};
+void gsc_fixup_irqs(struct parisc_device *parent, void *ctrl,
+                       void (*choose_irq)(struct parisc_device *, void *))
+{
+       struct device *dev;
 
+       list_for_each_entry(dev, &parent->dev.children, node) {
+               struct parisc_device *padev = to_parisc_device(dev);
 
-int gsc_common_irqsetup(struct parisc_device *parent, struct busdevice *busdev)
+               /* work-around for 715/64 and others which have parent 
+                  at path [5] and children at path [5/0/x] */
+               if (padev->id.hw_type == HPHW_FAULTY)
+                       return gsc_fixup_irqs(padev, ctrl, choose_irq);
+               choose_irq(padev, ctrl);
+       }
+}
+
+int gsc_common_setup(struct parisc_device *parent, struct gsc_asic *gsc_asic)
 {
        struct resource *res;
 
-       busdev->gsc = parent;
-
-       /* the IRQs we simulate */
-       busdev->busdev_region = alloc_irq_region(32, &busdev_irq_ops,
-                                                busdev->name, busdev);
-       if (!busdev->busdev_region)
-               return -ENOMEM;
+       gsc_asic->gsc = parent;
 
        /* allocate resource region */
-       res = request_mem_region(busdev->hpa, 0x100000, busdev->name);
+       res = request_mem_region(gsc_asic->hpa, 0x100000, gsc_asic->name);
        if (res) {
                res->flags = IORESOURCE_MEM;    /* do not mark it busy ! */
        }
 
 #if 0
-       printk(KERN_WARNING "%s IRQ %d EIM 0x%x", busdev->name,
-                       busdev->parent_irq, busdev->eim);
-       if (gsc_readl(busdev->hpa + OFFSET_IMR))
+       printk(KERN_WARNING "%s IRQ %d EIM 0x%x", gsc_asic->name,
+                       parent->irq, gsc_asic->eim);
+       if (gsc_readl(gsc_asic->hpa + OFFSET_IMR))
                printk("  IMR is non-zero! (0x%x)",
-                               gsc_readl(busdev->hpa + OFFSET_IMR));
+                               gsc_readl(gsc_asic->hpa + OFFSET_IMR));
        printk("\n");
 #endif