sparc32: genirq support
[deliverable/linux.git] / arch / sparc / kernel / irq_32.c
index b2dbb4ba860815f2a0a1eb065e4def197b3f9062..197e1ba8548404acd3c590c9443386e651f4f680 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/seq_file.h>
 
 #include <asm/cacheflush.h>
+#include <asm/cpudata.h>
 #include <asm/pcic.h>
 #include <asm/leon.h>
 
@@ -101,284 +102,163 @@ EXPORT_SYMBOL(arch_local_irq_restore);
  * directed CPU interrupts using the existing enable/disable irq code
  * with tweaks.
  *
+ * Sun4d complicates things even further.  IRQ numbers are arbitrary
+ * 32-bit values in that case.  Since this is similar to sparc64,
+ * we adopt a virtual IRQ numbering scheme as is done there.
+ * Virutal interrupt numbers are allocated by build_irq().  So NR_IRQS
+ * just becomes a limit of how many interrupt sources we can handle in
+ * a single system.  Even fully loaded SS2000 machines top off at
+ * about 32 interrupt sources or so, therefore a NR_IRQS value of 64
+ * is more than enough.
+  *
+ * We keep a map of per-PIL enable interrupts.  These get wired
+ * up via the irq_chip->startup() method which gets invoked by
+ * the generic IRQ layer during request_irq().
  */
 
 
+/* Table of allocated irqs. Unused entries has irq == 0 */
+static struct irq_bucket irq_table[NR_IRQS];
+/* Protect access to irq_table */
+static DEFINE_SPINLOCK(irq_table_lock);
 
-/*
- * Dave Redman (djhr@tadpole.co.uk)
- *
- * There used to be extern calls and hard coded values here.. very sucky!
- * instead, because some of the devices attach very early, I do something
- * equally sucky but at least we'll never try to free statically allocated
- * space or call kmalloc before kmalloc_init :(.
- *
- * In fact it's the timer10 that attaches first.. then timer14
- * then kmalloc_init is called.. then the tty interrupts attach.
- * hmmm....
- *
- */
-#define MAX_STATIC_ALLOC       4
-struct irqaction static_irqaction[MAX_STATIC_ALLOC];
-int static_irq_count;
-
-static struct {
-       struct irqaction *action;
-       int flags;
-} sparc_irq[NR_IRQS];
-#define SPARC_IRQ_INPROGRESS 1
-
-/* Used to protect the IRQ action lists */
-DEFINE_SPINLOCK(irq_action_lock);
+/* Map between the irq identifier used in hw to the irq_bucket. */
+struct irq_bucket *irq_map[SUN4D_MAX_IRQ];
+/* Protect access to irq_map */
+static DEFINE_SPINLOCK(irq_map_lock);
 
-int show_interrupts(struct seq_file *p, void *v)
+/* Allocate a new irq from the irq_table */
+unsigned int irq_alloc(unsigned int real_irq, unsigned int pil)
 {
-       int i = *(loff_t *)v;
-       struct irqaction *action;
        unsigned long flags;
-#ifdef CONFIG_SMP
-       int j;
-#endif
+       unsigned int i;
+
+       spin_lock_irqsave(&irq_table_lock, flags);
+       for (i = 1; i < NR_IRQS; i++) {
+               if (irq_table[i].real_irq == real_irq && irq_table[i].pil == pil)
+                       goto found;
+       }
 
-       if (sparc_cpu_model == sun4d)
-               return show_sun4d_interrupts(p, v);
+       for (i = 1; i < NR_IRQS; i++) {
+               if (!irq_table[i].irq)
+                       break;
+       }
 
-       spin_lock_irqsave(&irq_action_lock, flags);
        if (i < NR_IRQS) {
-               action = sparc_irq[i].action;
-               if (!action)
-                       goto out_unlock;
-               seq_printf(p, "%3d: ", i);
-#ifndef CONFIG_SMP
-               seq_printf(p, "%10u ", kstat_irqs(i));
-#else
-               for_each_online_cpu(j) {
-                       seq_printf(p, "%10u ",
-                                   kstat_cpu(j).irqs[i]);
-               }
-#endif
-               seq_printf(p, " %c %s",
-                       (action->flags & IRQF_DISABLED) ? '+' : ' ',
-                       action->name);
-               for (action = action->next; action; action = action->next) {
-                       seq_printf(p, ",%s %s",
-                               (action->flags & IRQF_DISABLED) ? " +" : "",
-                               action->name);
-               }
-               seq_putc(p, '\n');
+               irq_table[i].real_irq = real_irq;
+               irq_table[i].irq = i;
+               irq_table[i].pil = pil;
+       } else {
+               printk(KERN_ERR "IRQ: Out of virtual IRQs.\n");
+               i = 0;
        }
-out_unlock:
-       spin_unlock_irqrestore(&irq_action_lock, flags);
-       return 0;
+found:
+       spin_unlock_irqrestore(&irq_table_lock, flags);
+
+       return i;
 }
 
