#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
{
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) {
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