]> err.no Git - linux-2.6/blobdiff - arch/powerpc/sysdev/uic.c
Merge branch 'for-2.6.25' of git://git.kernel.org/pub/scm/linux/kernel/git/olof/pasemi
[linux-2.6] / arch / powerpc / sysdev / uic.c
index 89059895a20d0ef8f8866759eae18855f43030d4..ae3eadddddbd7425e5de36a397717807bc5cb405 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/spinlock.h>
 #include <linux/irq.h>
 #include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
 #include <asm/irq.h>
 #include <asm/io.h>
 #include <asm/prom.h>
@@ -52,24 +53,23 @@ struct uic {
 
        /* The remapper for this UIC */
        struct irq_host *irqhost;
-
-       /* For secondary UICs, the cascade interrupt's irqaction */
-       struct irqaction cascade;
-
-       /* The device node of the interrupt controller */
-       struct device_node *of_node;
 };
 
 static void uic_unmask_irq(unsigned int virq)
 {
+       struct irq_desc *desc = get_irq_desc(virq);
        struct uic *uic = get_irq_chip_data(virq);
        unsigned int src = uic_irq_to_hw(virq);
        unsigned long flags;
-       u32 er;
+       u32 er, sr;
 
+       sr = 1 << (31-src);
        spin_lock_irqsave(&uic->lock, flags);
+       /* ack level-triggered interrupts here */
+       if (desc->status & IRQ_LEVEL)
+               mtdcr(uic->dcrbase + UIC_SR, sr);
        er = mfdcr(uic->dcrbase + UIC_ER);
-       er |= 1 << (31 - src);
+       er |= sr;
        mtdcr(uic->dcrbase + UIC_ER, er);
        spin_unlock_irqrestore(&uic->lock, flags);
 }
@@ -99,6 +99,32 @@ static void uic_ack_irq(unsigned int virq)
        spin_unlock_irqrestore(&uic->lock, flags);
 }
 
+static void uic_mask_ack_irq(unsigned int virq)
+{
+       struct irq_desc *desc = get_irq_desc(virq);
+       struct uic *uic = get_irq_chip_data(virq);
+       unsigned int src = uic_irq_to_hw(virq);
+       unsigned long flags;
+       u32 er, sr;
+
+       sr = 1 << (31-src);
+       spin_lock_irqsave(&uic->lock, flags);
+       er = mfdcr(uic->dcrbase + UIC_ER);
+       er &= ~sr;
+       mtdcr(uic->dcrbase + UIC_ER, er);
+       /* On the UIC, acking (i.e. clearing the SR bit)
+        * a level irq will have no effect if the interrupt
+        * is still asserted by the device, even if
+        * the interrupt is already masked. Therefore
+        * we only ack the egde interrupts here, while
+        * level interrupts are ack'ed after the actual
+        * isr call in the uic_unmask_irq()
+        */
+       if (!(desc->status & IRQ_LEVEL))
+               mtdcr(uic->dcrbase + UIC_SR, sr);
+       spin_unlock_irqrestore(&uic->lock, flags);
+}
+
 static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
 {
        struct uic *uic = get_irq_chip_data(virq);
@@ -142,7 +168,7 @@ static int uic_set_irq_type(unsigned int virq, unsigned int flow_type)
 
        desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL);
        desc->status |= flow_type & IRQ_TYPE_SENSE_MASK;
-       if (trigger)
+       if (!trigger)
                desc->status |= IRQ_LEVEL;
 
        spin_unlock_irqrestore(&uic->lock, flags);
@@ -154,17 +180,11 @@ static struct irq_chip uic_irq_chip = {
        .typename       = " UIC  ",
        .unmask         = uic_unmask_irq,
        .mask           = uic_mask_irq,
-/*     .mask_ack       = uic_mask_irq_and_ack, */
+       .mask_ack       = uic_mask_ack_irq,
        .ack            = uic_ack_irq,
        .set_type       = uic_set_irq_type,
 };
 
