diff options
Diffstat (limited to 'arch/mips/galileo-boards/ev96100/irq.c')
-rw-r--r-- | arch/mips/galileo-boards/ev96100/irq.c | 351 |
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); |