-void free_irq(unsigned int irq, void *dev_id)
+/* Based on a single pil handler_irq may need to call several
+ * interrupt handlers. Use irq_map as entry to irq_table,
+ * and let each entry in irq_table point to the next entry.
+ */
+void irq_link(unsigned int irq)
 {
-       struct irqaction *action;
-       struct irqaction **actionp;
+       struct irq_bucket *p;
        unsigned long flags;
-       unsigned int cpu_irq;
+       unsigned int pil;
 
-       if (sparc_cpu_model == sun4d) {
-               sun4d_free_irq(irq, dev_id);
-               return;
-       }
-       cpu_irq = irq & (NR_IRQS - 1);
-       if (cpu_irq > 14) {  /* 14 irq levels on the sparc */
-               printk(KERN_ERR "Trying to free bogus IRQ %d\n", irq);
-               return;
-       }
+       BUG_ON(irq >= NR_IRQS);
 
-       spin_lock_irqsave(&irq_action_lock, flags);
+       spin_lock_irqsave(&irq_map_lock, flags);
 
-       actionp = &sparc_irq[cpu_irq].action;
-       action = *actionp;
+       p = &irq_table[irq];
+       pil = p->pil;
+       BUG_ON(pil > SUN4D_MAX_IRQ);
+       p->next = irq_map[pil];
+       irq_map[pil] = p;
 
-       if (!action->handler) {
-               printk(KERN_ERR "Trying to free free IRQ%d\n", irq);
-               goto out_unlock;
-       }
-       if (dev_id) {
-               for (; action; action = action->next) {
-                       if (action->dev_id == dev_id)
-                               break;
-                       actionp = &action->next;
-               }
-               if (!action) {
-                       printk(KERN_ERR "Trying to free free shared IRQ%d\n",
-                              irq);
-                       goto out_unlock;
-               }
-       } else if (action->flags & IRQF_SHARED) {
-               printk(KERN_ERR "Trying to free shared IRQ%d with NULL device ID\n",
-                      irq);
-               goto out_unlock;
-       }
-       if (action->flags & SA_STATIC_ALLOC) {
-               /*
-                * This interrupt is marked as specially allocated
-                * so it is a bad idea to free it.
-                */
-               printk(KERN_ERR "Attempt to free statically allocated IRQ%d (%s)\n",
-                      irq, action->name);
-               goto out_unlock;
-       }
-
-       *actionp = action->next;
-
-       spin_unlock_irqrestore(&irq_action_lock, flags);
+       spin_unlock_irqrestore(&irq_map_lock, flags);
+}
 
-       synchronize_irq(irq);
+void irq_unlink(unsigned int irq)
+{
+       struct irq_bucket *p, **pnext;
+       unsigned long flags;
 
-       spin_lock_irqsave(&irq_action_lock, flags);
+       BUG_ON(irq >= NR_IRQS);
 
-       kfree(action);
+       spin_lock_irqsave(&irq_map_lock, flags);
 
-       if (!sparc_irq[cpu_irq].action)
-               __disable_irq(irq);
+       p = &irq_table[irq];
+       BUG_ON(p->pil > SUN4D_MAX_IRQ);
+       pnext = &irq_map[p->pil];
+       while (*pnext != p)
+               pnext = &(*pnext)->next;
+       *pnext = p->next;
 
-out_unlock:
-       spin_unlock_irqrestore(&irq_action_lock, flags);
+       spin_unlock_irqrestore(&irq_map_lock, flags);
 }
