[SPARC64]: Add support for IRQ pre-handlers.
authorDavid S. Miller <davem@davemloft.net>
Mon, 4 Jul 2005 20:24:38 +0000 (13:24 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 4 Jul 2005 20:24:38 +0000 (13:24 -0700)
This allows a PCI controller to shim into IRQ delivery
so that DMA queues can be drained, if necessary.

If some bus specific code needs to run before an IRQ
handler is invoked, the bus driver simply needs to setup
the function pointer in bucket->irq_info->pre_handler and
the two args bucket->irq_info->pre_handler_arg[12].

The Schizo PCI driver is converted over to use a pre-handler
for the DMA write-sync processing it needs when a device
is behind a PCI->PCI bus deeper than the top-level APB
bridges.

While we're here, clean up all of the action allocation
and handling.  Now, we allocate the irqaction as part of
the bucket->irq_info area.  There is an array of 4 irqaction
(for PCI irq sharing) and a bitmask saying which entries
are active.

The bucket->irq_info is allocated at build_irq() time, not
at request_irq() time.  This simplifies request_irq() and
free_irq() tremendously.

The SMP dynamic IRQ retargetting code got removed in this
change too.  It was disabled for a few months now, and we
can resurrect it in the future if we want.

Signed-off-by: David S. Miller <davem@davemloft.net>
arch/sparc64/kernel/entry.S
arch/sparc64/kernel/irq.c
arch/sparc64/kernel/pci_sabre.c
arch/sparc64/kernel/time.c
include/asm-sparc64/irq.h
include/asm-sparc64/signal.h

index eee516a71c14b73bb103ef33a91dd3029f41e64f..d3973d8a7195a5adc622db19218fed74aa046975 100644 (file)
@@ -553,13 +553,11 @@ do_ivec:
        sllx            %g3, 5, %g3
        or              %g2, %lo(ivector_table), %g2
        add             %g2, %g3, %g3
-       ldx             [%g3 + 0x08], %g2       /* irq_info */
        ldub            [%g3 + 0x04], %g4       /* pil */
-       brz,pn          %g2, do_ivec_spurious
-        mov            1, %g2
-
+       mov             1, %g2
        sllx            %g2, %g4, %g2
        sllx            %g4, 2, %g4
+
        lduw            [%g6 + %g4], %g5        /* g5 = irq_work(cpu, pil) */
        stw             %g5, [%g3 + 0x00]       /* bucket->irq_chain = g5 */
        stw             %g3, [%g6 + %g4]        /* irq_work(cpu, pil) = bucket */
@@ -567,9 +565,9 @@ do_ivec:
        retry
 do_ivec_xcall:
        mov             0x50, %g1
-
        ldxa            [%g1 + %g0] ASI_INTR_R, %g1
        srl             %g3, 0, %g3
+
        mov             0x60, %g7
        ldxa            [%g7 + %g0] ASI_INTR_R, %g7
        stxa            %g0, [%g0] ASI_INTR_RECEIVE
@@ -581,19 +579,6 @@ do_ivec_xcall:
 1:     jmpl            %g3, %g0
         nop
 
-do_ivec_spurious:
-       stw             %g3, [%g6 + 0x00]       /* irq_work(cpu, 0) = bucket */
-       rdpr            %pstate, %g5
-
-       wrpr            %g5, PSTATE_IG | PSTATE_AG, %pstate
-       sethi           %hi(109f), %g7
-       ba,pt           %xcc, etrap
-109:    or             %g7, %lo(109b), %g7
-       call            catch_disabled_ivec
-        add            %sp, PTREGS_OFF, %o0
-       ba,pt           %xcc, rtrap
-        clr            %l6
-
        .globl          save_alternate_globals
 save_alternate_globals: /* %o0 = save_area */
        rdpr            %pstate, %o5
index 424712577307d07e0cab4a2e92b07e8cc58b45fe..74a2e0808cbcda8939a2708c37aa5cec96f08816 100644 (file)
@@ -71,31 +71,7 @@ struct irq_work_struct {
 struct irq_work_struct __irq_work[NR_CPUS];
 #define irq_work(__cpu, __pil) &(__irq_work[(__cpu)].irq_worklists[(__pil)])
 
-#ifdef CONFIG_PCI
-/* This is a table of physical addresses used to deal with IBF_DMA_SYNC.
- * It is used for PCI only to synchronize DMA transfers with IRQ delivery
- * for devices behind busses other than APB on Sabre systems.
- *
- * Currently these physical addresses are just config space accesses
- * to the command register for that device.
- */
-unsigned long pci_dma_wsync;
-unsigned long dma_sync_reg_table[256];
-unsigned char dma_sync_reg_table_entry = 0;
-#endif
-
-/* This is based upon code in the 32-bit Sparc kernel written mostly by
- * David Redman (djhr@tadpole.co.uk).
- */
-#define MAX_STATIC_ALLOC       4
-static struct irqaction static_irqaction[MAX_STATIC_ALLOC];
-static int static_irq_count;
-
-/* This is exported so that fast IRQ handlers can get at it... -DaveM */
-struct irqaction *irq_action[NR_IRQS+1] = {
-         NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL,
-         NULL, NULL, NULL, NULL, NULL, NULL , NULL, NULL
-};
+static struct irqaction *irq_action[NR_IRQS+1];
 
 /* This only synchronizes entities which modify IRQ handler
  * state and some selected user-level spots that want to
@@ -241,17 +217,22 @@ void disable_irq(unsigned int irq)
  * the CPU %tick register and not by some normal vectored interrupt
  * source.  To handle this special case, we use this dummy INO bucket.
  */
+static struct irq_desc pil0_dummy_desc;
 static struct ino_bucket pil0_dummy_bucket = {
-       0,      /* irq_chain */
-       0,      /* pil */
-       0,      /* pending */
-       0,      /* flags */
-       0,      /* __unused */
-       NULL,   /* irq_info */
-       0UL,    /* iclr */
-       0UL,    /* imap */
+       .irq_info       =       &pil0_dummy_desc,
 };
 
+static void build_irq_error(const char *msg, unsigned int ino, int pil, int inofixup,
+                           unsigned long iclr, unsigned long imap,
+                           struct ino_bucket *bucket)
+{
+       prom_printf("IRQ: INO %04x (%d:%016lx:%016lx) --> "
+                   "(%d:%d:%016lx:%016lx), halting...\n",
+                   ino, bucket->pil, bucket->iclr, bucket->imap,
+                   pil, inofixup, iclr, imap);
+       prom_halt();
+}
+
 unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long imap)
 {
        struct ino_bucket *bucket;
@@ -280,28 +261,35 @@ unsigned int build_irq(int pil, int inofixup, unsigned long iclr, unsigned long
                prom_halt();
        }
 
-       /* Ok, looks good, set it up.  Don't touch the irq_chain or
-        * the pending flag.
-        */
        bucket = &ivector_table[ino];
-       if ((bucket->flags & IBF_ACTIVE) ||
-           (bucket->irq_info != NULL)) {
-               /* This is a gross fatal error if it happens here. */
-               prom_printf("IRQ: Trying to reinit INO bucket, fatal error.\n");
-               prom_printf("IRQ: Request INO %04x (%d:%d:%016lx:%016lx)\n",
-                           ino, pil, inofixup, iclr, imap);
-               prom_printf("IRQ: Existing (%d:%016lx:%016lx)\n",
-                           bucket->pil, bucket->iclr, bucket->imap);
-               prom_printf("IRQ: Cannot continue, halting...\n");
+       if (bucket->flags & IBF_ACTIVE)
+               build_irq_error("IRQ: Trying to build active INO bucket.\n",
+                               ino, pil, inofixup, iclr, imap, bucket);
+
+       if (bucket->irq_info) {
+               if (bucket->imap != imap || bucket->iclr != iclr)
+                       build_irq_error("IRQ: Trying to reinit INO bucket.\n",
+                                       ino, pil, inofixup, iclr, imap, bucket);
+
+               goto out;
+       }
+
+       bucket->irq_info = kmalloc(sizeof(struct irq_desc), GFP_ATOMIC);
+       if (!bucket->irq_info) {
+               prom_printf("IRQ: Error, kmalloc(irq_desc) failed.\n");
                prom_halt();
        }
+       memset(bucket->irq_info, 0, sizeof(struct irq_desc));
+
+       /* Ok, looks good, set it up.  Don't touch the irq_chain or
+        * the pending flag.
+        */
        bucket->imap  = imap;
        bucket->iclr  = iclr;
        bucket->pil   = pil;
        bucket->flags = 0;
 
-       bucket->irq_info = NULL;
-
+out:
        return __irq(bucket);
 }
 
@@ -319,26 +307,65 @@ static void atomic_bucket_insert(struct ino_bucket *bucket)
        __asm__ __volatile__("wrpr %0, 0x0, %%pstate" : : "r" (pstate));
 }
 
+static int check_irq_sharing(int pil, unsigned long irqflags)
+{
+       struct irqaction *action, *tmp;
+
+       action = *(irq_action + pil);
+       if (action) {
+               if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ)) {
+                       for (tmp = action; tmp->next; tmp = tmp->next)
+                               ;
+               } else {
+                       return -EBUSY;
+               }
+       }
+       return 0;
+}
+
+static void append_irq_action(int pil, struct irqaction *action)
+{
+       struct irqaction **pp = irq_action + pil;
+
+       while (*pp)
+               pp = &((*pp)->next);
+       *pp = action;
+}
+
+static struct irqaction *get_action_slot(struct ino_bucket *bucket)
+{
+       struct irq_desc *desc = bucket->irq_info;
+       int max_irq, i;
+
+       max_irq = 1;
+       if (bucket->flags & IBF_PCI)
+               max_irq = MAX_IRQ_DESC_ACTION;
+       for (i = 0; i < max_irq; i++) {
+               struct irqaction *p = &desc->action[i];
+               u32 mask = (1 << i);
+
+               if (desc->action_active_mask & mask)
+                       continue;
+
+               desc->action_active_mask |= mask;
+               return p;
+       }
+       return NULL;
+}
+
 int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_regs *),
                unsigned long irqflags, const char *name, void *dev_id)
 {
-       struct irqaction *action, *tmp = NULL;
+       struct irqaction *action;
        struct ino_bucket *bucket = __bucket(irq);
        unsigned long flags;
        int pending = 0;
 
-       if ((bucket != &pil0_dummy_bucket) &&
-           (bucket < &ivector_table[0] ||
-            bucket >= &ivector_table[NUM_IVECS])) {
-               unsigned int *caller;
-
-               __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
-               printk(KERN_CRIT "request_irq: Old style IRQ registry attempt "
-                      "from %p, irq %08x.\n", caller, irq);
+       if (unlikely(!handler))
                return -EINVAL;
-       }       
-       if (!handler)
-           return -EINVAL;
+
+       if (unlikely(!bucket->irq_info))
+               return -ENODEV;
 
        if ((bucket != &pil0_dummy_bucket) && (irqflags & SA_SAMPLE_RANDOM)) {
                /*
@@ -356,93 +383,20 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
 
        spin_lock_irqsave(&irq_action_lock, flags);
 
-       action = *(bucket->pil + irq_action);
-       if (action) {
-               if ((action->flags & SA_SHIRQ) && (irqflags & SA_SHIRQ))
-                       for (tmp = action; tmp->next; tmp = tmp->next)
-                               ;
-               else {
-                       spin_unlock_irqrestore(&irq_action_lock, flags);
-                       return -EBUSY;
-               }
-               action = NULL;          /* Or else! */
+       if (check_irq_sharing(bucket->pil, irqflags)) {
+               spin_unlock_irqrestore(&irq_action_lock, flags);
+               return -EBUSY;
        }
 
-       /* If this is flagged as statically allocated then we use our
-        * private struct which is never freed.
-        */
-       if (irqflags & SA_STATIC_ALLOC) {
-           if (static_irq_count < MAX_STATIC_ALLOC)
-               action = &static_irqaction[static_irq_count++];
-           else
-               printk("Request for IRQ%d (%s) SA_STATIC_ALLOC failed "
-                      "using kmalloc\n", irq, name);
-       }       
-       if (action == NULL)
-           action = (struct irqaction *)kmalloc(sizeof(struct irqaction),
-                                                GFP_ATOMIC);
-       
+       action = get_action_slot(bucket);
        if (!action) { 
                spin_unlock_irqrestore(&irq_action_lock, flags);
                return -ENOMEM;
        }
 
-       if (bucket == &pil0_dummy_bucket) {
-               bucket->irq_info = action;
-               bucket->flags |= IBF_ACTIVE;
-       } else {
-               if ((bucket->flags & IBF_ACTIVE) != 0) {
-                       void *orig = bucket->irq_info;
-                       void **vector = NULL;
-
-                       if ((bucket->flags & IBF_PCI) == 0) {
-                               printk("IRQ: Trying to share non-PCI bucket.\n");
-                               goto free_and_ebusy;
-                       }
-                       if ((bucket->flags & IBF_MULTI) == 0) {
-                               vector = kmalloc(sizeof(void *) * 4, GFP_ATOMIC);
-                               if (vector == NULL)
-                                       goto free_and_enomem;
-
-                               /* We might have slept. */
-                               if ((bucket->flags & IBF_MULTI) != 0) {
-                                       int ent;
-
-                                       kfree(vector);
-                                       vector = (void **)bucket->irq_info;
-                                       for(ent = 0; ent < 4; ent++) {
-                                               if (vector[ent] == NULL) {
-                                                       vector[ent] = action;
-                                                       break;
-                                               }
-                                       }
-                                       if (ent == 4)
-                                               goto free_and_ebusy;
-                               } else {
-                                       vector[0] = orig;
-                                       vector[1] = action;
-                                       vector[2] = NULL;
-                                       vector[3] = NULL;
-                                       bucket->irq_info = vector;
-                                       bucket->flags |= IBF_MULTI;
-                               }
-                       } else {
-                               int ent;
-
-                               vector = (void **)orig;
-                               for (ent = 0; ent < 4; ent++) {
-                                       if (vector[ent] == NULL) {
-                                               vector[ent] = action;
-                                               break;
-                                       }
-                               }
-                               if (ent == 4)
-                                       goto free_and_ebusy;
-                       }
-               } else {
-                       bucket->irq_info = action;
-                       bucket->flags |= IBF_ACTIVE;
-               }
+       bucket->flags |= IBF_ACTIVE;
+       pending = 0;
+       if (bucket != &pil0_dummy_bucket) {
                pending = bucket->pending;
                if (pending)
                        bucket->pending = 0;
@@ -456,10 +410,7 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
        put_ino_in_irqaction(action, irq);
        put_smpaff_in_irqaction(action, CPU_MASK_NONE);
 
-       if (tmp)
-               tmp->next = action;
-       else
-               *(bucket->pil + irq_action) = action;
+       append_irq_action(bucket->pil, action);
 
        enable_irq(irq);
 
@@ -468,147 +419,103 @@ int request_irq(unsigned int irq, irqreturn_t (*handler)(int, void *, struct pt_
                atomic_bucket_insert(bucket);
                set_softint(1 << bucket->pil);
        }
+
        spin_unlock_irqrestore(&irq_action_lock, flags);
-       if ((bucket != &pil0_dummy_bucket) && (!(irqflags & SA_STATIC_ALLOC)))
+
+       if (bucket != &pil0_dummy_bucket)
                register_irq_proc(__irq_ino(irq));
 
 #ifdef CONFIG_SMP
        distribute_irqs();
 #endif
        return 0;
-
-free_and_ebusy:
-       kfree(action);
-       spin_unlock_irqrestore(&irq_action_lock, flags);
-       return -EBUSY;
-
-free_and_enomem:
-       kfree(action);
-       spin_unlock_irqrestore(&irq_action_lock, flags);
-       return -ENOMEM;
 }
 
 EXPORT_SYMBOL(request_irq);
 
-void free_irq(unsigned int irq, void *dev_id)
+static struct irqaction *unlink_irq_action(unsigned int irq, void *dev_id)
 {
-       struct irqaction *action;
-       struct irqaction *tmp = NULL;
-       unsigned long flags;
-       struct ino_bucket *bucket = __bucket(irq), *bp;
-
-       if ((bucket != &pil0_dummy_bucket) &&
-           (bucket < &ivector_table[0] ||
-            bucket >= &ivector_table[NUM_IVECS])) {
-               unsigned int *caller;
+       struct ino_bucket *bucket = __bucket(irq);
+       struct irqaction *action, **pp;
 
-               __asm__ __volatile__("mov %%i7, %0" : "=r" (caller));
-               printk(KERN_CRIT "free_irq: Old style IRQ removal attempt "
-                      "from %p, irq %08x.\n", caller, irq);
-               return;
-       }
-       
-       spin_lock_irqsave(&irq_action_lock, flags);
+       pp = irq_action + bucket->pil;
+       action = *pp;
+       if (unlikely(!action))
+               return NULL;
 
-       action = *(bucket->pil + irq_action);
-       if (!action->handler) {
+       if (unlikely(!action->handler)) {
                printk("Freeing free IRQ %d\n", bucket->pil);
-               return;
-       }
-       if (dev_id) {
-               for ( ; action; action = action->next) {
-                       if (action->dev_id == dev_id)
-                               break;
-                       tmp = action;
-               }
-               if (!action) {
-                       printk("Trying to free free shared IRQ %d\n", bucket->pil);
-                       spin_unlock_irqrestore(&irq_action_lock, flags);
-                       return;
-               }
-       } else if (action->flags & SA_SHIRQ) {
-               printk("Trying to free shared IRQ %d with NULL device ID\n", bucket->pil);
-               spin_unlock_irqrestore(&irq_action_lock, flags);
-               return;
+               return NULL;
        }
 
-       if (action->flags & SA_STATIC_ALLOC) {
-               printk("Attempt to free statically allocated IRQ %d (%s)\n",
-                      bucket->pil, action->name);
-               spin_unlock_irqrestore(&irq_action_lock, flags);
-               return;
+       while (action && action->dev_id != dev_id) {
+               pp = &action->next;
+               action = *pp;
        }
 
-       if (action && tmp)
-               tmp->next = action->next;
-       else
-               *(bucket->pil + irq_action) = action->next;
+       if (likely(action))
+               *pp = action->next;
+
+       return action;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+       struct irqaction *action;
+       struct ino_bucket *bucket;
+       unsigned long flags;
+
+       spin_lock_irqsave(&irq_action_lock, flags);
+
+       action = unlink_irq_action(irq, dev_id);
 
        spin_unlock_irqrestore(&irq_action_lock, flags);
 
+       if (unlikely(!action))
+               return;
+
        synchronize_irq(irq);
 
        spin_lock_irqsave(&irq_action_lock, flags);
 
+       bucket = __bucket(irq);
        if (bucket != &pil0_dummy_bucket) {
+               struct irq_desc *desc = bucket->irq_info;
                unsigned long imap = bucket->imap;
-               void **vector, *orig;
-               int ent;
-
-               orig = bucket->irq_info;
-               vector = (void **)orig;
-
-               if ((bucket->flags & IBF_MULTI) != 0) {
-                       int other = 0;
-                       void *orphan = NULL;
-                       for (ent = 0; ent < 4; ent++) {
-                               if (vector[ent] == action)
-                                       vector[ent] = NULL;
-                               else if (vector[ent] != NULL) {
-                                       orphan = vector[ent];
-                                       other++;
-                               }
-                       }
+               int ent, i;
 
-                       /* Only free when no other shared irq
-                        * uses this bucket.
-                        */
-                       if (other) {
-                               if (other == 1) {
-                                       /* Convert back to non-shared bucket. */
-                                       bucket->irq_info = orphan;
-                                       bucket->flags &= ~(IBF_MULTI);
-                                       kfree(vector);
-                               }
-                               goto out;
+               for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) {
+                       struct irqaction *p = &desc->action[i];
+
+                       if (p == action) {
+                               desc->action_active_mask &= ~(1 << i);
+                               break;
                        }
-               } else {
-                       bucket->irq_info = NULL;
                }
 
-               /* This unique interrupt source is now inactive. */
-               bucket->flags &= ~IBF_ACTIVE;
+               if (!desc->action_active_mask) {
+                       /* This unique interrupt source is now inactive. */
+                       bucket->flags &= ~IBF_ACTIVE;
 
-               /* See if any other buckets share this bucket's IMAP
-                * and are still active.
-                */
-               for (ent = 0; ent < NUM_IVECS; ent++) {
-                       bp = &ivector_table[ent];
-                       if (bp != bucket        &&
-                           bp->imap == imap    &&
-                           (bp->flags & IBF_ACTIVE) != 0)
-                               break;
-               }
+                       /* See if any other buckets share this bucket's IMAP
+                        * and are still active.
+                        */
+                       for (ent = 0; ent < NUM_IVECS; ent++) {
+                               struct ino_bucket *bp = &ivector_table[ent];
+                               if (bp != bucket        &&
+                                   bp->imap == imap    &&
+                                   (bp->flags & IBF_ACTIVE) != 0)
+                                       break;
+                       }
 
-               /* Only disable when no other sub-irq levels of
-                * the same IMAP are active.
-                */
-               if (ent == NUM_IVECS)
-                       disable_irq(irq);
+                       /* Only disable when no other sub-irq levels of
+                        * the same IMAP are active.
+                        */
+                       if (ent == NUM_IVECS)
+                               disable_irq(irq);
+               }
        }
 
-out:
-       kfree(action);
        spin_unlock_irqrestore(&irq_action_lock, flags);
 }
 
@@ -647,99 +554,55 @@ void synchronize_irq(unsigned int irq)
 }
 #endif /* CONFIG_SMP */
 
-void catch_disabled_ivec(struct pt_regs *regs)
-{
-       int cpu = smp_processor_id();
-       struct ino_bucket *bucket = __bucket(*irq_work(cpu, 0));
-
-       /* We can actually see this on Ultra/PCI PCI cards, which are bridges
-        * to other devices.  Here a single IMAP enabled potentially multiple
-        * unique interrupt sources (which each do have a unique ICLR register.
-        *
-        * So what we do is just register that the IVEC arrived, when registered
-        * for real the request_irq() code will check the bit and signal
-        * a local CPU interrupt for it.
-        */
-#if 0
-       printk("IVEC: Spurious interrupt vector (%x) received at (%016lx)\n",
-              bucket - &ivector_table[0], regs->tpc);
-#endif
-       *irq_work(cpu, 0) = 0;
-       bucket->pending = 1;
-}
-
-/* Tune this... */
-#define FORWARD_VOLUME         12
-
-#ifdef CONFIG_SMP
-
-static inline void redirect_intr(int cpu, struct ino_bucket *bp)
+static void process_bucket(int irq, struct ino_bucket *bp, struct pt_regs *regs)
 {
-       /* Ok, here is what is going on:
-        * 1) Retargeting IRQs on Starfire is very
-        *    expensive so just forget about it on them.
-        * 2) Moving around very high priority interrupts
-        *    is a losing game.
-        * 3) If the current cpu is idle, interrupts are
-        *    useful work, so keep them here.  But do not
-        *    pass to our neighbour if he is not very idle.
-        * 4) If sysadmin explicitly asks for directed intrs,
-        *    Just Do It.
-        */
-       struct irqaction *ap = bp->irq_info;
-       cpumask_t cpu_mask;
-       unsigned int buddy, ticks;
+       struct irq_desc *desc = bp->irq_info;
+       unsigned char flags = bp->flags;
+       u32 action_mask, i;
+       int random;
 
-       cpu_mask = get_smpaff_in_irqaction(ap);
-       cpus_and(cpu_mask, cpu_mask, cpu_online_map);
-       if (cpus_empty(cpu_mask))
-               cpu_mask = cpu_online_map;
+       bp->flags |= IBF_INPROGRESS;
 
-       if (this_is_starfire != 0 ||
-           bp->pil >= 10 || current->pid == 0)
+       if (unlikely(!(flags & IBF_ACTIVE))) {
+               bp->pending = 1;
                goto out;
-
-       /* 'cpu' is the MID (ie. UPAID), calculate the MID
-        * of our buddy.
-        */
-       buddy = cpu + 1;
-       if (buddy >= NR_CPUS)
-               buddy = 0;
-
-       ticks = 0;
-       while (!cpu_isset(buddy, cpu_mask)) {
-               if (++buddy >= NR_CPUS)
-                       buddy = 0;
-               if (++ticks > NR_CPUS) {
-                       put_smpaff_in_irqaction(ap, CPU_MASK_NONE);
-                       goto out;
-               }
        }
 
-       if (buddy == cpu)
-               goto out;
+       if (desc->pre_handler)
+               desc->pre_handler(bp,
+                                 desc->pre_handler_arg1,
+                                 desc->pre_handler_arg2);
 
-       /* Voo-doo programming. */
-       if (cpu_data(buddy).idle_volume < FORWARD_VOLUME)
-               goto out;
+       action_mask = desc->action_active_mask;
+       random = 0;
+       for (i = 0; i < MAX_IRQ_DESC_ACTION; i++) {
+               struct irqaction *p = &desc->action[i];
+               u32 mask = (1 << i);
 
-       /* This just so happens to be correct on Cheetah
-        * at the moment.
-        */
-       buddy <<= 26;
+               if (!(action_mask & mask))
+                       continue;
+
+               action_mask &= ~mask;
 
-       /* Push it to our buddy. */
-       upa_writel(buddy | IMAP_VALID, bp->imap);
+               if (p->handler(__irq(bp), p->dev_id, regs) == IRQ_HANDLED)
+                       random |= p->flags;
 
+               if (!action_mask)
+                       break;
+       }
+       if (bp->pil != 0) {
+               upa_writel(ICLR_IDLE, bp->iclr);
+               /* Test and add entropy */
+               if (random & SA_SAMPLE_RANDOM)
+                       add_interrupt_randomness(irq);
+       }
 out:
-       return;
+       bp->flags &= ~IBF_INPROGRESS;
 }
 
-#endif
-
 void handler_irq(int irq, struct pt_regs *regs)
 {
-       struct ino_bucket *bp, *nbp;
+       struct ino_bucket *bp;
        int cpu = smp_processor_id();
 
 #ifndef CONFIG_SMP
@@ -757,8 +620,6 @@ void handler_irq(int irq, struct pt_regs *regs)
                clear_softint(clr_mask);
        }
 #else
-       int should_forward = 0;
-
        clear_softint(1 << irq);
 #endif
 
@@ -773,63 +634,12 @@ void handler_irq(int irq, struct pt_regs *regs)
 #else
        bp = __bucket(xchg32(irq_work(cpu, irq), 0));
 #endif
-       for ( ; bp != NULL; bp = nbp) {
-               unsigned char flags = bp->flags;
-               unsigned char random = 0;
+       while (bp) {
+               struct ino_bucket *nbp = __bucket(bp->irq_chain);
 
-               nbp = __bucket(bp->irq_chain);
                bp->irq_chain = 0;
-
-               bp->flags |= IBF_INPROGRESS;
-
-               if ((flags & IBF_ACTIVE) != 0) {
-#ifdef CONFIG_PCI
-                       if ((flags & IBF_DMA_SYNC) != 0) {
-                               upa_readl(dma_sync_reg_table[bp->synctab_ent]);
-                               upa_readq(pci_dma_wsync);
-                       }
-#endif
-                       if ((flags & IBF_MULTI) == 0) {
-                               struct irqaction *ap = bp->irq_info;
-                               int ret;
-
-                               ret = ap->handler(__irq(bp), ap->dev_id, regs);
-                               if (ret == IRQ_HANDLED)
-                                       random |= ap->flags;
-                       } else {
-                               void **vector = (void **)bp->irq_info;
-                               int ent;
-                               for (ent = 0; ent < 4; ent++) {
-                                       struct irqaction *ap = vector[ent];
-                                       if (ap != NULL) {
-                                               int ret;
-
-                                               ret = ap->handler(__irq(bp),
-                                                                 ap->dev_id,
-                                                                 regs);
-                                               if (ret == IRQ_HANDLED)
-                                                       random |= ap->flags;
-                                       }
-                               }
-                       }
-                       /* Only the dummy bucket lacks IMAP/ICLR. */
-                       if (bp->pil != 0) {
-#ifdef CONFIG_SMP
-                               if (should_forward) {
-                                       redirect_intr(cpu, bp);
-                                       should_forward = 0;
-                               }
-#endif
-                               upa_writel(ICLR_IDLE, bp->iclr);
-
-                               /* Test and add entropy */
-                               if (random & SA_SAMPLE_RANDOM)
-                                       add_interrupt_randomness(irq);
-                       }
-               } else
-                       bp->pending = 1;
-
-               bp->flags &= ~IBF_INPROGRESS;
+               process_bucket(irq, bp, regs);
+               bp = nbp;
        }
        irq_exit();
 }
@@ -959,7 +769,10 @@ static void distribute_irqs(void)
         */
        for (level = 1; level < NR_IRQS; level++) {
                struct irqaction *p = irq_action[level];
-               if (level == 12) continue;
+
+               if (level == 12)
+                       continue;
+
                while(p) {
                        cpu = retarget_one_irq(p, cpu);
                        p = p->next;
index 53d333b4a4e809f7ebf48231c46bb5e65af86ec3..52bf3431a42243cc323bfbbe8750a4c3622b4d3c 100644 (file)
@@ -595,6 +595,23 @@ static int __init sabre_ino_to_pil(struct pci_dev *pdev, unsigned int ino)
        return ret;
 }
 
+/* When a device lives behind a bridge deeper in the PCI bus topology
+ * than APB, a special sequence must run to make sure all pending DMA
+ * transfers at the time of IRQ delivery are visible in the coherency
+ * domain by the cpu.  This sequence is to perform a read on the far
+ * side of the non-APB bridge, then perform a read of Sabre's DMA
+ * write-sync register.
+ */
+static void sabre_wsync_handler(struct ino_bucket *bucket, void *_arg1, void *_arg2)
+{
+       struct pci_dev *pdev = _arg1;
+       unsigned long sync_reg = (unsigned long) _arg2;
+       u16 _unused;
+
+       pci_read_config_word(pdev, PCI_VENDOR_ID, &_unused);
+       sabre_read(sync_reg);
+}
+
 static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm,
                                           struct pci_dev *pdev,
                                           unsigned int ino)
@@ -639,24 +656,14 @@ static unsigned int __init sabre_irq_build(struct pci_pbm_info *pbm,
        if (pdev) {
                struct pcidev_cookie *pcp = pdev->sysdata;
 
-               /* When a device lives behind a bridge deeper in the
-                * PCI bus topology than APB, a special sequence must
-                * run to make sure all pending DMA transfers at the
-                * time of IRQ delivery are visible in the coherency
-                * domain by the cpu.  This sequence is to perform
-                * a read on the far side of the non-APB bridge, then
-                * perform a read of Sabre's DMA write-sync register.
-                *
-                * Currently, the PCI_CONFIG register for the device
-                * is used for this read from the far side of the bridge.
-                */
                if (pdev->bus->number != pcp->pbm->pci_first_busno) {
-                       bucket->flags |= IBF_DMA_SYNC;
-                       bucket->synctab_ent = dma_sync_reg_table_entry++;
-                       dma_sync_reg_table[bucket->synctab_ent] =
-                               (unsigned long) sabre_pci_config_mkaddr(
-                                       pcp->pbm,
-                                       pdev->bus->number, pdev->devfn, PCI_COMMAND);
+                       struct pci_controller_info *p = pcp->pbm->parent;
+                       struct irq_desc *d = bucket->irq_info;
+
+                       d->pre_handler = sabre_wsync_handler;
+                       d->pre_handler_arg1 = pdev;
+                       d->pre_handler_arg2 = (void *)
+                               p->pbm_A.controller_regs + SABRE_WRSYNC;
                }
        }
        return __irq(bucket);
@@ -1626,10 +1633,9 @@ void __init sabre_init(int pnode, char *model_name)
         */
        p->pbm_A.controller_regs = pr_regs[0].phys_addr;
        p->pbm_B.controller_regs = pr_regs[0].phys_addr;
-       pci_dma_wsync = p->pbm_A.controller_regs + SABRE_WRSYNC;
 
-       printk("PCI: Found SABRE, main regs at %016lx, wsync at %016lx\n",
-              p->pbm_A.controller_regs, pci_dma_wsync);
+       printk("PCI: Found SABRE, main regs at %016lx\n",
+              p->pbm_A.controller_regs);
 
        /* Clear interrupts */
 
index 71b4e38076940627c9d13d263581a7aa4552d7fc..b40db389f90b2a8863229d66c74773ae5f4e695a 100644 (file)
@@ -973,7 +973,7 @@ static void sparc64_start_timers(irqreturn_t (*cfunc)(int, void *, struct pt_reg
        int err;
 
        /* Register IRQ handler. */
-       err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, SA_STATIC_ALLOC,
+       err = request_irq(build_irq(0, 0, 0UL, 0UL), cfunc, 0,
                          "timer", NULL);
 
        if (err) {
index 018e2e46082b9844b2a7c50738c3002c0a1103ff..8b70edcb80dc04732c598e2fc0df00f3770f95bc 100644 (file)
 #include <asm/pil.h>
 #include <asm/ptrace.h>
 
+struct ino_bucket;
+
+#define MAX_IRQ_DESC_ACTION    4
+
+struct irq_desc {
+       void                    (*pre_handler)(struct ino_bucket *, void *, void *);
+       void                    *pre_handler_arg1;
+       void                    *pre_handler_arg2;
+       u32                     action_active_mask;
+       struct irqaction        action[MAX_IRQ_DESC_ACTION];
+};
+
 /* You should not mess with this directly. That's the job of irq.c.
  *
  * If you make changes here, please update hand coded assembler of
@@ -42,24 +54,11 @@ struct ino_bucket {
        /* Miscellaneous flags. */
 /*0x06*/unsigned char flags;
 
-       /* This is used to deal with IBF_DMA_SYNC on
-        * Sabre systems.
-        */
-/*0x07*/unsigned char synctab_ent;
-
-       /* Reference to handler for this IRQ.  If this is
-        * non-NULL this means it is active and should be
-        * serviced.  Else the pending member is set to one
-        * and later registry of the interrupt checks for
-        * this condition.
-        *
-        * Normally this is just an irq_action structure.
-        * But, on PCI, if multiple interrupt sources behind
-        * a bridge have multiple interrupt sources that share
-        * the same INO bucket, this points to an array of
-        * pointers to four IRQ action structures.
-        */
-/*0x08*/void *irq_info;
+       /* Currently unused.  */
+/*0x07*/unsigned char __pad;
+
+       /* Reference to IRQ descriptor for this bucket. */
+/*0x08*/struct irq_desc *irq_info;
 
        /* Sun5 Interrupt Clear Register. */
 /*0x10*/unsigned long iclr;
@@ -69,12 +68,6 @@ struct ino_bucket {
 
 };
 
-#ifdef CONFIG_PCI
-extern unsigned long pci_dma_wsync;
-extern unsigned long dma_sync_reg_table[256];
-extern unsigned char dma_sync_reg_table_entry;
-#endif
-
 /* IMAP/ICLR register defines */
 #define IMAP_VALID             0x80000000      /* IRQ Enabled          */
 #define IMAP_TID_UPA           0x7c000000      /* UPA TargetID         */
@@ -90,11 +83,9 @@ extern unsigned char dma_sync_reg_table_entry;
 #define ICLR_PENDING           0x00000003      /* Pending state        */
 
 /* Only 8-bits are available, be careful.  -DaveM */
-#define IBF_DMA_SYNC   0x01    /* DMA synchronization behind PCI bridge needed. */
-#define IBF_PCI                0x02    /* Indicates PSYCHO/SABRE/SCHIZO PCI interrupt.  */
-#define IBF_ACTIVE     0x04    /* This interrupt is active and has a handler.   */
-#define IBF_MULTI      0x08    /* On PCI, indicates shared bucket.              */
-#define IBF_INPROGRESS 0x10    /* IRQ is being serviced.                        */
+#define IBF_PCI                0x02    /* PSYCHO/SABRE/SCHIZO PCI interrupt.    */
+#define IBF_ACTIVE     0x04    /* Interrupt is active and has a handler.*/
+#define IBF_INPROGRESS 0x10    /* IRQ is being serviced.                */
 
 #define NUM_IVECS      (IMAP_INR + 1)
 extern struct ino_bucket ivector_table[NUM_IVECS];
index becdf1bc59245fd7f8f5d8c5866ce7d2f159ca90..e3059bb4a465519a8b6f22490828dea4168706fa 100644 (file)
@@ -162,21 +162,6 @@ struct sigstack {
 #define MINSIGSTKSZ    4096
 #define SIGSTKSZ       16384
 
-#ifdef __KERNEL__
-/*
- * DJHR
- * SA_STATIC_ALLOC is used for the SPARC system to indicate that this
- * interrupt handler's irq structure should be statically allocated
- * by the request_irq routine.
- * The alternative is that arch/sparc/kernel/irq.c has carnal knowledge
- * of interrupt usage and that sucks. Also without a flag like this
- * it may be possible for the free_irq routine to attempt to free
- * statically allocated data.. which is NOT GOOD.
- *
- */
-#define SA_STATIC_ALLOC                0x80
-#endif
-
 #include <asm-generic/signal.h>
 
 struct __new_sigaction {