aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/dec
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/dec')
-rw-r--r--arch/mips/dec/Makefile11
-rw-r--r--arch/mips/dec/boot/Makefile12
-rw-r--r--arch/mips/dec/boot/decstation.c83
-rw-r--r--arch/mips/dec/boot/ld.ecoff43
-rw-r--r--arch/mips/dec/ecc-berr.c280
-rw-r--r--arch/mips/dec/int-handler.S297
-rw-r--r--arch/mips/dec/ioasic-irq.c157
-rw-r--r--arch/mips/dec/kn02-irq.c127
-rw-r--r--arch/mips/dec/prom/Makefile11
-rw-r--r--arch/mips/dec/prom/call_o32.S91
-rw-r--r--arch/mips/dec/prom/cmdline.c39
-rw-r--r--arch/mips/dec/prom/console.c55
-rw-r--r--arch/mips/dec/prom/dectypes.h14
-rw-r--r--arch/mips/dec/prom/identify.c177
-rw-r--r--arch/mips/dec/prom/init.c134
-rw-r--r--arch/mips/dec/prom/locore.S30
-rw-r--r--arch/mips/dec/prom/memory.c130
-rw-r--r--arch/mips/dec/promcon.c55
-rw-r--r--arch/mips/dec/reset.c41
-rw-r--r--arch/mips/dec/setup.c750
-rw-r--r--arch/mips/dec/time.c200
-rw-r--r--arch/mips/dec/wbflush.c94
22 files changed, 2831 insertions, 0 deletions
diff --git a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile
new file mode 100644
index 00000000000..688757a97cb
--- /dev/null
+++ b/arch/mips/dec/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the DECstation family specific parts of the kernel
+#
+
+obj-y := ecc-berr.o int-handler.o ioasic-irq.o kn02-irq.o reset.o \
+ setup.o time.o
+
+obj-$(CONFIG_PROM_CONSOLE) += promcon.o
+obj-$(CONFIG_CPU_HAS_WB) += wbflush.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/dec/boot/Makefile b/arch/mips/dec/boot/Makefile
new file mode 100644
index 00000000000..bcea41698ef
--- /dev/null
+++ b/arch/mips/dec/boot/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the DECstation family specific parts of the kernel
+#
+
+netboot: all
+ $(LD) -N -G 0 -T ld.ecoff ../../boot/zImage \
+ dec_boot.o ramdisk.img -o nbImage
+
+obj-y := decstation.o
+
+clean:
+ rm -f nbImage
diff --git a/arch/mips/dec/boot/decstation.c b/arch/mips/dec/boot/decstation.c
new file mode 100644
index 00000000000..56fd4277555
--- /dev/null
+++ b/arch/mips/dec/boot/decstation.c
@@ -0,0 +1,83 @@
+/*
+ * arch/mips/dec/decstation.c
+ */
+
+#define RELOC
+#define INITRD
+#define DEBUG_BOOT
+
+/*
+ * Magic number indicating REX PROM available on DECSTATION.
+ */
+#define REX_PROM_MAGIC 0x30464354
+
+#define REX_PROM_CLEARCACHE 0x7c/4
+#define REX_PROM_PRINTF 0x30/4
+
+#define VEC_RESET 0xBFC00000 /* Prom base address */
+#define PMAX_PROM_ENTRY(x) (VEC_RESET+((x)*8)) /* Prom jump table */
+#define PMAX_PROM_PRINTF PMAX_PROM_ENTRY(17)
+
+#define PARAM (k_start + 0x2000)
+
+#define LOADER_TYPE (*(unsigned char *) (PARAM+0x210))
+#define INITRD_START (*(unsigned long *) (PARAM+0x218))
+#define INITRD_SIZE (*(unsigned long *) (PARAM+0x21c))
+
+extern int _ftext, _end; /* begin and end of kernel image */
+extern void kernel_entry(int, char **, unsigned long, int *);
+
+void * memcpy(void * dest, const void *src, unsigned int count)
+{
+ unsigned long *tmp = (unsigned long *) dest, *s = (unsigned long *) src;
+
+ count >>= 2;
+ while (count--)
+ *tmp++ = *s++;
+
+ return dest;
+}
+
+void dec_entry(int argc, char **argv,
+ unsigned long magic, int *prom_vec)
+{
+ void (*rex_clear_cache)(void);
+ int (*prom_printf)(char *, ...);
+ unsigned long k_start, len;
+
+ /*
+ * The DS5100 leaves cpu with BEV enabled, clear it.
+ */
+ asm( "lui\t$8,0x3000\n\t"
+ "mtc0\t$8,$12\n\t"
+ ".section\t.sdata\n\t"
+ ".section\t.sbss\n\t"
+ ".section\t.text"
+ : : : "$8");
+
+#ifdef DEBUG_BOOT
+ if (magic == REX_PROM_MAGIC) {
+ prom_printf = (int (*)(char *, ...)) *(prom_vec + REX_PROM_PRINTF);
+ } else {
+ prom_printf = (int (*)(char *, ...)) PMAX_PROM_PRINTF;
+ }
+ prom_printf("Launching kernel...\n");
+#endif
+
+ k_start = (unsigned long) (&kernel_entry) & 0xffff0000;
+
+#ifdef RELOC
+ /*
+ * Now copy kernel image to its destination.
+ */
+ len = ((unsigned long) (&_end) - k_start);
+ memcpy((void *)k_start, &_ftext, len);
+#endif
+
+ if (magic == REX_PROM_MAGIC) {
+ rex_clear_cache = (void (*)(void)) * (prom_vec + REX_PROM_CLEARCACHE);
+ rex_clear_cache();
+ }
+
+ kernel_entry(argc, argv, magic, prom_vec);
+}
diff --git a/arch/mips/dec/boot/ld.ecoff b/arch/mips/dec/boot/ld.ecoff
new file mode 100644
index 00000000000..aaa633dfb5f
--- /dev/null
+++ b/arch/mips/dec/boot/ld.ecoff
@@ -0,0 +1,43 @@
+OUTPUT_FORMAT("ecoff-littlemips")
+OUTPUT_ARCH(mips)
+ENTRY(dec_entry)
+SECTIONS
+{
+ . = 0x80200000;
+
+ .text :
+ {
+ _ftext = .;
+ *(.text)
+ *(.fixup)
+ }
+ .rdata :
+ {
+ *(.rodata .rodata.* .rdata)
+ }
+ .data :
+ {
+ . = ALIGN(0x1000);
+ ramdisk.img (.data)
+ *(.data)
+ }
+ .sdata :
+ {
+ *(.sdata)
+ }
+ _gp = .;
+ .sbss :
+ {
+ *(.sbss)
+ *(.scommon)
+ }
+ .bss :
+ {
+ *(.dynbss)
+ *(.bss)
+ *(COMMON)
+ }
+ /DISCARD/ : {
+ *(.reginfo .mdebug .note)
+ }
+}
diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c
new file mode 100644
index 00000000000..133fb7c48e6
--- /dev/null
+++ b/arch/mips/dec/ecc-berr.c
@@ -0,0 +1,280 @@
+/*
+ * linux/arch/mips/dec/ecc-berr.c
+ *
+ * Bus error event handling code for systems equipped with ECC
+ * handling logic, i.e. DECstation/DECsystem 5000/200 (KN02),
+ * 5000/240 (KN03), 5000/260 (KN05) and DECsystem 5900 (KN03),
+ * 5900/260 (KN05) systems.
+ *
+ * Copyright (c) 2003 Maciej W. Rozycki
+ *
+ * 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 <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/processor.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+
+#include <asm/dec/ecc.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/kn03.h>
+#include <asm/dec/kn05.h>
+
+static volatile u32 *kn0x_erraddr;
+static volatile u32 *kn0x_chksyn;
+
+static inline void dec_ecc_be_ack(void)
+{
+ *kn0x_erraddr = 0; /* any write clears the IRQ */
+ iob();
+}
+
+static int dec_ecc_be_backend(struct pt_regs *regs, int is_fixup, int invoker)
+{
+ static const char excstr[] = "exception";
+ static const char intstr[] = "interrupt";
+ static const char cpustr[] = "CPU";
+ static const char dmastr[] = "DMA";
+ static const char readstr[] = "read";
+ static const char mreadstr[] = "memory read";
+ static const char writestr[] = "write";
+ static const char mwritstr[] = "partial memory write";
+ static const char timestr[] = "timeout";
+ static const char overstr[] = "overrun";
+ static const char eccstr[] = "ECC error";
+
+ const char *kind, *agent, *cycle, *event;
+ const char *status = "", *xbit = "", *fmt = "";
+ dma_addr_t address;
+ u16 syn = 0, sngl;
+
+ int i = 0;
+
+ u32 erraddr = *kn0x_erraddr;
+ u32 chksyn = *kn0x_chksyn;
+ int action = MIPS_BE_FATAL;
+
+ /* For non-ECC ack ASAP, so any subsequent errors get caught. */
+ if ((erraddr & (KN0X_EAR_VALID | KN0X_EAR_ECCERR)) == KN0X_EAR_VALID)
+ dec_ecc_be_ack();
+
+ kind = invoker ? intstr : excstr;
+
+ if (!(erraddr & KN0X_EAR_VALID)) {
+ /* No idea what happened. */
+ printk(KERN_ALERT "Unidentified bus error %s.\n", kind);
+ return action;
+ }
+
+ agent = (erraddr & KN0X_EAR_CPU) ? cpustr : dmastr;
+
+ if (erraddr & KN0X_EAR_ECCERR) {
+ /* An ECC error on a CPU or DMA transaction. */
+ cycle = (erraddr & KN0X_EAR_WRITE) ? mwritstr : mreadstr;
+ event = eccstr;
+ } else {
+ /* A CPU timeout or a DMA overrun. */
+ cycle = (erraddr & KN0X_EAR_WRITE) ? writestr : readstr;
+ event = (erraddr & KN0X_EAR_CPU) ? timestr : overstr;
+ }
+
+ address = erraddr & KN0X_EAR_ADDRESS;
+ /* For ECC errors on reads adjust for MT pipelining. */
+ if ((erraddr & (KN0X_EAR_WRITE | KN0X_EAR_ECCERR)) == KN0X_EAR_ECCERR)
+ address = (address & ~0xfffLL) | ((address - 5) & 0xfffLL);
+ address <<= 2;
+
+ /* Only CPU errors are fixable. */
+ if (erraddr & KN0X_EAR_CPU && is_fixup)
+ action = MIPS_BE_FIXUP;
+
+ if (erraddr & KN0X_EAR_ECCERR) {
+ static const u8 data_sbit[32] = {
+ 0x4f, 0x4a, 0x52, 0x54, 0x57, 0x58, 0x5b, 0x5d,
+ 0x23, 0x25, 0x26, 0x29, 0x2a, 0x2c, 0x31, 0x34,
+ 0x0e, 0x0b, 0x13, 0x15, 0x16, 0x19, 0x1a, 0x1c,
+ 0x62, 0x64, 0x67, 0x68, 0x6b, 0x6d, 0x70, 0x75,
+ };
+ static const u8 data_mbit[25] = {
+ 0x07, 0x0d, 0x1f,
+ 0x2f, 0x32, 0x37, 0x38, 0x3b, 0x3d, 0x3e,
+ 0x43, 0x45, 0x46, 0x49, 0x4c, 0x51, 0x5e,
+ 0x61, 0x6e, 0x73, 0x76, 0x79, 0x7a, 0x7c, 0x7f,
+ };
+ static const char sbestr[] = "corrected single";
+ static const char dbestr[] = "uncorrectable double";
+ static const char mbestr[] = "uncorrectable multiple";
+
+ if (!(address & 0x4))
+ syn = chksyn; /* Low bank. */
+ else
+ syn = chksyn >> 16; /* High bank. */
+
+ if (!(syn & KN0X_ESR_VLDLO)) {
+ /* Ack now, no rewrite will happen. */
+ dec_ecc_be_ack();
+
+ fmt = KERN_ALERT "%s" "invalid.\n";
+ } else {
+ sngl = syn & KN0X_ESR_SNGLO;
+ syn &= KN0X_ESR_SYNLO;
+
+ /*
+ * Multibit errors may be tagged incorrectly;
+ * check the syndrome explicitly.
+ */
+ for (i = 0; i < 25; i++)
+ if (syn == data_mbit[i])
+ break;
+
+ if (i < 25) {
+ status = mbestr;
+ } else if (!sngl) {
+ status = dbestr;
+ } else {
+ volatile u32 *ptr = (void *)KSEG1ADDR(address);
+
+ *ptr = *ptr; /* Rewrite. */
+ iob();
+
+ status = sbestr;
+ action = MIPS_BE_DISCARD;
+ }
+
+ /* Ack now, now we've rewritten (or not). */
+ dec_ecc_be_ack();
+
+ if (syn && syn == (syn & -syn)) {
+ if (syn == 0x01) {
+ fmt = KERN_ALERT "%s"
+ "%#04x -- %s bit error "
+ "at check bit C%s.\n";
+ xbit = "X";
+ } else {
+ fmt = KERN_ALERT "%s"
+ "%#04x -- %s bit error "
+ "at check bit C%s%u.\n";
+ }
+ i = syn >> 2;
+ } else {
+ for (i = 0; i < 32; i++)
+ if (syn == data_sbit[i])
+ break;
+ if (i < 32)
+ fmt = KERN_ALERT "%s"
+ "%#04x -- %s bit error "
+ "at data bit D%s%u.\n";
+ else
+ fmt = KERN_ALERT "%s"
+ "%#04x -- %s bit error.\n";
+ }
+ }
+ }
+
+ if (action != MIPS_BE_FIXUP)
+ printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx.\n",
+ kind, agent, cycle, event, address);
+
+ if (action != MIPS_BE_FIXUP && erraddr & KN0X_EAR_ECCERR)
+ printk(fmt, " ECC syndrome ", syn, status, xbit, i);
+
+ return action;
+}
+
+int dec_ecc_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ return dec_ecc_be_backend(regs, is_fixup, 0);
+}
+
+irqreturn_t dec_ecc_be_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ int action = dec_ecc_be_backend(regs, 0, 1);
+
+ if (action == MIPS_BE_DISCARD)
+ return IRQ_NONE;
+
+ /*
+ * FIXME: Find affected processes and kill them, otherwise we
+ * must die.
+ *
+ * The interrupt is asynchronously delivered thus EPC and RA
+ * may be irrelevant, but are printed for a reference.
+ */
+ printk(KERN_ALERT "Fatal bus interrupt, epc == %08lx, ra == %08lx\n",
+ regs->cp0_epc, regs->regs[31]);
+ die("Unrecoverable bus error", regs);
+}
+
+
+/*
+ * Initialization differs a bit between KN02 and KN03/KN05, so we
+ * need two variants. Once set up, all systems can be handled the
+ * same way.
+ */
+static inline void dec_kn02_be_init(void)
+{
+ volatile u32 *csr = (void *)KN02_CSR_BASE;
+ unsigned long flags;
+
+ kn0x_erraddr = (void *)(KN02_SLOT_BASE + KN02_ERRADDR);
+ kn0x_chksyn = (void *)(KN02_SLOT_BASE + KN02_CHKSYN);
+
+ spin_lock_irqsave(&kn02_lock, flags);
+
+ /* Preset write-only bits of the Control Register cache. */
+ cached_kn02_csr = *csr | KN03_CSR_LEDS;
+
+ /* Set normal ECC detection and generation. */
+ cached_kn02_csr &= ~(KN02_CSR_DIAGCHK | KN02_CSR_DIAGGEN);
+ /* Enable ECC correction. */
+ cached_kn02_csr |= KN02_CSR_CORRECT;
+ *csr = cached_kn02_csr;
+ iob();
+
+ spin_unlock_irqrestore(&kn02_lock, flags);
+}
+
+static inline void dec_kn03_be_init(void)
+{
+ volatile u32 *mcr = (void *)(KN03_SLOT_BASE + IOASIC_MCR);
+ volatile u32 *mbcs = (void *)(KN03_SLOT_BASE + KN05_MB_CSR);
+
+ kn0x_erraddr = (void *)(KN03_SLOT_BASE + IOASIC_ERRADDR);
+ kn0x_chksyn = (void *)(KN03_SLOT_BASE + IOASIC_CHKSYN);
+
+ /*
+ * Set normal ECC detection and generation, enable ECC correction.
+ * For KN05 we also need to make sure EE (?) is enabled in the MB.
+ * Otherwise DBE/IBE exceptions would be masked but bus error
+ * interrupts would still arrive, resulting in an inevitable crash
+ * if get_dbe() triggers one.
+ */
+ *mcr = (*mcr & ~(KN03_MCR_DIAGCHK | KN03_MCR_DIAGGEN)) |
+ KN03_MCR_CORRECT;
+ if (current_cpu_data.cputype == CPU_R4400SC)
+ *mbcs |= KN05_MB_CSR_EE;
+ fast_iob();
+}
+
+void __init dec_ecc_be_init(void)
+{
+ if (mips_machtype == MACH_DS5000_200)
+ dec_kn02_be_init();
+ else
+ dec_kn03_be_init();
+
+ /* Clear any leftover errors from the firmware. */
+ dec_ecc_be_ack();
+}
diff --git a/arch/mips/dec/int-handler.S b/arch/mips/dec/int-handler.S
new file mode 100644
index 00000000000..3b379099321
--- /dev/null
+++ b/arch/mips/dec/int-handler.S
@@ -0,0 +1,297 @@
+/*
+ * arch/mips/dec/int-handler.S
+ *
+ * Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen
+ * Copyright (C) 2000, 2001, 2002, 2003 Maciej W. Rozycki
+ *
+ * Written by Ralf Baechle and Andreas Busse, modified for DECStation
+ * support by Paul Antoine and Harald Koerfgen.
+ *
+ * completly rewritten:
+ * Copyright (C) 1998 Harald Koerfgen
+ *
+ * Rewritten extensively for controller-driven IRQ support
+ * by Maciej W. Rozycki.
+ */
+#include <linux/config.h>
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/addrspace.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/ioasic_ints.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/kn02xa.h>
+#include <asm/dec/kn03.h>
+
+
+ .text
+ .set noreorder
+/*
+ * decstation_handle_int: Interrupt handler for DECStations
+ *
+ * We follow the model in the Indy interrupt code by David Miller, where he
+ * says: 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 DECStations look basically (barring
+ * software IRQs which we don't use at all) like...
+ *
+ * DS2100/3100's, aka kn01, aka Pmax:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 SCSI
+ * 3 Lance Ethernet
+ * 4 DZ11 serial
+ * 5 RTC
+ * 6 Memory Controller
+ * 7 FPU
+ *
+ * DS5000/200, aka kn02, aka 3max:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 TurboChannel
+ * 3 RTC
+ * 4 Reserved
+ * 5 Memory Controller
+ * 6 Reserved
+ * 7 FPU
+ *
+ * DS5000/1xx's, aka kn02ba, aka 3min:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 TurboChannel Slot 0
+ * 3 TurboChannel Slot 1
+ * 4 TurboChannel Slot 2
+ * 5 TurboChannel Slot 3 (ASIC)
+ * 6 Halt button
+ * 7 FPU/R4k timer
+ *
+ * DS5000/2x's, aka kn02ca, aka maxine:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 Periodic Interrupt (100usec)
+ * 3 RTC
+ * 4 I/O write timeout
+ * 5 TurboChannel (ASIC)
+ * 6 Halt Keycode from Access.Bus keyboard (CTRL-ALT-ENTER)
+ * 7 FPU/R4k timer
+ *
+ * DS5000/2xx's, aka kn03, aka 3maxplus:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 System Board (ASIC)
+ * 3 RTC
+ * 4 Reserved
+ * 5 Memory
+ * 6 Halt Button
+ * 7 FPU/R4k timer
+ *
+ * We handle the IRQ according to _our_ priority (see setup.c),
+ * then we just return. If multiple IRQs are pending then we will
+ * just take another exception, big deal.
+ */
+ .align 5
+ NESTED(decstation_handle_int, PT_SIZE, ra)
+ .set noat
+ SAVE_ALL
+ CLI # TEST: interrupts should be off
+ .set at
+ .set noreorder
+
+ /*
+ * Get pending Interrupts
+ */
+ mfc0 t0,CP0_CAUSE # get pending interrupts
+ mfc0 t1,CP0_STATUS
+#ifdef CONFIG_MIPS32
+ lw t2,cpu_fpu_mask
+#endif
+ andi t0,ST0_IM # CAUSE.CE may be non-zero!
+ and t0,t1 # isolate allowed ones
+
+ beqz t0,spurious
+
+#ifdef CONFIG_MIPS32
+ and t2,t0
+ bnez t2,fpu # handle FPU immediately
+#endif
+
+ /*
+ * Find irq with highest priority
+ */
+ PTR_LA t1,cpu_mask_nr_tbl
+1: lw t2,(t1)
+ nop
+ and t2,t0
+ beqz t2,1b
+ addu t1,2*PTRSIZE # delay slot
+
+ /*
+ * Do the low-level stuff
+ */
+ lw a0,(-PTRSIZE)(t1)
+ nop
+ bgez a0,handle_it # irq_nr >= 0?
+ # irq_nr < 0: it is an address
+ nop
+ jr a0
+ # a trick to save a branch:
+ lui t2,(KN03_IOASIC_BASE>>16)&0xffff
+ # upper part of IOASIC Address
+
+/*
+ * Handle "IRQ Controller" Interrupts
+ * Masked Interrupts are still visible and have to be masked "by hand".
+ */
+ FEXPORT(kn02_io_int) # 3max
+ lui t0,(KN02_CSR_BASE>>16)&0xffff
+ # get interrupt status and mask
+ lw t0,(t0)
+ nop
+ andi t1,t0,KN02_IRQ_ALL
+ b 1f
+ srl t0,16 # shift interrupt mask
+
+ FEXPORT(kn02xa_io_int) # 3min/maxine
+ lui t2,(KN02XA_IOASIC_BASE>>16)&0xffff
+ # upper part of IOASIC Address
+
+ FEXPORT(kn03_io_int) # 3max+ (t2 loaded earlier)
+ lw t0,IO_REG_SIR(t2) # get status: IOASIC sir
+ lw t1,IO_REG_SIMR(t2) # get mask: IOASIC simr
+ nop
+
+1: and t0,t1 # mask out allowed ones
+
+ beqz t0,spurious
+
+ /*
+ * Find irq with highest priority
+ */
+ PTR_LA t1,asic_mask_nr_tbl
+2: lw t2,(t1)
+ nop
+ and t2,t0
+ beq zero,t2,2b
+ addu t1,2*PTRSIZE # delay slot
+
+ /*
+ * Do the low-level stuff
+ */
+ lw a0,%lo(-PTRSIZE)(t1)
+ nop
+ bgez a0,handle_it # irq_nr >= 0?
+ # irq_nr < 0: it is an address
+ nop
+ jr a0
+ nop # delay slot
+
+/*
+ * Dispatch low-priority interrupts. We reconsider all status
+ * bits again, which looks like a lose, but it makes the code
+ * simple and O(log n), so it gets compensated.
+ */
+ FEXPORT(cpu_all_int) # HALT, timers, software junk
+ li a0,DEC_CPU_IRQ_BASE
+ srl t0,CAUSEB_IP
+ li t1,CAUSEF_IP>>CAUSEB_IP # mask
+ b 1f
+ li t2,4 # nr of bits / 2
+
+ FEXPORT(kn02_all_int) # impossible ?
+ li a0,KN02_IRQ_BASE
+ li t1,KN02_IRQ_ALL # mask
+ b 1f
+ li t2,4 # nr of bits / 2
+
+ FEXPORT(asic_all_int) # various I/O ASIC junk
+ li a0,IO_IRQ_BASE
+ li t1,IO_IRQ_ALL # mask
+ b 1f
+ li t2,8 # nr of bits / 2
+
+/*
+ * Dispatch DMA interrupts -- O(log n).
+ */
+ FEXPORT(asic_dma_int) # I/O ASIC DMA events
+ li a0,IO_IRQ_BASE+IO_INR_DMA
+ srl t0,IO_INR_DMA
+ li t1,IO_IRQ_DMA>>IO_INR_DMA # mask
+ li t2,8 # nr of bits / 2
+
+ /*
+ * Find irq with highest priority.
+ * Highest irq number takes precedence.
+ */
+1: srlv t3,t1,t2
+2: xor t1,t3
+ and t3,t0,t1
+ beqz t3,3f
+ nop
+ move t0,t3
+ addu a0,t2
+3: srl t2,1
+ bnez t2,2b
+ srlv t3,t1,t2
+
+handle_it:
+ jal do_IRQ
+ move a1,sp
+
+ j ret_from_irq
+ nop
+
+#ifdef CONFIG_MIPS32
+fpu:
+ j handle_fpe_int
+ nop
+#endif
+
+spurious:
+ j spurious_interrupt
+ nop
+ END(decstation_handle_int)
+
+/*
+ * Generic unimplemented interrupt routines -- cpu_mask_nr_tbl
+ * and asic_mask_nr_tbl are initialized to point all interrupts here.
+ * The tables are then filled in by machine-specific initialisation
+ * in dec_setup().
+ */
+ FEXPORT(dec_intr_unimplemented)
+ move a1,t0 # cheats way of printing an arg!
+ PANIC("Unimplemented cpu interrupt! CP0_CAUSE: 0x%08x");
+
+ FEXPORT(asic_intr_unimplemented)
+ move a1,t0 # cheats way of printing an arg!
+ PANIC("Unimplemented asic interrupt! ASIC ISR: 0x%08x");
diff --git a/arch/mips/dec/ioasic-irq.c b/arch/mips/dec/ioasic-irq.c
new file mode 100644
index 00000000000..d5bca5d233b
--- /dev/null
+++ b/arch/mips/dec/ioasic-irq.c
@@ -0,0 +1,157 @@
+/*
+ * linux/arch/mips/dec/ioasic-irq.c
+ *
+ * DEC I/O ASIC interrupts.
+ *
+ * Copyright (c) 2002, 2003 Maciej W. Rozycki
+ *
+ * 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 <linux/init.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/dec/ioasic.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/ioasic_ints.h>
+
+
+static DEFINE_SPINLOCK(ioasic_lock);
+
+static int ioasic_irq_base;
+
+
+static inline void unmask_ioasic_irq(unsigned int irq)
+{
+ u32 simr;
+
+ simr = ioasic_read(IO_REG_SIMR);
+ simr |= (1 << (irq - ioasic_irq_base));
+ ioasic_write(IO_REG_SIMR, simr);
+}
+
+static inline void mask_ioasic_irq(unsigned int irq)
+{
+ u32 simr;
+
+ simr = ioasic_read(IO_REG_SIMR);
+ simr &= ~(1 << (irq - ioasic_irq_base));
+ ioasic_write(IO_REG_SIMR, simr);
+}
+
+static inline void clear_ioasic_irq(unsigned int irq)
+{
+ u32 sir;
+
+ sir = ~(1 << (irq - ioasic_irq_base));
+ ioasic_write(IO_REG_SIR, sir);
+}
+
+static inline void enable_ioasic_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioasic_lock, flags);
+ unmask_ioasic_irq(irq);
+ spin_unlock_irqrestore(&ioasic_lock, flags);
+}
+
+static inline void disable_ioasic_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ioasic_lock, flags);
+ mask_ioasic_irq(irq);
+ spin_unlock_irqrestore(&ioasic_lock, flags);
+}
+
+
+static inline unsigned int startup_ioasic_irq(unsigned int irq)
+{
+ enable_ioasic_irq(irq);
+ return 0;
+}
+
+#define shutdown_ioasic_irq disable_ioasic_irq
+
+static inline void ack_ioasic_irq(unsigned int irq)
+{
+ spin_lock(&ioasic_lock);
+ mask_ioasic_irq(irq);
+ spin_unlock(&ioasic_lock);
+ fast_iob();
+}
+
+static inline void end_ioasic_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_ioasic_irq(irq);
+}
+
+static struct hw_interrupt_type ioasic_irq_type = {
+ .typename = "IO-ASIC",
+ .startup = startup_ioasic_irq,
+ .shutdown = shutdown_ioasic_irq,
+ .enable = enable_ioasic_irq,
+ .disable = disable_ioasic_irq,
+ .ack = ack_ioasic_irq,
+ .end = end_ioasic_irq,
+};
+
+
+#define startup_ioasic_dma_irq startup_ioasic_irq
+
+#define shutdown_ioasic_dma_irq shutdown_ioasic_irq
+
+#define enable_ioasic_dma_irq enable_ioasic_irq
+
+#define disable_ioasic_dma_irq disable_ioasic_irq
+
+#define ack_ioasic_dma_irq ack_ioasic_irq
+
+static inline void end_ioasic_dma_irq(unsigned int irq)
+{
+ clear_ioasic_irq(irq);
+ fast_iob();
+ end_ioasic_irq(irq);
+}
+
+static struct hw_interrupt_type ioasic_dma_irq_type = {
+ .typename = "IO-ASIC-DMA",
+ .startup = startup_ioasic_dma_irq,
+ .shutdown = shutdown_ioasic_dma_irq,
+ .enable = enable_ioasic_dma_irq,
+ .disable = disable_ioasic_dma_irq,
+ .ack = ack_ioasic_dma_irq,
+ .end = end_ioasic_dma_irq,
+};
+
+
+void __init init_ioasic_irqs(int base)
+{
+ int i;
+
+ /* Mask interrupts. */
+ ioasic_write(IO_REG_SIMR, 0);
+ fast_iob();
+
+ for (i = base; i < base + IO_INR_DMA; i++) {
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = 0;
+ irq_desc[i].depth = 1;
+ irq_desc[i].handler = &ioasic_irq_type;
+ }
+ for (; i < base + IO_IRQ_LINES; i++) {
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = 0;
+ irq_desc[i].depth = 1;
+ irq_desc[i].handler = &ioasic_dma_irq_type;
+ }
+
+ ioasic_irq_base = base;
+}
diff --git a/arch/mips/dec/kn02-irq.c b/arch/mips/dec/kn02-irq.c
new file mode 100644
index 00000000000..e0bfcd1521e
--- /dev/null
+++ b/arch/mips/dec/kn02-irq.c
@@ -0,0 +1,127 @@
+/*
+ * linux/arch/mips/dec/kn02-irq.c
+ *
+ * DECstation 5000/200 (KN02) Control and Status Register
+ * interrupts.
+ *
+ * Copyright (c) 2002, 2003 Maciej W. Rozycki
+ *
+ * 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 <linux/init.h>
+#include <linux/irq.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/dec/kn02.h>
+
+
+/*
+ * Bits 7:0 of the Control Register are write-only -- the
+ * corresponding bits of the Status Register have a different
+ * meaning. Hence we use a cache. It speeds up things a bit
+ * as well.
+ *
+ * There is no default value -- it has to be initialized.
+ */
+u32 cached_kn02_csr;
+DEFINE_SPINLOCK(kn02_lock);
+
+
+static int kn02_irq_base;
+
+
+static inline void unmask_kn02_irq(unsigned int irq)
+{
+ volatile u32 *csr = (volatile u32 *)KN02_CSR_BASE;
+
+ cached_kn02_csr |= (1 << (irq - kn02_irq_base + 16));
+ *csr = cached_kn02_csr;
+}
+
+static inline void mask_kn02_irq(unsigned int irq)
+{
+ volatile u32 *csr = (volatile u32 *)KN02_CSR_BASE;
+
+ cached_kn02_csr &= ~(1 << (irq - kn02_irq_base + 16));
+ *csr = cached_kn02_csr;
+}
+
+static inline void enable_kn02_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kn02_lock, flags);
+ unmask_kn02_irq(irq);
+ spin_unlock_irqrestore(&kn02_lock, flags);
+}
+
+static inline void disable_kn02_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&kn02_lock, flags);
+ mask_kn02_irq(irq);
+ spin_unlock_irqrestore(&kn02_lock, flags);
+}
+
+
+static unsigned int startup_kn02_irq(unsigned int irq)
+{
+ enable_kn02_irq(irq);
+ return 0;
+}
+
+#define shutdown_kn02_irq disable_kn02_irq
+
+static void ack_kn02_irq(unsigned int irq)
+{
+ spin_lock(&kn02_lock);
+ mask_kn02_irq(irq);
+ spin_unlock(&kn02_lock);
+ iob();
+}
+
+static void end_kn02_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_kn02_irq(irq);
+}
+
+static struct hw_interrupt_type kn02_irq_type = {
+ .typename = "KN02-CSR",
+ .startup = startup_kn02_irq,
+ .shutdown = shutdown_kn02_irq,
+ .enable = enable_kn02_irq,
+ .disable = disable_kn02_irq,
+ .ack = ack_kn02_irq,
+ .end = end_kn02_irq,
+};
+
+
+void __init init_kn02_irqs(int base)
+{
+ volatile u32 *csr = (volatile u32 *)KN02_CSR_BASE;
+ unsigned long flags;
+ int i;
+
+ /* Mask interrupts. */
+ spin_lock_irqsave(&kn02_lock, flags);
+ cached_kn02_csr &= ~KN03_CSR_IOINTEN;
+ *csr = cached_kn02_csr;
+ iob();
+ spin_unlock_irqrestore(&kn02_lock, flags);
+
+ for (i = base; i < base + KN02_IRQ_LINES; i++) {
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = 0;
+ irq_desc[i].depth = 1;
+ irq_desc[i].handler = &kn02_irq_type;
+ }
+
+ kn02_irq_base = base;
+}
diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile
new file mode 100644
index 00000000000..373822ec2d8
--- /dev/null
+++ b/arch/mips/dec/prom/Makefile