-EXPORT_SYMBOL(free_irq);
 
-/*
- * This is called when we want to synchronize with
- * interrupts. We may for example tell a device to
- * stop sending interrupts: but to make sure there
- * are no interrupts that are executing on another
- * CPU we need to call this function.
- */
-#ifdef CONFIG_SMP
-void synchronize_irq(unsigned int irq)
-{
-       unsigned int cpu_irq;
 
-       cpu_irq = irq & (NR_IRQS - 1);
-       while (sparc_irq[cpu_irq].flags & SPARC_IRQ_INPROGRESS)
-               cpu_relax();
-}
-EXPORT_SYMBOL(synchronize_irq);
-#endif /* SMP */
-
-void unexpected_irq(int irq, void *dev_id, struct pt_regs *regs)
+/* /proc/interrupts printing */
+int arch_show_interrupts(struct seq_file *p, int prec)
 {
-       int i;
-       struct irqaction *action;
-       unsigned int cpu_irq;
+       int j;
 
-       cpu_irq = irq & (NR_IRQS - 1);
-       action = sparc_irq[cpu_irq].action;
-
-       printk(KERN_ERR "IO device interrupt, irq = %d\n", irq);
-       printk(KERN_ERR "PC = %08lx NPC = %08lx FP=%08lx\n", regs->pc,
-                   regs->npc, regs->u_regs[14]);
-       if (action) {
-               printk(KERN_ERR "Expecting: ");
-               for (i = 0; i < 16; i++)
-                       if (action->handler)
-                               printk(KERN_CONT "[%s:%d:0x%x] ", action->name,
-                                      i, (unsigned int)action->handler);
-       }
-       printk(KERN_ERR "AIEEE\n");
-       panic("bogus interrupt received");
+       seq_printf(p, "NMI: ");
+       for_each_online_cpu(j)
+               seq_printf(p, "%10u ", cpu_data(j).counter);
+       seq_printf(p, "     Non-maskable interrupts\n");
+       return 0;
 }
 
-void handler_irq(int pil, struct pt_regs *regs)
+void handler_irq(unsigned int pil, struct pt_regs *regs)
 {
        struct pt_regs *old_regs;
-       struct irqaction *action;
-       int cpu = smp_processor_id();
+       struct irq_bucket *p;
 
+       BUG_ON(pil > 15);
        old_regs = set_irq_regs(regs);
        irq_enter();
-       disable_pil_irq(pil);
-#ifdef CONFIG_SMP
-       /* Only rotate on lower priority IRQs (scsi, ethernet, etc.). */
-       if ((sparc_cpu_model==sun4m) && (pil < 10))
-               smp4m_irq_rotate(cpu);
-#endif
-       action = sparc_irq[pil].action;
-       sparc_irq[pil].flags |= SPARC_IRQ_INPROGRESS;
-       kstat_cpu(cpu).irqs[pil]++;
-       do {
-               if (!action || !action->handler)
-                       unexpected_irq(pil, NULL, regs);
-               action->handler(pil, action->dev_id);
-               action = action->next;
-       } while (action);
-       sparc_irq[pil].flags &= ~SPARC_IRQ_INPROGRESS;
-       enable_pil_irq(pil);
+
+       p = irq_map[pil];
+       while (p) {
+               struct irq_bucket *next = p->next;
+
+               generic_handle_irq(p->irq);
+               p = next;
+       }
        irq_exit();
        set_irq_regs(old_regs);
 }
 
 #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
+static unsigned int floppy_irq;
 
