/* * BRIEF MODULE DESCRIPTION * Au1000 interrupt routines. * * Copyright 2001 MontaVista Software Inc. * Author: MontaVista Software, Inc. * ppopov@mvista.com or source@mvista.com * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5) #undef DEBUG_IRQ #ifdef DEBUG_IRQ /* note: prints function name for you */ #define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args) #else #define DPRINTK(fmt, args...) #endif #define EXT_INTC0_REQ0 2 /* IP 2 */ #define EXT_INTC0_REQ1 3 /* IP 3 */ #define EXT_INTC1_REQ0 4 /* IP 4 */ #define EXT_INTC1_REQ1 5 /* IP 5 */ #define MIPS_TIMER_IP 7 /* IP 7 */ #ifdef CONFIG_REMOTE_DEBUG extern void breakpoint(void); #endif extern asmlinkage void au1000_IRQ(void); extern void set_debug_traps(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]; static void setup_au1000_irq(unsigned int irq, int type, int int_req); static unsigned int startup_au1000_irq(unsigned int irq); static void enable_au1000_irq(unsigned int irq_nr); static void disable_au1000_irq(unsigned int irq_nr); static void end_au1000_irq(unsigned int irq_nr); static void ack_level_irq(unsigned int irq_nr); static void ack_rise_edge_irq(unsigned int irq_nr); static void ack_fall_edge_irq(unsigned int irq_nr); void disable_ack_irq(int irq); /* 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_cpu_irq_input(unsigned int irq_nr) { modify_cp0_intmask(irq_nr, 0); } static inline void unmask_cpu_irq_input(unsigned int irq_nr) { modify_cp0_intmask(0, irq_nr); } static void disable_cpu_irq_input(unsigned int irq_nr) { unsigned long flags; save_and_cli(flags); mask_cpu_irq_input(irq_nr); restore_flags(flags); } static void enable_cpu_irq_input(unsigned int irq_nr) { unsigned long flags; save_and_cli(flags); unmask_cpu_irq_input(irq_nr); restore_flags(flags); } static void setup_au1000_irq(unsigned int irq_nr, int type, int int_req) { /* Config2[n], Config1[n], Config0[n] */ if (irq_nr > AU1000_LAST_INTC0_INT) { switch (type) { case INTC_INT_RISE_EDGE: /* 0:0:1 */ outl(1< AU1000_LAST_INTC0_INT) { outl(1< AU1000_LAST_INTC0_INT) { outl(1< AU1000_LAST_INTC0_INT) { outl(1< AU1000_LAST_INTC0_INT) { outl(1< AU1000_LAST_INTC0_INT) { outl(1< AU1000_LAST_INTC0_INT) { outl(1<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(int irq, struct pt_regs *regs) { struct irqaction *action; irq_desc_t *desc = irq_desc + irq; int cpu; cpu = smp_processor_id(); irq_enter(cpu, irq); kstat.irqs[cpu][irq]++; desc->handler->ack(irq); action = irq_desc[irq].action; if (action && action->handler) { //desc->handler->disable(irq); //if (!(action->flags & SA_INTERRUPT)) __sti(); /* reenable ints */ do { action->handler(irq, action->dev_id, regs); action = action->next; } while ( action ); //__cli(); /* disable ints */ //desc->handler->ack(irq); //desc->handler->enable(irq); } else { spurious_count++; printk("Unhandled interrupt %d, cause %x, disabled\n", (unsigned)irq, (unsigned)regs->cp0_cause); desc->handler->disable(irq); } irq_exit(cpu, irq); #if 0 if (softirq_active(cpu) & softirq_mask(cpu)) do_softirq(); #endif } 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, shared=0; irq_desc_t *desc = irq_desc + irq; unsigned long cp0_status; cp0_status = read_32bit_cp0_register(CP0_STATUS); 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; desc->handler->disable(irq); desc->handler->ack(irq); 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)); 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; spin_lock_irqsave(&desc->lock,flags); if ((old = *p) != NULL) { /* Can't share interrupts unless both agree to */ if (!(old->flags & action->flags & SA_SHIRQ)) { spin_unlock_irqrestore(&desc->lock,flags); return -EBUSY; } /* add new interrupt at end of irq queue */ do { p = &old->next; old = *p; } while (old); shared = 1; } *p = action; if (!shared) { desc->depth = 0; desc->status &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING); desc->handler->startup(irq); desc->handler->ack(irq); desc->handler->enable(irq); } else { printk("irq %d is shared\n", irq); } spin_unlock_irqrestore(&desc->lock,flags); return 0; } void free_irq(unsigned int irq, void *dev_id) { request_irq(irq, NULL, 0, NULL, dev_id); } void enable_cpu_timer(void) { enable_cpu_irq_input(1<handler->disable(irq); desc->handler->ack(irq); } void mips_spurious_interrupt(struct pt_regs *regs) { #if 0 return; #else unsigned cause; cause = read_32bit_cp0_register(CP0_CAUSE); printk("spurious int (epc %x) (cause %x) (badvaddr %x)\n", (unsigned)regs->cp0_epc, cause, (unsigned)regs->cp0_badvaddr); #endif } void intc0_req0_irqdispatch(struct pt_regs *regs) { int irq = 0; unsigned long int_request; int_request = inl(INTC0_REQ0_INT); if (!int_request) return; for (;;) { if (!(int_request & 0x1)) { irq++; int_request >>= 1; } else break; } do_IRQ(irq, regs); } void intc0_req1_irqdispatch(struct pt_regs *regs) { int irq = 0; unsigned long int_request; int_request = inl(INTC0_REQ1_INT); if (!int_request) return; for (;;) { if (!(int_request & 0x1)) { irq++; int_request >>= 1; } else break; } do_IRQ(irq, regs); } void intc1_req0_irqdispatch(struct pt_regs *regs) { int irq = 0; unsigned long int_request; int_request = inl(INTC1_REQ0_INT); if (!int_request) return; for (;;) { if (!(int_request & 0x1)) { irq++; int_request >>= 1; } else break; } do_IRQ(irq, regs); } void intc1_req1_irqdispatch(struct pt_regs *regs) { int irq = 0; unsigned long int_request; int_request = inl(INTC1_REQ1_INT); if (!int_request) return; for (;;) { if (!(int_request & 0x1)) { irq++; int_request >>= 1; } else break; } do_IRQ(irq, regs); } void show_pending_irqs(void) { }