summaryrefslogtreecommitdiffstats
path: root/arch/sparc/kernel/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc/kernel/irq.c')
-rw-r--r--arch/sparc/kernel/irq.c335
1 files changed, 335 insertions, 0 deletions
diff --git a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c
new file mode 100644
index 000000000..5dcaf1971
--- /dev/null
+++ b/arch/sparc/kernel/irq.c
@@ -0,0 +1,335 @@
+/* arch/sparc/kernel/irq.c: Interrupt request handling routines. On the
+ * Sparc the IRQ's are basically 'cast in stone'
+ * and you are supposed to probe the prom's device
+ * node trees to find out who's got which IRQ.
+ *
+ * Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+ *
+ */
+
+/*
+ * IRQ's are in fact implemented a bit like signal handlers for the kernel.
+ * The same sigaction struct is used, and with similar semantics (ie there
+ * is a SA_INTERRUPT flag etc). Naturally it's not a 1:1 relation, but there
+ * are similarities.
+ *
+ * sa_handler(int irq_NR) is the default function called (0 if no).
+ * sa_mask is horribly ugly (I won't even mention it)
+ * sa_flags contains various info: SA_INTERRUPT etc
+ * sa_restorer is the unused
+ */
+
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/psr.h>
+#include <asm/vaddrs.h>
+#include <asm/clock.h>
+#include <asm/openprom.h>
+
+#define DEBUG_IRQ
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ unsigned char *int_reg;
+
+ save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (unsigned char *) IRQ_ENA_ADR;
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) & (~(0x02)));
+ break;
+ case 4:
+ *int_reg = ((*int_reg) & (~(0x04)));
+ break;
+ case 6:
+ *int_reg = ((*int_reg) & (~(0x08)));
+ break;
+ case 8:
+ *int_reg = ((*int_reg) & (~(0x10)));
+ break;
+ case 10:
+ *int_reg = ((*int_reg) & (~(0x20)));
+ break;
+ case 14:
+ *int_reg = ((*int_reg) & (~(0x80)));
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt disable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
+ restore_flags(flags);
+ return;
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ unsigned char *int_reg;
+
+ save_flags(flags);
+ cli();
+
+ /* We have mapped the irq enable register in head.S and all we
+ * have to do here is frob the bits.
+ */
+
+ int_reg = (unsigned char *) IRQ_ENA_ADR;
+
+#ifdef DEBUG_IRQ
+ printk(" --- Enabling IRQ level %d ---\n", irq_nr);
+#endif
+
+ switch(irq_nr)
+ {
+ case 1:
+ *int_reg = ((*int_reg) | 0x02);
+ break;
+ case 4:
+ *int_reg = ((*int_reg) | 0x04);
+ break;
+ case 6:
+ *int_reg = ((*int_reg) | 0x08);
+ break;
+ case 8:
+ *int_reg = ((*int_reg) | 0x10);
+ break;
+ case 10:
+ *int_reg = ((*int_reg) | 0x20);
+ break;
+ case 14:
+ *int_reg = ((*int_reg) | 0x80);
+ break;
+ default:
+ printk("AIEEE, Illegal interrupt enable requested irq=%d\n",
+ (int) irq_nr);
+ break;
+ };
+
+ restore_flags(flags);
+
+ return;
+}
+
+/*
+ * Initial irq handlers.
+ */
+struct irqaction {
+ void (*handler)(int, struct pt_regs *);
+ unsigned long flags;
+ unsigned long mask;
+ const char *name;
+};
+
+static struct irqaction irq_action[16] = {
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL },
+ { NULL, 0, 0, NULL }, { NULL, 0, 0, NULL }
+};
+
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ struct irqaction * action = irq_action;
+
+ for (i = 0 ; i < 16 ; i++, action++) {
+ if (!action->handler)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s\n",
+ i, kstat.interrupts[i],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ }
+ return len;
+}
+
+void free_irq(unsigned int irq)
+{
+ struct irqaction * action = irq + irq_action;
+ unsigned long flags;
+
+ if (irq > 14) { /* 14 irq levels on the sparc */
+ printk("Trying to free IRQ %d\n", irq);
+ return;
+ }
+ if (!action->handler) {
+ printk("Trying to free free IRQ%d\n", irq);
+ return;
+ }
+ save_flags(flags);
+ cli();
+ disable_irq(irq);
+ action->handler = NULL;
+ action->flags = 0;
+ action->mask = 0;
+ action->name = NULL;
+ restore_flags(flags);
+}
+
+#if 0
+static void handle_nmi(struct pt_regs * regs)
+{
+ printk("NMI, probably due to bus-parity error.\n");
+ printk("PC=%08lx, SP=%08lx\n", regs->pc, regs->sp);
+}
+#endif
+
+void unexpected_irq(int irq, struct pt_regs * regs)
+{
+ int i;
+
+ printk("IO device interrupt, irq = %d\n", irq);
+ printk("PC = %08lx NPC = %08lx SP=%08lx\n", regs->pc,
+ regs->npc, regs->sp);
+ printk("Expecting: ");
+ for (i = 0; i < 16; i++)
+ if (irq_action[i].handler)
+ printk("[%s:%d] ", irq_action[i].name, i);
+ printk("AIEEE\n");
+}
+
+static inline void handler_irq(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action = irq + irq_action;
+
+ if (!action->handler) {
+ unexpected_irq(irq, regs);
+ return;
+ }
+ action->handler(irq, regs);
+}
+
+/*
+ * do_IRQ handles IRQ's that have been installed without the
+ * SA_INTERRUPT flag: it uses the full signal-handling return
+ * and runs with other interrupts enabled. All relatively slow
+ * IRQ's should use this format: notably the keyboard/timer
+ * routines.
+ */
+asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+{
+ struct irqaction *action = irq + irq_action;
+
+ kstat.interrupts[irq]++;
+ action->handler(irq, regs);
+ return;
+}
+
+/*
+ * Since we need to special things to clear up the clock chip around
+ * the do_timer() call we have a special version of do_IRQ for the
+ * level 14 interrupt which does these things.
+ */
+
+asmlinkage void do_sparc_timer(int irq, struct pt_regs * regs)
+{
+ struct irqaction *action = irq + irq_action;
+ register volatile int clear;
+
+ kstat.interrupts[irq]++;
+
+ /* I do the following already in the entry code, better safe than
+ * sorry for now. Reading the limit register clears the interrupt.
+ */
+ clear = TIMER_STRUCT->timer_limit14;
+
+ action->handler(irq, regs);
+ return;
+}
+
+/*
+ * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
+ * stuff - the handler is also running with interrupts disabled unless
+ * it explicitly enables them later.
+ */
+asmlinkage void do_fast_IRQ(int irq)
+{
+ kstat.interrupts[irq]++;
+ printk("Got FAST_IRQ number %04lx\n", (long unsigned int) irq);
+ return;
+}
+
+extern int first_descent;
+extern void probe_clock(int);
+
+int request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long irqflags, const char * devname)
+{
+ struct irqaction *action;
+ unsigned long flags;
+
+ if(irq > 14) /* Only levels 1-14 are valid on the Sparc. */
+ return -EINVAL;
+
+ if(irq == 0) /* sched_init() requesting the timer IRQ */
+ {
+ irq = 14;
+ probe_clock(first_descent);
+ }
+
+ action = irq + irq_action;
+
+ if(action->handler)
+ return -EBUSY;
+
+ if(!handler)
+ return -EINVAL;
+
+ save_flags(flags);
+
+ cli();
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+
+ enable_irq(irq);
+
+ restore_flags(flags);
+
+ return 0;
+}
+
+unsigned int probe_irq_on (void)
+{
+ unsigned int irqs = 0;
+
+ return irqs;
+}
+
+int probe_irq_off (unsigned int irqs)
+{
+ unsigned int i = 0;
+
+ return i;
+}
+
+void init_IRQ(void)
+{
+ return;
+}