summaryrefslogtreecommitdiffstats
path: root/arch/mips/galileo-boards/ev96100/irq.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/galileo-boards/ev96100/irq.c')
-rw-r--r--arch/mips/galileo-boards/ev96100/irq.c351
1 files changed, 351 insertions, 0 deletions
diff --git a/arch/mips/galileo-boards/ev96100/irq.c b/arch/mips/galileo-boards/ev96100/irq.c
new file mode 100644
index 000000000..0458e643b
--- /dev/null
+++ b/arch/mips/galileo-boards/ev96100/irq.c
@@ -0,0 +1,351 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ * Galileo EV96100 interrupt/setup routines.
+ *
+ * Copyright 2000 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ * ppopov@mvista.com or support@mvista.com
+ *
+ * This file was derived from Carsten Langgaard's
+ * arch/mips/mips-boards/atlas/atlas_int.c.
+ *
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/galileo-boards/ev96100int.h>
+
+extern void mips_timer_interrupt(int irq, struct pt_regs *regs);
+extern asmlinkage void ev96100IRQ(void);
+irq_cpustat_t irq_stat[NR_CPUS];
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+unsigned long spurious_count = 0;
+irq_desc_t irq_desc[NR_IRQS];
+irq_desc_t *irq_desc_base = &irq_desc[0];
+
+static struct irqaction timer_action = {
+ NULL, 0, 0, "R7000 timer/counter", NULL, NULL,
+};
+
+static struct hw_interrupt_type mips_timer = {
+ "MIPS CPU Timer",
+ NULL,
+ NULL,
+ NULL, /* unmask_irq */
+ NULL, /* mask_irq */
+ NULL, /* mask_and_ack */
+ 0
+};
+
+/* Function for careful CP0 interrupt mask access */
+static inline void modify_cp0_intmask(unsigned clr_mask, unsigned set_mask)
+{
+ unsigned long status = read_32bit_cp0_register(CP0_STATUS);
+ status &= ~((clr_mask & 0xFF) << 8);
+ status |= (set_mask & 0xFF) << 8;
+ write_32bit_cp0_register(CP0_STATUS, status);
+}
+
+static inline void mask_irq(unsigned int irq_nr)
+{
+ modify_cp0_intmask(irq_nr, 0);
+}
+
+static inline void unmask_irq(unsigned int irq_nr)
+{
+ modify_cp0_intmask(0, irq_nr);
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_and_cli(flags);
+ mask_irq(irq_nr);
+ restore_flags(flags);
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+#if 0
+ printk("enable irq %d\n", irq_nr);
+ printk("status reg: %x, cause %x\n",
+ read_32bit_cp0_register(CP0_STATUS),
+ read_32bit_cp0_register(CP0_CAUSE));
+#endif
+ save_and_cli(flags);
+ unmask_irq(irq_nr);
+ restore_flags(flags);
+#if 0
+ printk("new status reg: %x, cause %x\n",
+ read_32bit_cp0_register(CP0_STATUS),
+ read_32bit_cp0_register(CP0_CAUSE));
+#endif
+}
+
+
+void __init ev96100_time_init()
+{
+ puts("ev96100 time_init\n");
+
+}
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0, j;
+ struct irqaction *action;
+
+ len += sprintf(buf + len, " ");
+ for (j = 0; j < smp_num_cpus; j++)
+ len += sprintf(buf + len, "CPU%d ", j);
+ *(char *) (buf + len++) = '\n';
+
+ for (i = 0; i < NR_IRQS; i++) {
+ action = irq_desc[i].action;
+ if (!action || !action->handler)
+ continue;
+ len += sprintf(buf + len, "%3d: ", i);
+ len += sprintf(buf + len, "%10u ", kstat_irqs(i));
+ if (irq_desc[i].handler)
+ len +=
+ sprintf(buf + len, " %s ",
+ irq_desc[i].handler->typename);
+ else
+ len += sprintf(buf + len, " None ");
+ len += sprintf(buf + len, " %s", action->name);
+ for (action = action->next; action; action = action->next) {
+ len += sprintf(buf + len, ", %s", action->name);
+ }
+ len += sprintf(buf + len, "\n");
+ }
+ len += sprintf(buf + len, "BAD: %10lu\n", spurious_count);
+ return len;
+}
+
+asmlinkage void do_IRQ(unsigned long cause, struct pt_regs *regs)
+{
+ struct irqaction *action;
+ int cpu;
+ int status;
+ int irq;
+
+ //printk("do_IRQ: cause %x *regs %x\n", cause, regs);
+ /*
+ * Service one interrupt only.
+ * The "priority" is not really defined at this point.
+ * This will change once we add the R7000 extensions.
+ */
+// if (cause & CAUSEF_IP7)
+// irq = 7;
+ if (cause & CAUSEF_IP6)
+ irq = 6;
+ else if (cause & CAUSEF_IP5)
+ irq = 5;
+ else if (cause & CAUSEF_IP4)
+ irq = 4;
+ else if (cause & CAUSEF_IP3)
+ irq = 3;
+ else if (cause & CAUSEF_IP2)
+ irq = 2;
+ else if (cause & CAUSEF_IP1)
+ irq = 1;
+ else if (cause & CAUSEF_IP0)
+ irq = 0;
+ else
+ return; /* should not happen */
+
+ cpu = smp_processor_id();
+ irq_enter(cpu);
+ kstat.irqs[cpu][irq]++;
+ status = 0;
+
+ if (irq_desc[irq].handler && irq_desc[irq].handler->ack) {
+ irq_desc[irq].handler->ack(irq);
+ }
+
+ action = irq_desc[irq].action;
+
+ if (action && action->handler) {
+ //if (!(action->flags & SA_INTERRUPT)) __sti();
+ //printk("irq %d, action->handler %x\n", irq, action->handler);
+ do {
+ status |= action->flags;
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ } while (action);
+ //__cli();
+ if (irq_desc[irq].handler) {
+ printk("handler??\n");
+ while (1);
+ if (irq_desc[irq].handler->end)
+ irq_desc[irq].handler->end(irq);
+ else if (irq_desc[irq].handler->enable)
+ irq_desc[irq].handler->enable(irq);
+ }
+ } else {
+ spurious_count++;
+ //printk(KERN_DEBUG "Unhandled interrupt %x, disabled\n", irq);
+ printk("Unhandled interrupt %x, disabled\n", irq);
+ disable_irq(1 << irq);
+ if (irq_desc[irq].handler->end)
+ irq_desc[irq].handler->end(irq);
+ }
+
+ irq_exit(cpu);
+
+ if (softirq_active(cpu) & softirq_mask(cpu))
+ do_softirq();
+}
+
+int request_irq(unsigned int irq,
+ void (*handler) (int, void *, struct pt_regs *),
+ unsigned long irqflags, const char *devname, void *dev_id)
+{
+ struct irqaction *old, **p, *action;
+ unsigned long flags;
+
+ /*
+ * IRQs are number 0 through 7, where 0 corresponds to IP0 and
+ * 7 corresponds to IP7. IP0 and IP1 are software interrupts. IP7
+ * is typically the timer interrupt, unless the R7000 extensions are
+ * used.
+ */
+
+ printk("request_irq %d, handler %x\n", irq, handler);
+
+ if (irq >= NR_IRQS)
+ return -EINVAL;
+ if (!handler) {
+ /* Free */
+ for (p = &irq_desc[irq].action; (action = *p) != NULL;
+ p = &action->next) {
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ restore_flags(flags);
+ kfree(action);
+ return 0;
+ }
+ return -ENOENT;
+ }
+
+ action = (struct irqaction *)
+ kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+ memset(action, 0, sizeof(struct irqaction));
+
+ save_flags(flags);
+ cli();
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->dev_id = dev_id;
+ action->next = NULL;
+
+ p = &irq_desc[irq].action;
+
+ if ((old = *p) != NULL) {
+ /* Can't share interrupts unless both agree to */
+ if (!(old->flags & action->flags & SA_SHIRQ))
+ return -EBUSY;
+ /* add new interrupt at end of irq queue */
+ do {
+ p = &old->next;
+ old = *p;
+ } while (old);
+ }
+ *p = action;
+ printk("action %x, action->handler %x\n",
+ irq_desc[irq].action, irq_desc[irq].action->handler);
+
+ enable_irq(1 << irq);
+ restore_flags(flags);
+ return 0;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ printk("free_irq %d\n", irq);
+ request_irq(irq, NULL, 0, NULL, dev_id);
+}
+
+
+unsigned long probe_irq_on(void)
+{
+ return 0;
+}
+
+int probe_irq_off(unsigned long irqs)
+{
+ return 0;
+}
+
+int (*irq_cannonicalize) (int irq);
+
+int ev96100_irq_cannonicalize(int i)
+{
+ return i;
+}
+
+void __init init_IRQ(void)
+{
+ puts("init_IRQ\n");
+ memset(irq_desc, 0, sizeof(irq_desc));
+ irq_cannonicalize = ev96100_irq_cannonicalize;
+
+ /*
+ irq_desc[EV96100INT_TIMER].handler = &mips_timer;
+ irq_desc[EV96100INT_TIMER].action = &timer_action;
+ irq_desc[EV96100INT_TIMER].action->handler = mips_timer_interrupt;
+ */
+
+ set_except_vector(0, ev96100IRQ);
+}
+
+EXPORT_SYMBOL(irq_cannonicalize);