-/*
- * Fast IRQs on the Sparc can only have one routine attached to them,
- * thus no sharing possible.
- */
-static int request_fast_irq(unsigned int irq,
-                           void (*handler)(void),
-                           unsigned long irqflags, const char *devname)
+int sparc_floppy_request_irq(unsigned int irq, irq_handler_t irq_handler)
 {
-       struct irqaction *action;
-       unsigned long flags;
        unsigned int cpu_irq;
-       int ret;
+       int err;
+
 #if defined CONFIG_SMP && !defined CONFIG_SPARC_LEON
        struct tt_entry *trap_table;
 #endif
-       cpu_irq = irq & (NR_IRQS - 1);
-       if (cpu_irq > 14) {
-               ret = -EINVAL;
-               goto out;
-       }
-       if (!handler) {
-               ret = -EINVAL;
-               goto out;
-       }
 
-       spin_lock_irqsave(&irq_action_lock, flags);
+       err = request_irq(irq, irq_handler, 0, "floppy", NULL);
+       if (err)
+               return -1;
 
-       action = sparc_irq[cpu_irq].action;
-       if (action) {
-               if (action->flags & IRQF_SHARED)
-                       panic("Trying to register fast irq when already shared.\n");
-               if (irqflags & IRQF_SHARED)
-                       panic("Trying to register fast irq as shared.\n");
+       /* Save for later use in floppy interrupt handler */
+       floppy_irq = irq;
 
-               /* Anyway, someone already owns it so cannot be made fast. */
-               printk(KERN_ERR "request_fast_irq: Trying to register yet already owned.\n");
-               ret = -EBUSY;
-               goto out_unlock;
-       }
-
-       /*
-        * 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(KERN_ERR "Fast IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
-                              irq, devname);
-       }
-
-       if (action == NULL)
-               action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
-       if (!action) {
-               ret = -ENOMEM;
-               goto out_unlock;
-       }
+       cpu_irq = (irq & (NR_IRQS - 1));
 
        /* Dork with trap table if we get this far. */
 #define INSTANTIATE(table) \
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_one = SPARC_RD_PSR_L0; \
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two = \
-               SPARC_BRANCH((unsigned long) handler, \
+               SPARC_BRANCH((unsigned long) floppy_hardint, \
                             (unsigned long) &table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_two);\
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_three = SPARC_RD_WIM_L3; \
        table[SP_TRAP_IRQ1+(cpu_irq-1)].inst_four = SPARC_NOP;
@@ -399,22 +279,9 @@ static int request_fast_irq(unsigned int irq,
         * writing we have no CPU-neutral interface to fine-grained flushes.
         */
        flush_cache_all();
-
-       action->flags = irqflags;
-       action->name = devname;
-       action->dev_id = NULL;
-       action->next = NULL;
-
-       sparc_irq[cpu_irq].action = action;
-
-       __enable_irq(irq);
-
-       ret = 0;
-out_unlock:
-       spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
-       return ret;
+       return 0;
 }
+EXPORT_SYMBOL(sparc_floppy_request_irq);
 
 /*
  * These variables are used to access state from the assembler
@@ -440,154 +307,23 @@ EXPORT_SYMBOL(pdma_base);
 unsigned long pdma_areasize;
 EXPORT_SYMBOL(pdma_areasize);
 
-static irq_handler_t floppy_irq_handler;
-
+/* Use the generic irq support to call floppy_interrupt
+ * which was setup using request_irq() in sparc_floppy_request_irq().
+ * We only have one floppy interrupt so we do not need to check
+ * for additional handlers being wired up by irq_link()
+ */
 void sparc_floppy_irq(int irq, void *dev_id, struct pt_regs *regs)
 {
        struct pt_regs *old_regs;
-       int cpu = smp_processor_id();
 
        old_regs = set_irq_regs(regs);
-       disable_pil_irq(irq);
        irq_enter();
-       kstat_cpu(cpu).irqs[irq]++;
-       floppy_irq_handler(irq, dev_id);
+       generic_handle_irq(floppy_irq);
        irq_exit();
-       enable_pil_irq(irq);
        set_irq_regs(old_regs);
-       /*
-        * XXX Eek, it's totally changed with preempt_count() and such
-        * if (softirq_pending(cpu))
-        *      do_softirq();
-        */
 }
-
-int sparc_floppy_request_irq(int irq, unsigned long flags,
-                            irq_handler_t irq_handler)
-{
-       floppy_irq_handler = irq_handler;
-       return request_fast_irq(irq, floppy_hardint, flags, "floppy");
-}
-EXPORT_SYMBOL(sparc_floppy_request_irq);
-
 #endif
 
-int request_irq(unsigned int irq,
-               irq_handler_t handler,
-               unsigned long irqflags, const char *devname, void *dev_id)
-{
-       struct irqaction *action, **actionp;
-       unsigned long flags;
-       unsigned int cpu_irq;
-       int ret;
-
-       if (sparc_cpu_model == sun4d)
-               return sun4d_request_irq(irq, handler, irqflags, devname, dev_id);
-
-       cpu_irq = irq & (NR_IRQS - 1);
-       if (cpu_irq > 14) {
-               ret = -EINVAL;
-               goto out;
-       }
-       if (!handler) {
-               ret = -EINVAL;
-               goto out;
-       }
-
-       spin_lock_irqsave(&irq_action_lock, flags);
-
-       actionp = &sparc_irq[cpu_irq].action;
-       action = *actionp;
-       if (action) {
-               if (!(action->flags & IRQF_SHARED) || !(irqflags & IRQF_SHARED)) {
-                       ret = -EBUSY;
-                       goto out_unlock;
-               }
-               if ((action->flags & IRQF_DISABLED) != (irqflags & IRQF_DISABLED)) {
-                       printk(KERN_ERR "Attempt to mix fast and slow interrupts on IRQ%d denied\n",
-                              irq);
-                       ret = -EBUSY;
-                       goto out_unlock;
-               }
-               for ( ; action; action = *actionp)
-                       actionp = &action->next;
-       }
-
-       /* 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(KERN_ERR "Request for IRQ%d (%s) SA_STATIC_ALLOC failed using kmalloc\n",
-                              irq, devname);
-       }
-       if (action == NULL)
-               action = kmalloc(sizeof(struct irqaction), GFP_ATOMIC);
-       if (!action) {
-               ret = -ENOMEM;
-               goto out_unlock;
-       }
-
-       action->handler = handler;
-       action->flags = irqflags;
-       action->name = devname;
-       action->next = NULL;
-       action->dev_id = dev_id;
-
-       *actionp = action;
-
-       __enable_irq(irq);
-
-       ret = 0;
-out_unlock:
-       spin_unlock_irqrestore(&irq_action_lock, flags);
-out:
-       return ret;
-}
-EXPORT_SYMBOL(request_irq);
-
-void disable_irq_nosync(unsigned int irq)
-{
-       __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq_nosync);
-
-void disable_irq(unsigned int irq)
-{
-       __disable_irq(irq);
-}
-EXPORT_SYMBOL(disable_irq);
-
-void enable_irq(unsigned int irq)
-{
-       __enable_irq(irq);
-}
-EXPORT_SYMBOL(enable_irq);
-
-/*
- * We really don't need these at all on the Sparc.  We only have
- * stubs here because they are exported to modules.
- */
-unsigned long probe_irq_on(void)
-{
-       return 0;
-}
-EXPORT_SYMBOL(probe_irq_on);
-
-int probe_irq_off(unsigned long mask)
-{
-       return 0;
-}
-EXPORT_SYMBOL(probe_irq_off);
-
-static unsigned int build_device_irq(struct platform_device *op,
-                                     unsigned int real_irq)
-{
-       return real_irq;
-}
-
 /* djhr
  * This could probably be made indirect too and assigned in the CPU
  * bits of the code. That would be much nicer I think and would also
@@ -598,8 +334,6 @@ static unsigned int build_device_irq(struct platform_device *op,
 
 void __init init_IRQ(void)
 {
-       sparc_irq_config.build_device_irq = build_device_irq;
-
        switch (sparc_cpu_model) {
        case sun4c:
        case sun4:
@@ -629,9 +363,3 @@ void __init init_IRQ(void)
        btfixup();
 }
 
-#ifdef CONFIG_PROC_FS
-void init_irq_proc(void)
-{
-       /* For now, nothing... */
-}
-#endif /* CONFIG_PROC_FS */
This page took 0.032925 seconds and 5 git commands to generate.