-static int uic_host_match(struct irq_host *h, struct device_node *node)
-{
-       struct uic *uic = h->host_data;
-       return uic->of_node == node;
-}
-
 static int uic_host_map(struct irq_host *h, unsigned int virq,
                        irq_hw_number_t hw)
 {
@@ -194,25 +214,40 @@ static int uic_host_xlate(struct irq_host *h, struct device_node *ct,
 }
 
 static struct irq_host_ops uic_host_ops = {
-       .match  = uic_host_match,
        .map    = uic_host_map,
        .xlate  = uic_host_xlate,
 };
 
-irqreturn_t uic_cascade(int virq, void *data)
+void uic_irq_cascade(unsigned int virq, struct irq_desc *desc)
 {
-       struct uic *uic = data;
+       struct uic *uic = get_irq_data(virq);
        u32 msr;
        int src;
        int subvirq;
 
+       spin_lock(&desc->lock);
+       if (desc->status & IRQ_LEVEL)
+               desc->chip->mask(virq);
+       else
+               desc->chip->mask_ack(virq);
+       spin_unlock(&desc->lock);
+
        msr = mfdcr(uic->dcrbase + UIC_MSR);
+       if (!msr) /* spurious interrupt */
+               goto uic_irq_ret;
+
        src = 32 - ffs(msr);
 
        subvirq = irq_linear_revmap(uic->irqhost, src);
        generic_handle_irq(subvirq);
 
-       return IRQ_HANDLED;
+uic_irq_ret:
+       spin_lock(&desc->lock);
+       if (desc->status & IRQ_LEVEL)
+               desc->chip->ack(virq);
+       if (!(desc->status & IRQ_DISABLED) && desc->chip->unmask)
+               desc->chip->unmask(virq);
+       spin_unlock(&desc->lock);
 }
 
 static struct uic * __init uic_init_one(struct device_node *node)
@@ -229,7 +264,6 @@ static struct uic * __init uic_init_one(struct device_node *node)
 
        memset(uic, 0, sizeof(*uic));
        spin_lock_init(&uic->lock);
-       uic->of_node = of_node_get(node);
        indexp = of_get_property(node, "cell-index", &len);
        if (!indexp || (len != sizeof(u32))) {
                printk(KERN_ERR "uic: Device node %s has missing or invalid "
@@ -246,8 +280,8 @@ static struct uic * __init uic_init_one(struct device_node *node)
        }
        uic->dcrbase = *dcrreg;
 
-       uic->irqhost = irq_alloc_host(IRQ_HOST_MAP_LINEAR, NR_UIC_INTS,
-                                     &uic_host_ops, -1);
+       uic->irqhost = irq_alloc_host(of_node_get(node), IRQ_HOST_MAP_LINEAR,
+                                     NR_UIC_INTS, &uic_host_ops, -1);
        if (! uic->irqhost) {
                of_node_put(node);
                return NULL; /* FIXME: panic? */
@@ -301,7 +335,6 @@ void __init uic_init_tree(void)
                if (interrupts) {
                        /* Secondary UIC */
                        int cascade_virq;
-                       int ret;
 
                        uic = uic_init_one(np);
                        if (! uic)
@@ -310,15 +343,8 @@ void __init uic_init_tree(void)
 
                        cascade_virq = irq_of_parse_and_map(np, 0);
 
-                       uic->cascade.handler = uic_cascade;
-                       uic->cascade.name = "UIC cascade";
-                       uic->cascade.dev_id = uic;
-
-                       ret = setup_irq(cascade_virq, &uic->cascade);
-                       if (ret)
-                               printk(KERN_ERR "Failed to setup_irq(%d) for "
-                                      "UIC%d cascade\n", cascade_virq,
-                                      uic->index);
+                       set_irq_data(cascade_virq, uic);
+                       set_irq_chained_handler(cascade_virq, uic_irq_cascade);
 
                        /* FIXME: setup critical cascade?? */
                }