X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fparisc%2Fgsc.c;h=17960846935edbc0be158c7af75f04820bc24102;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=5a17a00d336c157933561c8ef8b3b7625f3e2ab0;hpb=87fc8d1bb10cd459024a742c6a10961fefcef18f;p=linux-2.6.git diff --git a/drivers/parisc/gsc.c b/drivers/parisc/gsc.c index 5a17a00d3..179608469 100644 --- a/drivers/parisc/gsc.c +++ b/drivers/parisc/gsc.c @@ -25,15 +25,9 @@ #include #include -#include #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