Commit e4ac58af authored by Ralf Baechle's avatar Ralf Baechle
Browse files

[MIPS] Rewrite all the assembler interrupt handlers to C.



Saves like 1,600 lines of code, is way easier to debug, compilers
frequently do a better job than the cut and paste type of handlers many
boards had.  And finally having all the stuff done in a single place
also means alot of bug potencial for the MT ASE is gone.

The only surviving handler in assembler is the DECstation one; I hope
Maciej will rewrite it.
Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent d35d473c
......@@ -6,7 +6,7 @@
# Makefile for the Alchemy Au1000 CPU, generic files.
#
obj-y += prom.o int-handler.o irq.o puts.o time.o reset.o \
obj-y += prom.o irq.o puts.o time.o reset.o \
au1xxx_irqmap.o clocks.o platform.o power.o setup.o \
sleeper.o cputable.o dma.o dbdma.o gpio.o
......
/*
* Copyright 2001 MontaVista Software Inc.
* Author: ppopov@mvista.com
*
* Interrupt dispatcher for Au1000 boards.
*
* 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.
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
.text
.set macro
.set noat
.align 5
NESTED(au1000_IRQ, PT_SIZE, sp)
SAVE_ALL
CLI # Important: mark KERNEL mode !
mfc0 t0,CP0_CAUSE # get pending interrupts
mfc0 t1,CP0_STATUS # get enabled interrupts
and t0,t1 # isolate allowed ones
andi t0,0xff00 # isolate pending bits
beqz t0, 3f # spurious interrupt
andi a0, t0, CAUSEF_IP7
beq a0, zero, 1f
move a0, sp
jal mips_timer_interrupt
j ret_from_irq
1:
andi a0, t0, CAUSEF_IP2 # Interrupt Controller 0, Request 0
beq a0, zero, 2f
move a0,sp
jal intc0_req0_irqdispatch
j ret_from_irq
2:
andi a0, t0, CAUSEF_IP3 # Interrupt Controller 0, Request 1
beq a0, zero, 3f
move a0,sp
jal intc0_req1_irqdispatch
j ret_from_irq
3:
andi a0, t0, CAUSEF_IP4 # Interrupt Controller 1, Request 0
beq a0, zero, 4f
move a0,sp
jal intc1_req0_irqdispatch
j ret_from_irq
4:
andi a0, t0, CAUSEF_IP5 # Interrupt Controller 1, Request 1
beq a0, zero, 5f
move a0, sp
jal intc1_req1_irqdispatch
j ret_from_irq
5:
move a0, sp
jal spurious_interrupt
j ret_from_irq
END(au1000_IRQ)
......@@ -66,7 +66,6 @@
#define EXT_INTC1_REQ1 5 /* IP 5 */
#define MIPS_TIMER_IP 7 /* IP 7 */
extern asmlinkage void au1000_IRQ(void);
extern void set_debug_traps(void);
extern irq_cpustat_t irq_stat [NR_CPUS];
......@@ -446,7 +445,6 @@ void __init arch_init_irq(void)
extern int au1xxx_ic0_nr_irqs;
cp0_status = read_c0_status();
set_except_vector(0, au1000_IRQ);
/* Initialize interrupt controllers to a safe state.
*/
......@@ -661,3 +659,21 @@ restore_au1xxx_intctl(void)
au_writel(sleep_intctl_mask[0], IC0_MASKSET); au_sync();
}
#endif /* CONFIG_PM */
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
if (pending & CAUSEF_IP7)
mips_timer_interrupt(regs);
else if (pending & CAUSEF_IP2)
intc0_req0_irqdispatch(regs);
else if (pending & CAUSEF_IP3)
intc0_req1_irqdispatch(regs);
else if (pending & CAUSEF_IP4)
intc1_req0_irqdispatch(regs);
else if (pending & CAUSEF_IP5)
intc1_req1_irqdispatch(regs);
else
spurious_interrupt(regs);
}
......@@ -2,7 +2,7 @@
# Makefile for the Cobalt micro systems family specific parts of the kernel
#
obj-y := irq.o int-handler.o reset.o setup.o
obj-y := irq.o reset.o setup.o
obj-$(CONFIG_EARLY_PRINTK) += console.o
......
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Copyright (C) 1995, 1996, 1997, 2003 by Ralf Baechle
* Copyright (C) 2001, 2002, 2003 by Liam Davies (ldavies@agile.tv)
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/mach-cobalt/cobalt.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
.text
.align 5
NESTED(cobalt_handle_int, PT_SIZE, sp)
SAVE_ALL
CLI
PTR_LA ra, ret_from_irq
move a0, sp
j cobalt_irq
END(cobalt_handle_int)
......@@ -20,8 +20,6 @@
#include <asm/mach-cobalt/cobalt.h>
extern void cobalt_handle_int(void);
/*
* We have two types of interrupts that we handle, ones that come in through
* the CPU interrupt lines, and ones that come in on the via chip. The CPU
......@@ -79,7 +77,7 @@ static inline void via_pic_irq(struct pt_regs *regs)
do_IRQ(irq, regs);
}
asmlinkage void cobalt_irq(struct pt_regs *regs)
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
unsigned pending;
......@@ -122,8 +120,6 @@ void __init arch_init_irq(void)
*/
GALILEO_OUTL(0, GT_INTRMASK_OFS);
set_except_vector(0, cobalt_handle_int);
init_i8259_irqs(); /* 0 ... 15 */
mips_cpu_irq_init(COBALT_CPU_IRQ); /* 16 ... 23 */
......
......@@ -3,6 +3,6 @@
# under Linux.
#
obj-y += setup.o irq.o int-handler.o nile4_pic.o
obj-y += setup.o irq.o nile4_pic.o
EXTRA_AFLAGS := $(CFLAGS)
/*
* arch/mips/ddb5074/int-handler.S -- NEC DDB Vrc-5074 interrupt handler
*
* Based on arch/mips/sgi/kernel/indyIRQ.S
*
* Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
*
* Copyright (C) 2000 Geert Uytterhoeven <geert@sonycom.com>
* Sony Software Development Center Europe (SDCE), Brussels
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
/* A lot of complication here is taken away because:
*
* 1) We handle one interrupt and return, sitting in a loop and moving across
* all the pending IRQ bits in the cause register is _NOT_ the answer, the
* common case is one pending IRQ so optimize in that direction.
*
* 2) We need not check against bits in the status register IRQ mask, that
* would make this routine slow as hell.
*
* 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in
* between like BSD spl() brain-damage.
*
* Furthermore, the IRQs on the INDY look basically (barring software IRQs
* which we don't use at all) like:
*
* MIPS IRQ Source
* -------- ------
* 0 Software (ignored)
* 1 Software (ignored)
* 2 Local IRQ level zero
* 3 Local IRQ level one
* 4 8254 Timer zero
* 5 8254 Timer one
* 6 Bus Error
* 7 R4k timer (what we use)
*
* We handle the IRQ according to _our_ priority which is:
*
* Highest ---- R4k Timer
* Local IRQ zero
* Local IRQ one
* Bus Error
* 8254 Timer zero
* Lowest ---- 8254 Timer one
*
* then we just return, if multiple IRQs are pending then we will just take
* another exception, big deal.
*/
.text
.set noreorder
.set noat
.align 5
NESTED(ddbIRQ, PT_SIZE, sp)
SAVE_ALL
CLI
.set at
mfc0 s0, CP0_CAUSE # get irq mask
#if 1
mfc0 t2,CP0_STATUS # get enabled interrupts
and s0,t2 # isolate allowed ones
#endif
/* First we check for r4k counter/timer IRQ. */
andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero
beq a0, zero, 1f
andi a0, s0, CAUSEF_IP3 # delay slot, check local level one
/* Wheee, local level zero interrupt. */
jal ddb_local0_irqdispatch
move a0, sp # delay slot
j ret_from_irq
nop # delay slot
1:
beq a0, zero, 1f
andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
/* Wheee, local level one interrupt. */
move a0, sp
jal ddb_local1_irqdispatch
nop
j ret_from_irq
nop
1:
beq a0, zero, 1f
nop
/* Wheee, an asynchronous bus error... */
move a0, sp
jal ddb_buserror_irq
nop
j ret_from_irq
nop
1:
/* Here by mistake? This is possible, what can happen
* is that by the time we take the exception the IRQ
* pin goes low, so just leave if this is the case.
*/
andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5)
beq a0, zero, 1f
/* Must be one of the 8254 timers... */
move a0, sp
jal ddb_8254timer_irq
nop
1:
j ret_from_irq
nop
END(ddbIRQ)
......@@ -21,8 +21,6 @@
#include <asm/ddb5xxx/ddb5074.h>
extern asmlinkage void ddbIRQ(void);
static struct irqaction irq_cascade = { no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL };
#define M1543_PNP_CONFIG 0x03f0 /* PnP Config Port */
......@@ -90,7 +88,7 @@ static void m1543_irq_setup(void)
}
void ddb_local0_irqdispatch(struct pt_regs *regs)
static void ddb_local0_irqdispatch(struct pt_regs *regs)
{
u32 mask;
int nile4_irq;
......@@ -118,29 +116,41 @@ void ddb_local0_irqdispatch(struct pt_regs *regs)
}
}
void ddb_local1_irqdispatch(void)
static void ddb_local1_irqdispatch(void)
{
printk("ddb_local1_irqdispatch called\n");
}
void ddb_buserror_irq(void)
static void ddb_buserror_irq(void)
{
printk("ddb_buserror_irq called\n");
}
void ddb_8254timer_irq(void)
static void ddb_8254timer_irq(void)
{
printk("ddb_8254timer_irq called\n");
}
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
unsigned int pending = read_c0_cause() & read_c0_status();
if (pending & CAUSEF_IP2)
ddb_local0_irqdispatch(regs);
else if (pending & CAUSEF_IP3)
ddb_local1_irqdispatch();
else if (pending & CAUSEF_IP6)
ddb_buserror_irq();
else if (pending & (CAUSEF_IP4 | CAUSEF_IP5))
ddb_8254timer_irq();
}
void __init arch_init_irq(void)
{
/* setup cascade interrupts */
setup_irq(NILE4_IRQ_BASE + NILE4_INT_INTE, &irq_cascade);
setup_irq(CPU_IRQ_BASE + CPU_NILE4_CASCADE, &irq_cascade);
set_except_vector(0, ddbIRQ);
nile4_irq_setup(NILE4_IRQ_BASE);
m1543_irq_setup();
init_i8259_irqs();
......
......@@ -3,7 +3,7 @@
# under Linux.
#
obj-y += setup.o irq.o int-handler.o nile4_pic.o vrc5476_irq.o
obj-y += setup.o irq.o nile4_pic.o vrc5476_irq.o
obj-$(CONFIG_KGDB) += dbg_io.o
EXTRA_AFLAGS := $(CFLAGS)
/*
* Copyright 2001 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* First-level interrupt dispatcher for ddb5476
*
* 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.
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/ddb5xxx/ddb5476.h>
/*
* first level interrupt dispatcher for ocelot board -
* We check for the timer first, then check PCI ints A and D.
* Then check for serial IRQ and fall through.
*/
.align 5
NESTED(ddb5476_handle_int, PT_SIZE, sp)
SAVE_ALL
CLI
.set at
.set noreorder
mfc0 t0, CP0_CAUSE
mfc0 t2, CP0_STATUS
and t0, t2
andi t1, t0, STATUSF_IP7 /* cpu timer */
bnez t1, ll_cpu_ip7
andi t1, t0, STATUSF_IP2 /* vrc5476 & i8259 */
bnez t1, ll_cpu_ip2
andi t1, t0, STATUSF_IP3
bnez t1, ll_cpu_ip3
andi t1, t0, STATUSF_IP4
bnez t1, ll_cpu_ip4
andi t1, t0, STATUSF_IP5
bnez t1, ll_cpu_ip5
andi t1, t0, STATUSF_IP6
bnez t1, ll_cpu_ip6
andi t1, t0, STATUSF_IP0 /* software int 0 */
bnez t1, ll_cpu_ip0
andi t1, t0, STATUSF_IP1 /* software int 1 */
bnez t1, ll_cpu_ip1
nop
.set reorder
/* wrong alarm or masked ... */
// jal spurious_interrupt
// j ret_from_irq
move a0, sp
jal vrc5476_irq_dispatch
j ret_from_irq
nop
.align 5
ll_cpu_ip0:
li a0, CPU_IRQ_BASE + 0
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip1:
li a0, CPU_IRQ_BASE + 1
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip2: /* jump to second-level dispatching */
move a0, sp
jal vrc5476_irq_dispatch
j ret_from_irq
ll_cpu_ip3:
li a0, CPU_IRQ_BASE + 3
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip4:
li a0, CPU_IRQ_BASE + 4
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip5:
li a0, CPU_IRQ_BASE + 5
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip6:
li a0, CPU_IRQ_BASE + 6
move a1, sp
jal do_IRQ
j ret_from_irq
ll_cpu_ip7:
li a0, CPU_IRQ_BASE + 7
move a1, sp
jal do_IRQ
j ret_from_irq
END(ddb5476_handle_int)
......@@ -110,11 +110,36 @@ static void nile4_irq_setup(void)
static struct irqaction irq_cascade = { no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL };
static struct irqaction irq_error = { no_action, 0, CPU_MASK_NONE, "error", NULL, NULL };
extern asmlinkage void ddb5476_handle_int(void);
extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
extern void mips_cpu_irq_init(u32 irq_base);
extern void vrc5476_irq_init(u32 irq_base);
extern void vrc5476_irq_dispatch(struct pt_regs *regs);
asmlinkage void plat_irq_dispatch(struct pt_regs *regs)
{
unsigned int pending = read_c0_cause() & read_c0_status();
if (pending & STATUSF_IP7)
do_IRQ(CPU_IRQ_BASE + 7, regs);
else if (pending & STATUSF_IP2)
vrc5476_irq_dispatch(regs);
else if (pending & STATUSF_IP3)
do_IRQ(CPU_IRQ_BASE + 3, regs);
else if (pending & STATUSF_IP4)
do_IRQ(CPU_IRQ_BASE + 4, regs);
else if (pending & STATUSF_IP5)
do_IRQ(CPU_IRQ_BASE + 5, regs);
else if (pending & STATUSF_IP6)
do_IRQ(CPU_IRQ_BASE + 6, regs);
else if (pending & STATUSF_IP0)
do_IRQ(CPU_IRQ_BASE, regs);
else if (pending & STATUSF_IP1)
do_IRQ(CPU_IRQ_BASE + 1, regs);
vrc5476_irq_dispatch(regs);
}
void __init arch_init_irq(void)
{
/* hardware initialization */
......@@ -137,7 +162,4 @@ void __init arch_init_irq(void)
setup_irq(VRC5476_IRQ_BASE + VRC5476_IRQ_LBRT, &irq_error);
setup_irq(VRC5476_IRQ_BASE + VRC5476_IRQ_PCIS, &irq_error);
setup_irq(VRC5476_IRQ_BASE + VRC5476_IRQ_PCI, &irq_error);
/* setup the grandpa intr vector */
set_except_vector(0, ddb5476_handle_int);
}
......@@ -77,7 +77,7 @@ vrc5476_irq_init(u32 base)
}
asmlinkage void
void
vrc5476_irq_dispatch(struct pt_regs *regs)
{
u32 mask;
......
......@@ -2,7 +2,7 @@
# Makefile for NEC DDB-Vrc5477 board
#
obj-y += int-handler.o irq.o irq_5477.o setup.o lcd44780.o
obj-y += irq.o irq_5477.o setup.o lcd44780.o
obj-$(CONFIG_RUNTIME_DEBUG) += debug.o
obj-$(CONFIG_KGDB) += kgdb_io.o
......
/*
* Copyright 2001 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* First-level interrupt dispatcher for ddb5477
*
* 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.
*/
#include <asm/asm.h>
#include <asm/mipsregs.h>
#include <asm/addrspace.h>
#include <asm/regdef.h>
#include <asm/stackframe.h>
#include <asm/ddb5xxx/ddb5477.h>
/*
* first level interrupt dispatcher for ocelot board -
* We check for the timer first, then check PCI ints A and D.
* Then check for serial IRQ and fall through.
*/
.align 5
NESTED(ddb5477_handle_int, PT_SIZE, sp)
SAVE_ALL
CLI
.set at
.set noreorder
mfc0 t0, CP0_CAUSE
mfc0 t2, CP0_STATUS
and t0, t2
andi t1, t0, STATUSF_IP7 /* cpu timer */
bnez t1, ll_cputimer_irq
andi t1, t0, (STATUSF_IP2 | STATUSF_IP3 | STATUSF_IP4 | STATUSF_IP5 | STATUSF_IP6 )
bnez t1, ll_vrc5477_irq
andi t1, t0, STATUSF_IP0 /* software int 0 */
bnez t1, ll_cpu_ip0
andi t1, t0, STATUSF_IP1 /* software int 1 */