v4.19.13 snapshot.
diff --git a/arch/mips/dec/Makefile b/arch/mips/dec/Makefile
new file mode 100644
index 0000000..bd74e05
--- /dev/null
+++ b/arch/mips/dec/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the DECstation family specific parts of the kernel
+#
+
+obj-y		:= ecc-berr.o int-handler.o ioasic-irq.o kn01-berr.o \
+		   kn02-irq.o kn02xa-berr.o platform.o reset.o setup.o time.o
+
+obj-$(CONFIG_TC)		+= tc.o
+obj-$(CONFIG_CPU_HAS_WB)	+= wbflush.o
diff --git a/arch/mips/dec/Platform b/arch/mips/dec/Platform
new file mode 100644
index 0000000..cf55a6f
--- /dev/null
+++ b/arch/mips/dec/Platform
@@ -0,0 +1,8 @@
+#
+# DECstation family
+#
+platform-$(CONFIG_MACH_DECSTATION)	+= dec/
+cflags-$(CONFIG_MACH_DECSTATION)	+= \
+			-I$(srctree)/arch/mips/include/asm/mach-dec
+libs-$(CONFIG_MACH_DECSTATION)		+= arch/mips/dec/prom/
+load-$(CONFIG_MACH_DECSTATION)		+= 0xffffffff80040000
diff --git a/arch/mips/dec/ecc-berr.c b/arch/mips/dec/ecc-berr.c
new file mode 100644
index 0000000..2a66e90
--- /dev/null
+++ b/arch/mips/dec/ecc-berr.c
@@ -0,0 +1,278 @@
+/*
+ *	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, 2005  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/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/cpu-type.h>
+#include <asm/irq_regs.h>
+#include <asm/processor.h>
+#include <asm/ptrace.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 = "";
+	unsigned long 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 that 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 *)CKSEG1ADDR(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 = get_irq_regs();
+
+	int action = dec_ecc_be_backend(regs, 0, 1);
+
+	if (action == MIPS_BE_DISCARD)
+		return IRQ_HANDLED;
+
+	/*
+	 * FIXME: Find the 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 *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR);
+
+	kn0x_erraddr = (void *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_ERRADDR);
+	kn0x_chksyn = (void *)CKSEG1ADDR(KN02_SLOT_BASE + KN02_CHKSYN);
+
+	/* Preset write-only bits of the Control Register cache. */
+	cached_kn02_csr = *csr | KN02_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();
+}
+
+static inline void dec_kn03_be_init(void)
+{
+	volatile u32 *mcr = (void *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_MCR);
+	volatile u32 *mbcs = (void *)CKSEG1ADDR(KN4K_SLOT_BASE + KN4K_MB_CSR);
+
+	kn0x_erraddr = (void *)CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_ERRADDR);
+	kn0x_chksyn = (void *)CKSEG1ADDR(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_type() == CPU_R4400SC)
+		*mbcs |= KN4K_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 0000000..a25ef82
--- /dev/null
+++ b/arch/mips/dec/int-handler.S
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 1995, 1996, 1997 Paul M. Antoine and Harald Koerfgen
+ * Copyright (C) 2000, 2001, 2002, 2003, 2005  Maciej W. Rozycki
+ *
+ * Written by Ralf Baechle and Andreas Busse, modified for DECstation
+ * support by Paul Antoine and Harald Koerfgen.
+ *
+ * completely rewritten:
+ * Copyright (C) 1998 Harald Koerfgen
+ *
+ * Rewritten extensively for controller-driven IRQ support
+ * by Maciej W. Rozycki.
+ */
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.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>
+
+#define KN02_CSR_BASE		CKSEG1ADDR(KN02_SLOT_BASE + KN02_CSR)
+#define KN02XA_IOASIC_BASE	CKSEG1ADDR(KN02XA_SLOT_BASE + IOASIC_IOCTL)
+#define KN03_IOASIC_BASE	CKSEG1ADDR(KN03_SLOT_BASE + IOASIC_IOCTL)
+
+		.text
+		.set	noreorder
+/*
+ * plat_irq_dispatch: 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 & Video
+ *	       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(plat_irq_dispatch, PT_SIZE, ra)
+		.set	noreorder
+
+		/*
+		 * Get pending Interrupts
+		 */
+		mfc0	t0,CP0_CAUSE		# get pending interrupts
+		mfc0	t1,CP0_STATUS
+#ifdef CONFIG_32BIT
+		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_32BIT
+		 and	t2,t0
+		bnez	t2,fpu			# handle FPU immediately
+#endif
+
+		/*
+		 * Find irq with highest priority
+		 */
+		# open coded PTR_LA t1, cpu_mask_nr_tbl
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+		# open coded la t1, cpu_mask_nr_tbl
+		lui	t1, %hi(cpu_mask_nr_tbl)
+		addiu	t1, %lo(cpu_mask_nr_tbl)
+#else
+#error GCC `-msym32' option required for 64-bit DECstation builds
+#endif
+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
+		 */
+		# open coded PTR_LA t1,asic_mask_nr_tbl
+#if defined(CONFIG_32BIT) || defined(KBUILD_64BIT_SYM32)
+		# open coded la t1, asic_mask_nr_tbl
+		lui	t1, %hi(asic_mask_nr_tbl)
+		addiu	t1, %lo(asic_mask_nr_tbl)
+#else
+#error GCC `-msym32' option required for 64-bit DECstation builds
+#endif
+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:
+		j	dec_irq_dispatch
+		 nop
+
+#ifdef CONFIG_32BIT
+fpu:
+		lw	t0,fpu_kstat_irq
+		nop
+		lw	t1,(t0)
+		nop
+		addu	t1,1
+		j	handle_fpe_int
+		 sw	t1,(t0)
+#endif
+
+spurious:
+		j	spurious_interrupt
+		 nop
+		END(plat_irq_dispatch)
+
+/*
+ * 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 0000000..e04d973
--- /dev/null
+++ b/arch/mips/dec/ioasic-irq.c
@@ -0,0 +1,116 @@
+/*
+ *	DEC I/O ASIC interrupts.
+ *
+ *	Copyright (c) 2002, 2003, 2013  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/types.h>
+
+#include <asm/dec/ioasic.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/ioasic_ints.h>
+
+static int ioasic_irq_base;
+
+static void unmask_ioasic_irq(struct irq_data *d)
+{
+	u32 simr;
+
+	simr = ioasic_read(IO_REG_SIMR);
+	simr |= (1 << (d->irq - ioasic_irq_base));
+	ioasic_write(IO_REG_SIMR, simr);
+}
+
+static void mask_ioasic_irq(struct irq_data *d)
+{
+	u32 simr;
+
+	simr = ioasic_read(IO_REG_SIMR);
+	simr &= ~(1 << (d->irq - ioasic_irq_base));
+	ioasic_write(IO_REG_SIMR, simr);
+}
+
+static void ack_ioasic_irq(struct irq_data *d)
+{
+	mask_ioasic_irq(d);
+	fast_iob();
+}
+
+static struct irq_chip ioasic_irq_type = {
+	.name = "IO-ASIC",
+	.irq_ack = ack_ioasic_irq,
+	.irq_mask = mask_ioasic_irq,
+	.irq_mask_ack = ack_ioasic_irq,
+	.irq_unmask = unmask_ioasic_irq,
+};
+
+static void clear_ioasic_dma_irq(struct irq_data *d)
+{
+	u32 sir;
+
+	sir = ~(1 << (d->irq - ioasic_irq_base));
+	ioasic_write(IO_REG_SIR, sir);
+	fast_iob();
+}
+
+static struct irq_chip ioasic_dma_irq_type = {
+	.name = "IO-ASIC-DMA",
+	.irq_ack = clear_ioasic_dma_irq,
+	.irq_mask = mask_ioasic_irq,
+	.irq_unmask = unmask_ioasic_irq,
+	.irq_eoi = clear_ioasic_dma_irq,
+};
+
+/*
+ * I/O ASIC implements two kinds of DMA interrupts, informational and
+ * error interrupts.
+ *
+ * The formers do not stop DMA and should be cleared as soon as possible
+ * so that if they retrigger before the handler has completed, usually as
+ * a side effect of actions taken by the handler, then they are reissued.
+ * These use the `handle_edge_irq' handler that clears the request right
+ * away.
+ *
+ * The latters stop DMA and do not resume it until the interrupt has been
+ * cleared.  This cannot be done until after a corrective action has been
+ * taken and this also means they will not retrigger.  Therefore they use
+ * the `handle_fasteoi_irq' handler that only clears the request on the
+ * way out.  Because MIPS processor interrupt inputs, one of which the I/O
+ * ASIC is cascaded to, are level-triggered it is recommended that error
+ * DMA interrupt action handlers are registered with the IRQF_ONESHOT flag
+ * set so that they are run with the interrupt line masked.
+ *
+ * This mask has `1' bits in the positions of informational interrupts.
+ */
+#define IO_IRQ_DMA_INFO							\
+	(IO_IRQ_MASK(IO_INR_SCC0A_RXDMA) |				\
+	 IO_IRQ_MASK(IO_INR_SCC1A_RXDMA) |				\
+	 IO_IRQ_MASK(IO_INR_ISDN_TXDMA) |				\
+	 IO_IRQ_MASK(IO_INR_ISDN_RXDMA) |				\
+	 IO_IRQ_MASK(IO_INR_ASC_DMA))
+
+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_set_chip_and_handler(i, &ioasic_irq_type,
+					 handle_level_irq);
+	for (; i < base + IO_IRQ_LINES; i++)
+		irq_set_chip_and_handler(i, &ioasic_dma_irq_type,
+					 1 << (i - base) & IO_IRQ_DMA_INFO ?
+					 handle_edge_irq : handle_fasteoi_irq);
+
+	ioasic_irq_base = base;
+}
diff --git a/arch/mips/dec/kn01-berr.c b/arch/mips/dec/kn01-berr.c
new file mode 100644
index 0000000..e9d2db4
--- /dev/null
+++ b/arch/mips/dec/kn01-berr.c
@@ -0,0 +1,200 @@
+/*
+ *	Bus error event handling code for DECstation/DECsystem 3100
+ *	and 2100 (KN01) systems equipped with parity error detection
+ *	logic.
+ *
+ *	Copyright (c) 2005  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/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+
+#include <asm/inst.h>
+#include <asm/irq_regs.h>
+#include <asm/mipsregs.h>
+#include <asm/page.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+#include <linux/uaccess.h>
+
+#include <asm/dec/kn01.h>
+
+
+/* CP0 hazard avoidance. */
+#define BARRIER				\
+	__asm__ __volatile__(		\
+		".set	push\n\t"	\
+		".set	noreorder\n\t"	\
+		"nop\n\t"		\
+		".set	pop\n\t")
+
+/*
+ * 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.
+ */
+u16 cached_kn01_csr;
+static DEFINE_RAW_SPINLOCK(kn01_lock);
+
+
+static inline void dec_kn01_be_ack(void)
+{
+	volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&kn01_lock, flags);
+
+	*csr = cached_kn01_csr | KN01_CSR_MEMERR;	/* Clear bus IRQ. */
+	iob();
+
+	raw_spin_unlock_irqrestore(&kn01_lock, flags);
+}
+
+static int dec_kn01_be_backend(struct pt_regs *regs, int is_fixup, int invoker)
+{
+	volatile u32 *kn01_erraddr = (void *)CKSEG1ADDR(KN01_SLOT_BASE +
+							KN01_ERRADDR);
+
+	static const char excstr[] = "exception";
+	static const char intstr[] = "interrupt";
+	static const char cpustr[] = "CPU";
+	static const char mreadstr[] = "memory read";
+	static const char readstr[] = "read";
+	static const char writestr[] = "write";
+	static const char timestr[] = "timeout";
+	static const char paritystr[] = "parity error";
+
+	int data = regs->cp0_cause & 4;
+	unsigned int __user *pc = (unsigned int __user *)regs->cp0_epc +
+				  ((regs->cp0_cause & CAUSEF_BD) != 0);
+	union mips_instruction insn;
+	unsigned long entrylo, offset;
+	long asid, entryhi, vaddr;
+
+	const char *kind, *agent, *cycle, *event;
+	unsigned long address;
+
+	u32 erraddr = *kn01_erraddr;
+	int action = MIPS_BE_FATAL;
+
+	/* Ack ASAP, so that any subsequent errors get caught. */
+	dec_kn01_be_ack();
+
+	kind = invoker ? intstr : excstr;
+
+	agent = cpustr;
+
+	if (invoker)
+		address = erraddr;
+	else {
+		/* Bloody hardware doesn't record the address for reads... */
+		if (data) {
+			/* This never faults. */
+			__get_user(insn.word, pc);
+			vaddr = regs->regs[insn.i_format.rs] +
+				insn.i_format.simmediate;
+		} else
+			vaddr = (long)pc;
+		if (KSEGX(vaddr) == CKSEG0 || KSEGX(vaddr) == CKSEG1)
+			address = CPHYSADDR(vaddr);
+		else {
+			/* Peek at what physical address the CPU used. */
+			asid = read_c0_entryhi();
+			entryhi = asid & (PAGE_SIZE - 1);
+			entryhi |= vaddr & ~(PAGE_SIZE - 1);
+			write_c0_entryhi(entryhi);
+			BARRIER;
+			tlb_probe();
+			/* No need to check for presence. */
+			tlb_read();
+			entrylo = read_c0_entrylo0();
+			write_c0_entryhi(asid);
+			offset = vaddr & (PAGE_SIZE - 1);
+			address = (entrylo & ~(PAGE_SIZE - 1)) | offset;
+		}
+	}
+
+	/* Treat low 256MB as memory, high -- as I/O. */
+	if (address < 0x10000000) {
+		cycle = mreadstr;
+		event = paritystr;
+	} else {
+		cycle = invoker ? writestr : readstr;
+		event = timestr;
+	}
+
+	if (is_fixup)
+		action = MIPS_BE_FIXUP;
+
+	if (action != MIPS_BE_FIXUP)
+		printk(KERN_ALERT "Bus error %s: %s %s %s at %#010lx\n",
+			kind, agent, cycle, event, address);
+
+	return action;
+}
+
+int dec_kn01_be_handler(struct pt_regs *regs, int is_fixup)
+{
+	return dec_kn01_be_backend(regs, is_fixup, 0);
+}
+
+irqreturn_t dec_kn01_be_interrupt(int irq, void *dev_id)
+{
+	volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
+	struct pt_regs *regs = get_irq_regs();
+	int action;
+
+	if (!(*csr & KN01_CSR_MEMERR))
+		return IRQ_NONE;		/* Must have been video. */
+
+	action = dec_kn01_be_backend(regs, 0, 1);
+
+	if (action == MIPS_BE_DISCARD)
+		return IRQ_HANDLED;
+
+	/*
+	 * FIXME: Find the 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);
+}
+
+
+void __init dec_kn01_be_init(void)
+{
+	volatile u16 *csr = (void *)CKSEG1ADDR(KN01_SLOT_BASE + KN01_CSR);
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&kn01_lock, flags);
+
+	/* Preset write-only bits of the Control Register cache. */
+	cached_kn01_csr = *csr;
+	cached_kn01_csr &= KN01_CSR_STATUS | KN01_CSR_PARDIS | KN01_CSR_TXDIS;
+	cached_kn01_csr |= KN01_CSR_LEDS;
+
+	/* Enable parity error detection. */
+	cached_kn01_csr &= ~KN01_CSR_PARDIS;
+	*csr = cached_kn01_csr;
+	iob();
+
+	raw_spin_unlock_irqrestore(&kn01_lock, flags);
+
+	/* Clear any leftover errors from the firmware. */
+	dec_kn01_be_ack();
+}
diff --git a/arch/mips/dec/kn02-irq.c b/arch/mips/dec/kn02-irq.c
new file mode 100644
index 0000000..37199f7
--- /dev/null
+++ b/arch/mips/dec/kn02-irq.c
@@ -0,0 +1,79 @@
+/*
+ *	DECstation 5000/200 (KN02) Control and Status Register
+ *	interrupts.
+ *
+ *	Copyright (c) 2002, 2003, 2005  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/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;
+
+static int kn02_irq_base;
+
+static void unmask_kn02_irq(struct irq_data *d)
+{
+	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
+						       KN02_CSR);
+
+	cached_kn02_csr |= (1 << (d->irq - kn02_irq_base + 16));
+	*csr = cached_kn02_csr;
+}
+
+static void mask_kn02_irq(struct irq_data *d)
+{
+	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
+						       KN02_CSR);
+
+	cached_kn02_csr &= ~(1 << (d->irq - kn02_irq_base + 16));
+	*csr = cached_kn02_csr;
+}
+
+static void ack_kn02_irq(struct irq_data *d)
+{
+	mask_kn02_irq(d);
+	iob();
+}
+
+static struct irq_chip kn02_irq_type = {
+	.name = "KN02-CSR",
+	.irq_ack = ack_kn02_irq,
+	.irq_mask = mask_kn02_irq,
+	.irq_mask_ack = ack_kn02_irq,
+	.irq_unmask = unmask_kn02_irq,
+};
+
+void __init init_kn02_irqs(int base)
+{
+	volatile u32 *csr = (volatile u32 *)CKSEG1ADDR(KN02_SLOT_BASE +
+						       KN02_CSR);
+	int i;
+
+	/* Mask interrupts. */
+	cached_kn02_csr &= ~KN02_CSR_IOINTEN;
+	*csr = cached_kn02_csr;
+	iob();
+
+	for (i = base; i < base + KN02_IRQ_LINES; i++)
+		irq_set_chip_and_handler(i, &kn02_irq_type, handle_level_irq);
+
+	kn02_irq_base = base;
+}
diff --git a/arch/mips/dec/kn02xa-berr.c b/arch/mips/dec/kn02xa-berr.c
new file mode 100644
index 0000000..ec60636
--- /dev/null
+++ b/arch/mips/dec/kn02xa-berr.c
@@ -0,0 +1,139 @@
+/*
+ *	Bus error event handling code for 5000-series systems equipped
+ *	with parity error detection logic, i.e. DECstation/DECsystem
+ *	5000/120, /125, /133 (KN02-BA), 5000/150 (KN04-BA) and Personal
+ *	DECstation/DECsystem 5000/20, /25, /33 (KN02-CA), 5000/50
+ *	(KN04-CA) systems.
+ *
+ *	Copyright (c) 2005  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/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/cpu-type.h>
+#include <asm/irq_regs.h>
+#include <asm/ptrace.h>
+#include <asm/traps.h>
+
+#include <asm/dec/kn02ca.h>
+#include <asm/dec/kn02xa.h>
+#include <asm/dec/kn05.h>
+
+static inline void dec_kn02xa_be_ack(void)
+{
+	volatile u32 *mer = (void *)CKSEG1ADDR(KN02XA_MER);
+	volatile u32 *mem_intr = (void *)CKSEG1ADDR(KN02XA_MEM_INTR);
+
+	*mer = KN02CA_MER_INTR;		/* Clear errors; keep the ARC IRQ. */
+	*mem_intr = 0;			/* Any write clears the bus IRQ. */
+	iob();
+}
+
+static int dec_kn02xa_be_backend(struct pt_regs *regs, int is_fixup,
+				 int invoker)
+{
+	volatile u32 *kn02xa_mer = (void *)CKSEG1ADDR(KN02XA_MER);
+	volatile u32 *kn02xa_ear = (void *)CKSEG1ADDR(KN02XA_EAR);
+
+	static const char excstr[] = "exception";
+	static const char intstr[] = "interrupt";
+	static const char cpustr[] = "CPU";
+	static const char mreadstr[] = "memory read";
+	static const char readstr[] = "read";
+	static const char writestr[] = "write";
+	static const char timestr[] = "timeout";
+	static const char paritystr[] = "parity error";
+	static const char lanestat[][4] = { " OK", "BAD" };
+
+	const char *kind, *agent, *cycle, *event;
+	unsigned long address;
+
+	u32 mer = *kn02xa_mer;
+	u32 ear = *kn02xa_ear;
+	int action = MIPS_BE_FATAL;
+
+	/* Ack ASAP, so that any subsequent errors get caught. */
+	dec_kn02xa_be_ack();
+
+	kind = invoker ? intstr : excstr;
+
+	/* No DMA errors? */
+	agent = cpustr;
+
+	address = ear & KN02XA_EAR_ADDRESS;
+
+	/* Low 256MB is decoded as memory, high -- as TC. */
+	if (address < 0x10000000) {
+		cycle = mreadstr;
+		event = paritystr;
+	} else {
+		cycle = invoker ? writestr : readstr;
+		event = timestr;
+	}
+
+	if (is_fixup)
+		action = MIPS_BE_FIXUP;
+
+	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 && address < 0x10000000)
+		printk(KERN_ALERT "  Byte lane status %#3x -- "
+		       "#3: %s, #2: %s, #1: %s, #0: %s\n",
+		       (mer & KN02XA_MER_BYTERR) >> 8,
+		       lanestat[(mer & KN02XA_MER_BYTERR_3) != 0],
+		       lanestat[(mer & KN02XA_MER_BYTERR_2) != 0],
+		       lanestat[(mer & KN02XA_MER_BYTERR_1) != 0],
+		       lanestat[(mer & KN02XA_MER_BYTERR_0) != 0]);
+
+	return action;
+}
+
+int dec_kn02xa_be_handler(struct pt_regs *regs, int is_fixup)
+{
+	return dec_kn02xa_be_backend(regs, is_fixup, 0);
+}
+
+irqreturn_t dec_kn02xa_be_interrupt(int irq, void *dev_id)
+{
+	struct pt_regs *regs = get_irq_regs();
+	int action = dec_kn02xa_be_backend(regs, 0, 1);
+
+	if (action == MIPS_BE_DISCARD)
+		return IRQ_HANDLED;
+
+	/*
+	 * FIXME: Find the 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);
+}
+
+
+void __init dec_kn02xa_be_init(void)
+{
+	volatile u32 *mbcs = (void *)CKSEG1ADDR(KN4K_SLOT_BASE + KN4K_MB_CSR);
+
+	/* For KN04 we need to make sure EE (?) is enabled in the MB.  */
+	if (current_cpu_type() == CPU_R4000SC)
+		*mbcs |= KN4K_MB_CSR_EE;
+	fast_iob();
+
+	/* Clear any leftover errors from the firmware. */
+	dec_kn02xa_be_ack();
+}
diff --git a/arch/mips/dec/platform.c b/arch/mips/dec/platform.c
new file mode 100644
index 0000000..c7ac86a
--- /dev/null
+++ b/arch/mips/dec/platform.c
@@ -0,0 +1,44 @@
+/*
+ *	DEC platform devices.
+ *
+ *	Copyright (c) 2014  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/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mc146818rtc.h>
+#include <linux/platform_device.h>
+
+static struct resource dec_rtc_resources[] = {
+	{
+		.name = "rtc",
+		.flags = IORESOURCE_MEM,
+	},
+};
+
+static struct cmos_rtc_board_info dec_rtc_info = {
+	.flags = CMOS_RTC_FLAGS_NOFREQ,
+	.address_space = 64,
+};
+
+static struct platform_device dec_rtc_device = {
+	.name = "rtc_cmos",
+	.id = PLATFORM_DEVID_NONE,
+	.dev.platform_data = &dec_rtc_info,
+	.resource = dec_rtc_resources,
+	.num_resources = ARRAY_SIZE(dec_rtc_resources),
+};
+
+static int __init dec_add_devices(void)
+{
+	dec_rtc_resources[0].start = RTC_PORT(0);
+	dec_rtc_resources[0].end = RTC_PORT(0) + dec_kn_slot_size - 1;
+	return platform_device_register(&dec_rtc_device);
+}
+
+device_initcall(dec_add_devices);
diff --git a/arch/mips/dec/prom/Makefile b/arch/mips/dec/prom/Makefile
new file mode 100644
index 0000000..ae73e42
--- /dev/null
+++ b/arch/mips/dec/prom/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for the DECstation prom monitor library routines
+# under Linux.
+#
+
+lib-y			+= init.o memory.o cmdline.o identify.o console.o
+
+lib-$(CONFIG_32BIT)	+= locore.o
diff --git a/arch/mips/dec/prom/cmdline.c b/arch/mips/dec/prom/cmdline.c
new file mode 100644
index 0000000..3ed6328
--- /dev/null
+++ b/arch/mips/dec/prom/cmdline.c
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cmdline.c: read the command line passed to us by the PROM.
+ *
+ * Copyright (C) 1998 Harald Koerfgen
+ * Copyright (C) 2002, 2004  Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/bootinfo.h>
+#include <asm/dec/prom.h>
+
+#undef PROM_DEBUG
+
+void __init prom_init_cmdline(s32 argc, s32 *argv, u32 magic)
+{
+	char *arg;
+	int start_arg, i;
+
+	/*
+	 * collect args and prepare cmd_line
+	 */
+	if (!prom_is_rex(magic))
+		start_arg = 1;
+	else
+		start_arg = 2;
+	for (i = start_arg; i < argc; i++) {
+		arg = (void *)(long)(argv[i]);
+		strcat(arcs_cmdline, arg);
+		if (i < (argc - 1))
+			strcat(arcs_cmdline, " ");
+	}
+
+#ifdef PROM_DEBUG
+	printk("arcs_cmdline: %s\n", &(arcs_cmdline[0]));
+#endif
+}
diff --git a/arch/mips/dec/prom/console.c b/arch/mips/dec/prom/console.c
new file mode 100644
index 0000000..caa6e04
--- /dev/null
+++ b/arch/mips/dec/prom/console.c
@@ -0,0 +1,45 @@
+/*
+ *	DECstation PROM-based early console support.
+ *
+ *	Copyright (C) 2004, 2007  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/console.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/dec/prom.h>
+
+static void __init prom_console_write(struct console *con, const char *s,
+				      unsigned int c)
+{
+	char buf[81];
+	unsigned int chunk = sizeof(buf) - 1;
+
+	while (c > 0) {
+		if (chunk > c)
+			chunk = c;
+		memcpy(buf, s, chunk);
+		buf[chunk] = '\0';
+		prom_printf("%s", buf);
+		s += chunk;
+		c -= chunk;
+	}
+}
+
+static struct console promcons __initdata = {
+	.name	= "prom",
+	.write	= prom_console_write,
+	.flags	= CON_BOOT | CON_PRINTBUFFER,
+	.index	= -1,
+};
+
+void __init register_prom_console(void)
+{
+	register_console(&promcons);
+}
diff --git a/arch/mips/dec/prom/dectypes.h b/arch/mips/dec/prom/dectypes.h
new file mode 100644
index 0000000..9fcbcc7
--- /dev/null
+++ b/arch/mips/dec/prom/dectypes.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef DECTYPES
+#define DECTYPES
+
+#define DS2100_3100	1	/* DS2100/3100	Pmax		*/
+#define DS5000_200	2	/* DS5000/200	3max		*/
+#define DS5000_1XX	3	/* DS5000/1xx	kmin		*/
+#define DS5000_2X0	4	/* DS5000/2x0	3max+		*/
+#define DS5800		5	/* DS5800	Isis		*/
+#define DS5400		6	/* DS5400	MIPSfair	*/
+#define DS5000_XX	7	/* DS5000/xx	maxine		*/
+#define DS5500		11	/* DS5500	MIPSfair-2	*/
+#define DS5100		12	/* DS5100	MIPSmate	*/
+
+#endif
diff --git a/arch/mips/dec/prom/identify.c b/arch/mips/dec/prom/identify.c
new file mode 100644
index 0000000..80cd14c
--- /dev/null
+++ b/arch/mips/dec/prom/identify.c
@@ -0,0 +1,187 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * identify.c: machine identification code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen and Paul M. Antoine
+ * Copyright (C) 2002, 2003, 2004, 2005  Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mc146818rtc.h>
+#include <linux/export.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/bootinfo.h>
+
+#include <asm/dec/ioasic.h>
+#include <asm/dec/ioasic_addrs.h>
+#include <asm/dec/kn01.h>
+#include <asm/dec/kn02.h>
+#include <asm/dec/kn02ba.h>
+#include <asm/dec/kn02ca.h>
+#include <asm/dec/kn03.h>
+#include <asm/dec/kn230.h>
+#include <asm/dec/prom.h>
+#include <asm/dec/system.h>
+
+#include "dectypes.h"
+
+static const char *dec_system_strings[] = {
+	[MACH_DSUNKNOWN]	"unknown DECstation",
+	[MACH_DS23100]		"DECstation 2100/3100",
+	[MACH_DS5100]		"DECsystem 5100",
+	[MACH_DS5000_200]	"DECstation 5000/200",
+	[MACH_DS5000_1XX]	"DECstation 5000/1xx",
+	[MACH_DS5000_XX]	"Personal DECstation 5000/xx",
+	[MACH_DS5000_2X0]	"DECstation 5000/2x0",
+	[MACH_DS5400]		"DECsystem 5400",
+	[MACH_DS5500]		"DECsystem 5500",
+	[MACH_DS5800]		"DECsystem 5800",
+	[MACH_DS5900]		"DECsystem 5900",
+};
+
+const char *get_system_type(void)
+{
+#define STR_BUF_LEN	64
+	static char system[STR_BUF_LEN];
+	static int called = 0;
+
+	if (called == 0) {
+		called = 1;
+		snprintf(system, STR_BUF_LEN, "Digital %s",
+			 dec_system_strings[mips_machtype]);
+	}
+
+	return system;
+}
+
+
+/*
+ * Setup essential system-specific memory addresses.  We need them
+ * early.  Semantically the functions belong to prom/init.c, but they
+ * are compact enough we want them inlined. --macro
+ */
+volatile u8 *dec_rtc_base;
+
+EXPORT_SYMBOL(dec_rtc_base);
+
+static inline void prom_init_kn01(void)
+{
+	dec_kn_slot_base = KN01_SLOT_BASE;
+	dec_kn_slot_size = KN01_SLOT_SIZE;
+
+	dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN01_RTC);
+}
+
+static inline void prom_init_kn230(void)
+{
+	dec_kn_slot_base = KN01_SLOT_BASE;
+	dec_kn_slot_size = KN01_SLOT_SIZE;
+
+	dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN01_RTC);
+}
+
+static inline void prom_init_kn02(void)
+{
+	dec_kn_slot_base = KN02_SLOT_BASE;
+	dec_kn_slot_size = KN02_SLOT_SIZE;
+	dec_tc_bus = 1;
+
+	dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + KN02_RTC);
+}
+
+static inline void prom_init_kn02xa(void)
+{
+	dec_kn_slot_base = KN02XA_SLOT_BASE;
+	dec_kn_slot_size = IOASIC_SLOT_SIZE;
+	dec_tc_bus = 1;
+
+	ioasic_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_IOCTL);
+	dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_TOY);
+}
+
+static inline void prom_init_kn03(void)
+{
+	dec_kn_slot_base = KN03_SLOT_BASE;
+	dec_kn_slot_size = IOASIC_SLOT_SIZE;
+	dec_tc_bus = 1;
+
+	ioasic_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_IOCTL);
+	dec_rtc_base = (void *)CKSEG1ADDR(dec_kn_slot_base + IOASIC_TOY);
+}
+
+
+void __init prom_identify_arch(u32 magic)
+{
+	unsigned char dec_cpunum, dec_firmrev, dec_etc, dec_systype;
+	u32 dec_sysid;
+
+	if (!prom_is_rex(magic)) {
+		dec_sysid = simple_strtoul(prom_getenv("systype"),
+					   (char **)0, 0);
+	} else {
+		dec_sysid = rex_getsysid();
+		if (dec_sysid == 0) {
+			printk("Zero sysid returned from PROM! "
+			       "Assuming a PMAX-like machine.\n");
+			dec_sysid = 1;
+		}
+	}
+
+	dec_cpunum = (dec_sysid & 0xff000000) >> 24;
+	dec_systype = (dec_sysid & 0xff0000) >> 16;
+	dec_firmrev = (dec_sysid & 0xff00) >> 8;
+	dec_etc = dec_sysid & 0xff;
+
+	/*
+	 * FIXME: This may not be an exhaustive list of DECStations/Servers!
+	 * Put all model-specific initialisation calls here.
+	 */
+	switch (dec_systype) {
+	case DS2100_3100:
+		mips_machtype = MACH_DS23100;
+		prom_init_kn01();
+		break;
+	case DS5100:		/* DS5100 MIPSMATE */
+		mips_machtype = MACH_DS5100;
+		prom_init_kn230();
+		break;
+	case DS5000_200:	/* DS5000 3max */
+		mips_machtype = MACH_DS5000_200;
+		prom_init_kn02();
+		break;
+	case DS5000_1XX:	/* DS5000/100 3min */
+		mips_machtype = MACH_DS5000_1XX;
+		prom_init_kn02xa();
+		break;
+	case DS5000_2X0:	/* DS5000/240 3max+ or DS5900 bigmax */
+		mips_machtype = MACH_DS5000_2X0;
+		prom_init_kn03();
+		if (!(ioasic_read(IO_REG_SIR) & KN03_IO_INR_3MAXP))
+			mips_machtype = MACH_DS5900;
+		break;
+	case DS5000_XX:		/* Personal DS5000/xx maxine */
+		mips_machtype = MACH_DS5000_XX;
+		prom_init_kn02xa();
+		break;
+	case DS5800:		/* DS5800 Isis */
+		mips_machtype = MACH_DS5800;
+		break;
+	case DS5400:		/* DS5400 MIPSfair */
+		mips_machtype = MACH_DS5400;
+		break;
+	case DS5500:		/* DS5500 MIPSfair-2 */
+		mips_machtype = MACH_DS5500;
+		break;
+	default:
+		mips_machtype = MACH_DSUNKNOWN;
+		break;
+	}
+
+	if (mips_machtype == MACH_DSUNKNOWN)
+		printk("This is an %s, id is %x\n",
+		       dec_system_strings[mips_machtype], dec_systype);
+	else
+		printk("This is a %s\n", dec_system_strings[mips_machtype]);
+}
diff --git a/arch/mips/dec/prom/init.c b/arch/mips/dec/prom/init.c
new file mode 100644
index 0000000..cc988bb
--- /dev/null
+++ b/arch/mips/dec/prom/init.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * init.c: PROM library initialisation code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen
+ * Copyright (C) 2002, 2004  Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/linkage.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/types.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/cpu-type.h>
+#include <asm/processor.h>
+
+#include <asm/dec/prom.h>
+
+
+int (*__rex_bootinit)(void);
+int (*__rex_bootread)(void);
+int (*__rex_getbitmap)(memmap *);
+unsigned long *(*__rex_slot_address)(int);
+void *(*__rex_gettcinfo)(void);
+int (*__rex_getsysid)(void);
+void (*__rex_clear_cache)(void);
+
+int (*__prom_getchar)(void);
+char *(*__prom_getenv)(char *);
+int (*__prom_printf)(char *, ...);
+
+int (*__pmax_open)(char*, int);
+int (*__pmax_lseek)(int, long, int);
+int (*__pmax_read)(int, void *, int);
+int (*__pmax_close)(int);
+
+
+/*
+ * Detect which PROM the DECSTATION has, and set the callback vectors
+ * appropriately.
+ */
+void __init which_prom(s32 magic, s32 *prom_vec)
+{
+	/*
+	 * No sign of the REX PROM's magic number means we assume a non-REX
+	 * machine (i.e. we're on a DS2100/3100, DS5100 or DS5000/2xx)
+	 */
+	if (prom_is_rex(magic)) {
+		/*
+		 * Set up prom abstraction structure with REX entry points.
+		 */
+		__rex_bootinit =
+			(void *)(long)*(prom_vec + REX_PROM_BOOTINIT);
+		__rex_bootread =
+			(void *)(long)*(prom_vec + REX_PROM_BOOTREAD);
+		__rex_getbitmap =
+			(void *)(long)*(prom_vec + REX_PROM_GETBITMAP);
+		__prom_getchar =
+			(void *)(long)*(prom_vec + REX_PROM_GETCHAR);
+		__prom_getenv =
+			(void *)(long)*(prom_vec + REX_PROM_GETENV);
+		__rex_getsysid =
+			(void *)(long)*(prom_vec + REX_PROM_GETSYSID);
+		__rex_gettcinfo =
+			(void *)(long)*(prom_vec + REX_PROM_GETTCINFO);
+		__prom_printf =
+			(void *)(long)*(prom_vec + REX_PROM_PRINTF);
+		__rex_slot_address =
+			(void *)(long)*(prom_vec + REX_PROM_SLOTADDR);
+		__rex_clear_cache =
+			(void *)(long)*(prom_vec + REX_PROM_CLEARCACHE);
+	} else {
+		/*
+		 * Set up prom abstraction structure with non-REX entry points.
+		 */
+		__prom_getchar = (void *)PMAX_PROM_GETCHAR;
+		__prom_getenv = (void *)PMAX_PROM_GETENV;
+		__prom_printf = (void *)PMAX_PROM_PRINTF;
+		__pmax_open = (void *)PMAX_PROM_OPEN;
+		__pmax_lseek = (void *)PMAX_PROM_LSEEK;
+		__pmax_read = (void *)PMAX_PROM_READ;
+		__pmax_close = (void *)PMAX_PROM_CLOSE;
+	}
+}
+
+void __init prom_init(void)
+{
+	extern void dec_machine_halt(void);
+	static const char cpu_msg[] __initconst =
+		"Sorry, this kernel is compiled for a wrong CPU type!\n";
+	s32 argc = fw_arg0;
+	s32 *argv = (void *)fw_arg1;
+	u32 magic = fw_arg2;
+	s32 *prom_vec = (void *)fw_arg3;
+
+	/*
+	 * Determine which PROM we have
+	 * (and therefore which machine we're on!)
+	 */
+	which_prom(magic, prom_vec);
+
+	if (prom_is_rex(magic))
+		rex_clear_cache();
+
+	/* Register the early console.  */
+	register_prom_console();
+
+	/* Were we compiled with the right CPU option? */
+#if defined(CONFIG_CPU_R3000)
+	if ((current_cpu_type() == CPU_R4000SC) ||
+	    (current_cpu_type() == CPU_R4400SC)) {
+		static const char r4k_msg[] __initconst =
+			"Please recompile with \"CONFIG_CPU_R4x00 = y\".\n";
+		printk(cpu_msg);
+		printk(r4k_msg);
+		dec_machine_halt();
+	}
+#endif
+
+#if defined(CONFIG_CPU_R4X00)
+	if ((current_cpu_type() == CPU_R3000) ||
+	    (current_cpu_type() == CPU_R3000A)) {
+		static const char r3k_msg[] __initconst =
+			"Please recompile with \"CONFIG_CPU_R3000 = y\".\n";
+		printk(cpu_msg);
+		printk(r3k_msg);
+		dec_machine_halt();
+	}
+#endif
+
+	prom_meminit(magic);
+	prom_identify_arch(magic);
+	prom_init_cmdline(argc, argv, magic);
+}
diff --git a/arch/mips/dec/prom/locore.S b/arch/mips/dec/prom/locore.S
new file mode 100644
index 0000000..0eb8fab
--- /dev/null
+++ b/arch/mips/dec/prom/locore.S
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * locore.S
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+
+	.text
+
+/*
+ * Simple general exception handling routine. This one is used for the
+ * Memory sizing routine for pmax machines. HK
+ */
+
+NESTED(genexcept_early, 0, sp)
+	.set	noat
+	.set	noreorder
+
+	mfc0	k0, CP0_STATUS
+	la	k1, mem_err
+
+	sw	k0, 0(k1)
+
+	mfc0	k0, CP0_EPC
+	nop
+	addiu	k0, 4		# skip the causing instruction
+	jr	k0
+	 rfe
+END(genexcept_early)
diff --git a/arch/mips/dec/prom/memory.c b/arch/mips/dec/prom/memory.c
new file mode 100644
index 0000000..a2acc64
--- /dev/null
+++ b/arch/mips/dec/prom/memory.c
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * memory.c: memory initialisation code.
+ *
+ * Copyright (C) 1998 Harald Koerfgen, Frieder Streffer and Paul M. Antoine
+ * Copyright (C) 2000, 2002  Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/bootmem.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/dec/machtype.h>
+#include <asm/dec/prom.h>
+#include <asm/page.h>
+#include <asm/sections.h>
+
+
+volatile unsigned long mem_err;		/* So we know an error occurred */
+
+/*
+ * Probe memory in 4MB chunks, waiting for an error to tell us we've fallen
+ * off the end of real memory.  Only suitable for the 2100/3100's (PMAX).
+ */
+
+#define CHUNK_SIZE 0x400000
+
+static inline void pmax_setup_memory_region(void)
+{
+	volatile unsigned char *memory_page, dummy;
+	char old_handler[0x80];
+	extern char genexcept_early;
+
+	/* Install exception handler */
+	memcpy(&old_handler, (void *)(CKSEG0 + 0x80), 0x80);
+	memcpy((void *)(CKSEG0 + 0x80), &genexcept_early, 0x80);
+
+	/* read unmapped and uncached (KSEG1)
+	 * DECstations have at least 4MB RAM
+	 * Assume less than 480MB of RAM, as this is max for 5000/2xx
+	 * FIXME this should be replaced by the first free page!
+	 */
+	for (memory_page = (unsigned char *)CKSEG1 + CHUNK_SIZE;
+	     mem_err == 0 && memory_page < (unsigned char *)CKSEG1 + 0x1e00000;
+	     memory_page += CHUNK_SIZE) {
+		dummy = *memory_page;
+	}
+	memcpy((void *)(CKSEG0 + 0x80), &old_handler, 0x80);
+
+	add_memory_region(0, (unsigned long)memory_page - CKSEG1 - CHUNK_SIZE,
+			  BOOT_MEM_RAM);
+}
+
+/*
+ * Use the REX prom calls to get hold of the memory bitmap, and thence
+ * determine memory size.
+ */
+static inline void rex_setup_memory_region(void)
+{
+	int i, bitmap_size;
+	unsigned long mem_start = 0, mem_size = 0;
+	memmap *bm;
+
+	/* some free 64k */
+	bm = (memmap *)CKSEG0ADDR(0x28000);
+
+	bitmap_size = rex_getbitmap(bm);
+
+	for (i = 0; i < bitmap_size; i++) {
+		/* FIXME: very simplistically only add full sets of pages */
+		if (bm->bitmap[i] == 0xff)
+			mem_size += (8 * bm->pagesize);
+		else if (!mem_size)
+			mem_start += (8 * bm->pagesize);
+		else {
+			add_memory_region(mem_start, mem_size, BOOT_MEM_RAM);
+			mem_start += mem_size + (8 * bm->pagesize);
+			mem_size = 0;
+		}
+	}
+	if (mem_size)
+		add_memory_region(mem_start, mem_size, BOOT_MEM_RAM);
+}
+
+void __init prom_meminit(u32 magic)
+{
+	if (!prom_is_rex(magic))
+		pmax_setup_memory_region();
+	else
+		rex_setup_memory_region();
+}
+
+void __init prom_free_prom_memory(void)
+{
+	unsigned long end;
+
+	/*
+	 * Free everything below the kernel itself but leave
+	 * the first page reserved for the exception handlers.
+	 */
+
+#if IS_ENABLED(CONFIG_DECLANCE)
+	/*
+	 * Leave 128 KB reserved for Lance memory for
+	 * IOASIC DECstations.
+	 *
+	 * XXX: save this address for use in dec_lance.c?
+	 */
+	if (IOASIC)
+		end = __pa(&_text) - 0x00020000;
+	else
+#endif
+		end = __pa(&_text);
+
+	free_init_pages("unused PROM memory", PAGE_SIZE, end);
+}
diff --git a/arch/mips/dec/reset.c b/arch/mips/dec/reset.c
new file mode 100644
index 0000000..3df01f1
--- /dev/null
+++ b/arch/mips/dec/reset.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Reset a DECstation machine.
+ *
+ * Copyright (C) 199x  the Anonymous
+ * Copyright (C) 2001, 2002, 2003  Maciej W. Rozycki
+ */
+#include <linux/interrupt.h>
+#include <linux/linkage.h>
+
+#include <asm/addrspace.h>
+
+typedef void __noreturn (* noret_func_t)(void);
+
+static inline void __noreturn back_to_prom(void)
+{
+	noret_func_t func = (void *)CKSEG1ADDR(0x1fc00000);
+
+	func();
+}
+
+void __noreturn dec_machine_restart(char *command)
+{
+	back_to_prom();
+}
+
+void __noreturn dec_machine_halt(void)
+{
+	back_to_prom();
+}
+
+void __noreturn dec_machine_power_off(void)
+{
+    /* DECstations don't have a software power switch */
+	back_to_prom();
+}
+
+irqreturn_t dec_intr_halt(int irq, void *dev_id)
+{
+	dec_machine_halt();
+}
diff --git a/arch/mips/dec/setup.c b/arch/mips/dec/setup.c
new file mode 100644
index 0000000..61a0bf1
--- /dev/null
+++ b/arch/mips/dec/setup.c
@@ -0,0 +1,787 @@
+/*
+ * System-specific setup, especially interrupts.
+ *
+ * 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) 1998 Harald Koerfgen
+ * Copyright (C) 2000, 2001, 2002, 2003, 2005  Maciej W. Rozycki
+ */
+#include <linux/console.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/irqnr.h>
+#include <linux/param.h>
+#include <linux/percpu-defs.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/types.h>
+#include <linux/pm.h>
+
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/cpu-features.h>
+#include <asm/cpu-type.h>
+#include <asm/irq.h>
+#include <asm/irq_cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/traps.h>
+#include <asm/wbflush.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic.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/kn02ba.h>
+#include <asm/dec/kn02ca.h>
+#include <asm/dec/kn03.h>
+#include <asm/dec/kn230.h>
+#include <asm/dec/system.h>
+
+
+extern void dec_machine_restart(char *command);
+extern void dec_machine_halt(void);
+extern void dec_machine_power_off(void);
+extern irqreturn_t dec_intr_halt(int irq, void *dev_id);
+
+unsigned long dec_kn_slot_base, dec_kn_slot_size;
+
+EXPORT_SYMBOL(dec_kn_slot_base);
+EXPORT_SYMBOL(dec_kn_slot_size);
+
+int dec_tc_bus;
+
+DEFINE_SPINLOCK(ioasic_ssr_lock);
+EXPORT_SYMBOL(ioasic_ssr_lock);
+
+volatile u32 *ioasic_base;
+
+EXPORT_SYMBOL(ioasic_base);
+
+/*
+ * IRQ routing and priority tables.  Priorites are set as follows:
+ *
+ *		KN01	KN230	KN02	KN02-BA	KN02-CA	KN03
+ *
+ * MEMORY	CPU	CPU	CPU	ASIC	CPU	CPU
+ * RTC		CPU	CPU	CPU	ASIC	CPU	CPU
+ * DMA		-	-	-	ASIC	ASIC	ASIC
+ * SERIAL0	CPU	CPU	CSR	ASIC	ASIC	ASIC
+ * SERIAL1	-	-	-	ASIC	-	ASIC
+ * SCSI		CPU	CPU	CSR	ASIC	ASIC	ASIC
+ * ETHERNET	CPU	*	CSR	ASIC	ASIC	ASIC
+ * other	-	-	-	ASIC	-	-
+ * TC2		-	-	CSR	CPU	ASIC	ASIC
+ * TC1		-	-	CSR	CPU	ASIC	ASIC
+ * TC0		-	-	CSR	CPU	ASIC	ASIC
+ * other	-	CPU	-	CPU	ASIC	ASIC
+ * other	-	-	-	-	CPU	CPU
+ *
+ * * -- shared with SCSI
+ */
+
+int dec_interrupt[DEC_NR_INTS] = {
+	[0 ... DEC_NR_INTS - 1] = -1
+};
+
+EXPORT_SYMBOL(dec_interrupt);
+
+int_ptr cpu_mask_nr_tbl[DEC_MAX_CPU_INTS][2] = {
+	{ { .i = ~0 }, { .p = dec_intr_unimplemented } },
+};
+int_ptr asic_mask_nr_tbl[DEC_MAX_ASIC_INTS][2] = {
+	{ { .i = ~0 }, { .p = asic_intr_unimplemented } },
+};
+int cpu_fpu_mask = DEC_CPU_IRQ_MASK(DEC_CPU_INR_FPU);
+int *fpu_kstat_irq;
+
+static struct irqaction ioirq = {
+	.handler = no_action,
+	.name = "cascade",
+	.flags = IRQF_NO_THREAD,
+};
+static struct irqaction fpuirq = {
+	.handler = no_action,
+	.name = "fpu",
+	.flags = IRQF_NO_THREAD,
+};
+
+static struct irqaction busirq = {
+	.name = "bus error",
+	.flags = IRQF_NO_THREAD,
+};
+
+static struct irqaction haltirq = {
+	.handler = dec_intr_halt,
+	.name = "halt",
+	.flags = IRQF_NO_THREAD,
+};
+
+
+/*
+ * Bus error (DBE/IBE exceptions and bus interrupts) handling setup.
+ */
+static void __init dec_be_init(void)
+{
+	switch (mips_machtype) {
+	case MACH_DS23100:	/* DS2100/DS3100 Pmin/Pmax */
+		board_be_handler = dec_kn01_be_handler;
+		busirq.handler = dec_kn01_be_interrupt;
+		busirq.flags |= IRQF_SHARED;
+		dec_kn01_be_init();
+		break;
+	case MACH_DS5000_1XX:	/* DS5000/1xx 3min */
+	case MACH_DS5000_XX:	/* DS5000/xx Maxine */
+		board_be_handler = dec_kn02xa_be_handler;
+		busirq.handler = dec_kn02xa_be_interrupt;
+		dec_kn02xa_be_init();
+		break;
+	case MACH_DS5000_200:	/* DS5000/200 3max */
+	case MACH_DS5000_2X0:	/* DS5000/240 3max+ */
+	case MACH_DS5900:	/* DS5900 bigmax */
+		board_be_handler = dec_ecc_be_handler;
+		busirq.handler = dec_ecc_be_interrupt;
+		dec_ecc_be_init();
+		break;
+	}
+}
+
+void __init plat_mem_setup(void)
+{
+	board_be_init = dec_be_init;
+
+	wbflush_setup();
+
+	_machine_restart = dec_machine_restart;
+	_machine_halt = dec_machine_halt;
+	pm_power_off = dec_machine_power_off;
+
+	ioport_resource.start = ~0UL;
+	ioport_resource.end = 0UL;
+}
+
+/*
+ * Machine-specific initialisation for KN01, aka DS2100 (aka Pmin)
+ * or DS3100 (aka Pmax).
+ */
+static int kn01_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= -1,
+	[DEC_IRQ_AB_RECV]	= -1,
+	[DEC_IRQ_AB_XMIT]	= -1,
+	[DEC_IRQ_DZ11]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_DZ11),
+	[DEC_IRQ_ASC]		= -1,
+	[DEC_IRQ_FLOPPY]	= -1,
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= -1,
+	[DEC_IRQ_ISDN]		= -1,
+	[DEC_IRQ_LANCE]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_LANCE),
+	[DEC_IRQ_BUS]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_BUS),
+	[DEC_IRQ_PSU]		= -1,
+	[DEC_IRQ_RTC]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_RTC),
+	[DEC_IRQ_SCC0]		= -1,
+	[DEC_IRQ_SCC1]		= -1,
+	[DEC_IRQ_SII]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_SII),
+	[DEC_IRQ_TC0]		= -1,
+	[DEC_IRQ_TC1]		= -1,
+	[DEC_IRQ_TC2]		= -1,
+	[DEC_IRQ_TIMER]		= -1,
+	[DEC_IRQ_VIDEO]		= DEC_CPU_IRQ_NR(KN01_CPU_INR_VIDEO),
+	[DEC_IRQ_ASC_MERR]	= -1,
+	[DEC_IRQ_ASC_ERR]	= -1,
+	[DEC_IRQ_ASC_DMA]	= -1,
+	[DEC_IRQ_FLOPPY_ERR]	= -1,
+	[DEC_IRQ_ISDN_ERR]	= -1,
+	[DEC_IRQ_ISDN_RXDMA]	= -1,
+	[DEC_IRQ_ISDN_TXDMA]	= -1,
+	[DEC_IRQ_LANCE_MERR]	= -1,
+	[DEC_IRQ_SCC0A_RXERR]	= -1,
+	[DEC_IRQ_SCC0A_RXDMA]	= -1,
+	[DEC_IRQ_SCC0A_TXERR]	= -1,
+	[DEC_IRQ_SCC0A_TXDMA]	= -1,
+	[DEC_IRQ_AB_RXERR]	= -1,
+	[DEC_IRQ_AB_RXDMA]	= -1,
+	[DEC_IRQ_AB_TXERR]	= -1,
+	[DEC_IRQ_AB_TXDMA]	= -1,
+	[DEC_IRQ_SCC1A_RXERR]	= -1,
+	[DEC_IRQ_SCC1A_RXDMA]	= -1,
+	[DEC_IRQ_SCC1A_TXERR]	= -1,
+	[DEC_IRQ_SCC1A_TXDMA]	= -1,
+};
+
+static int_ptr kn01_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_BUS) },
+		{ .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_BUS) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_RTC) },
+		{ .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_RTC) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_DZ11) },
+		{ .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_DZ11) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_SII) },
+		{ .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_SII) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN01_CPU_INR_LANCE) },
+		{ .i = DEC_CPU_IRQ_NR(KN01_CPU_INR_LANCE) } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static void __init dec_init_kn01(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn01_interrupt,
+		sizeof(kn01_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn01_cpu_mask_nr_tbl,
+		sizeof(kn01_cpu_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+
+}				/* dec_init_kn01 */
+
+
+/*
+ * Machine-specific initialisation for KN230, aka DS5100, aka MIPSmate.
+ */
+static int kn230_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= -1,
+	[DEC_IRQ_AB_RECV]	= -1,
+	[DEC_IRQ_AB_XMIT]	= -1,
+	[DEC_IRQ_DZ11]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_DZ11),
+	[DEC_IRQ_ASC]		= -1,
+	[DEC_IRQ_FLOPPY]	= -1,
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_HALT),
+	[DEC_IRQ_ISDN]		= -1,
+	[DEC_IRQ_LANCE]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_LANCE),
+	[DEC_IRQ_BUS]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_BUS),
+	[DEC_IRQ_PSU]		= -1,
+	[DEC_IRQ_RTC]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_RTC),
+	[DEC_IRQ_SCC0]		= -1,
+	[DEC_IRQ_SCC1]		= -1,
+	[DEC_IRQ_SII]		= DEC_CPU_IRQ_NR(KN230_CPU_INR_SII),
+	[DEC_IRQ_TC0]		= -1,
+	[DEC_IRQ_TC1]		= -1,
+	[DEC_IRQ_TC2]		= -1,
+	[DEC_IRQ_TIMER]		= -1,
+	[DEC_IRQ_VIDEO]		= -1,
+	[DEC_IRQ_ASC_MERR]	= -1,
+	[DEC_IRQ_ASC_ERR]	= -1,
+	[DEC_IRQ_ASC_DMA]	= -1,
+	[DEC_IRQ_FLOPPY_ERR]	= -1,
+	[DEC_IRQ_ISDN_ERR]	= -1,
+	[DEC_IRQ_ISDN_RXDMA]	= -1,
+	[DEC_IRQ_ISDN_TXDMA]	= -1,
+	[DEC_IRQ_LANCE_MERR]	= -1,
+	[DEC_IRQ_SCC0A_RXERR]	= -1,
+	[DEC_IRQ_SCC0A_RXDMA]	= -1,
+	[DEC_IRQ_SCC0A_TXERR]	= -1,
+	[DEC_IRQ_SCC0A_TXDMA]	= -1,
+	[DEC_IRQ_AB_RXERR]	= -1,
+	[DEC_IRQ_AB_RXDMA]	= -1,
+	[DEC_IRQ_AB_TXERR]	= -1,
+	[DEC_IRQ_AB_TXDMA]	= -1,
+	[DEC_IRQ_SCC1A_RXERR]	= -1,
+	[DEC_IRQ_SCC1A_RXDMA]	= -1,
+	[DEC_IRQ_SCC1A_TXERR]	= -1,
+	[DEC_IRQ_SCC1A_TXDMA]	= -1,
+};
+
+static int_ptr kn230_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_BUS) },
+		{ .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_BUS) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_RTC) },
+		{ .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_RTC) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_DZ11) },
+		{ .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_DZ11) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN230_CPU_INR_SII) },
+		{ .i = DEC_CPU_IRQ_NR(KN230_CPU_INR_SII) } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static void __init dec_init_kn230(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn230_interrupt,
+		sizeof(kn230_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn230_cpu_mask_nr_tbl,
+		sizeof(kn230_cpu_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+
+}				/* dec_init_kn230 */
+
+
+/*
+ * Machine-specific initialisation for KN02, aka DS5000/200, aka 3max.
+ */
+static int kn02_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= DEC_CPU_IRQ_NR(KN02_CPU_INR_CASCADE),
+	[DEC_IRQ_AB_RECV]	= -1,
+	[DEC_IRQ_AB_XMIT]	= -1,
+	[DEC_IRQ_DZ11]		= KN02_IRQ_NR(KN02_CSR_INR_DZ11),
+	[DEC_IRQ_ASC]		= KN02_IRQ_NR(KN02_CSR_INR_ASC),
+	[DEC_IRQ_FLOPPY]	= -1,
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= -1,
+	[DEC_IRQ_ISDN]		= -1,
+	[DEC_IRQ_LANCE]		= KN02_IRQ_NR(KN02_CSR_INR_LANCE),
+	[DEC_IRQ_BUS]		= DEC_CPU_IRQ_NR(KN02_CPU_INR_BUS),
+	[DEC_IRQ_PSU]		= -1,
+	[DEC_IRQ_RTC]		= DEC_CPU_IRQ_NR(KN02_CPU_INR_RTC),
+	[DEC_IRQ_SCC0]		= -1,
+	[DEC_IRQ_SCC1]		= -1,
+	[DEC_IRQ_SII]		= -1,
+	[DEC_IRQ_TC0]		= KN02_IRQ_NR(KN02_CSR_INR_TC0),
+	[DEC_IRQ_TC1]		= KN02_IRQ_NR(KN02_CSR_INR_TC1),
+	[DEC_IRQ_TC2]		= KN02_IRQ_NR(KN02_CSR_INR_TC2),
+	[DEC_IRQ_TIMER]		= -1,
+	[DEC_IRQ_VIDEO]		= -1,
+	[DEC_IRQ_ASC_MERR]	= -1,
+	[DEC_IRQ_ASC_ERR]	= -1,
+	[DEC_IRQ_ASC_DMA]	= -1,
+	[DEC_IRQ_FLOPPY_ERR]	= -1,
+	[DEC_IRQ_ISDN_ERR]	= -1,
+	[DEC_IRQ_ISDN_RXDMA]	= -1,
+	[DEC_IRQ_ISDN_TXDMA]	= -1,
+	[DEC_IRQ_LANCE_MERR]	= -1,
+	[DEC_IRQ_SCC0A_RXERR]	= -1,
+	[DEC_IRQ_SCC0A_RXDMA]	= -1,
+	[DEC_IRQ_SCC0A_TXERR]	= -1,
+	[DEC_IRQ_SCC0A_TXDMA]	= -1,
+	[DEC_IRQ_AB_RXERR]	= -1,
+	[DEC_IRQ_AB_RXDMA]	= -1,
+	[DEC_IRQ_AB_TXERR]	= -1,
+	[DEC_IRQ_AB_TXDMA]	= -1,
+	[DEC_IRQ_SCC1A_RXERR]	= -1,
+	[DEC_IRQ_SCC1A_RXDMA]	= -1,
+	[DEC_IRQ_SCC1A_TXERR]	= -1,
+	[DEC_IRQ_SCC1A_TXDMA]	= -1,
+};
+
+static int_ptr kn02_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_BUS) },
+		{ .i = DEC_CPU_IRQ_NR(KN02_CPU_INR_BUS) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_RTC) },
+		{ .i = DEC_CPU_IRQ_NR(KN02_CPU_INR_RTC) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02_CPU_INR_CASCADE) },
+		{ .p = kn02_io_int } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static int_ptr kn02_asic_mask_nr_tbl[][2] __initdata = {
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_DZ11) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_DZ11) } },
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_ASC) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_ASC) } },
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_LANCE) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_LANCE) } },
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC2) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_TC2) } },
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC1) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_TC1) } },
+	{ { .i = KN02_IRQ_MASK(KN02_CSR_INR_TC0) },
+		{ .i = KN02_IRQ_NR(KN02_CSR_INR_TC0) } },
+	{ { .i = KN02_IRQ_ALL },
+		{ .p = kn02_all_int } },
+};
+
+static void __init dec_init_kn02(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn02_interrupt,
+		sizeof(kn02_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn02_cpu_mask_nr_tbl,
+		sizeof(kn02_cpu_mask_nr_tbl));
+
+	/* KN02 CSR IRQ priorities. */
+	memcpy(&asic_mask_nr_tbl, &kn02_asic_mask_nr_tbl,
+		sizeof(kn02_asic_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+	init_kn02_irqs(KN02_IRQ_BASE);
+
+}				/* dec_init_kn02 */
+
+
+/*
+ * Machine-specific initialisation for KN02-BA, aka DS5000/1xx
+ * (xx = 20, 25, 33), aka 3min.  Also applies to KN04(-BA), aka
+ * DS5000/150, aka 4min.
+ */
+static int kn02ba_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= DEC_CPU_IRQ_NR(KN02BA_CPU_INR_CASCADE),
+	[DEC_IRQ_AB_RECV]	= -1,
+	[DEC_IRQ_AB_XMIT]	= -1,
+	[DEC_IRQ_DZ11]		= -1,
+	[DEC_IRQ_ASC]		= IO_IRQ_NR(KN02BA_IO_INR_ASC),
+	[DEC_IRQ_FLOPPY]	= -1,
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= DEC_CPU_IRQ_NR(KN02BA_CPU_INR_HALT),
+	[DEC_IRQ_ISDN]		= -1,
+	[DEC_IRQ_LANCE]		= IO_IRQ_NR(KN02BA_IO_INR_LANCE),
+	[DEC_IRQ_BUS]		= IO_IRQ_NR(KN02BA_IO_INR_BUS),
+	[DEC_IRQ_PSU]		= IO_IRQ_NR(KN02BA_IO_INR_PSU),
+	[DEC_IRQ_RTC]		= IO_IRQ_NR(KN02BA_IO_INR_RTC),
+	[DEC_IRQ_SCC0]		= IO_IRQ_NR(KN02BA_IO_INR_SCC0),
+	[DEC_IRQ_SCC1]		= IO_IRQ_NR(KN02BA_IO_INR_SCC1),
+	[DEC_IRQ_SII]		= -1,
+	[DEC_IRQ_TC0]		= DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC0),
+	[DEC_IRQ_TC1]		= DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC1),
+	[DEC_IRQ_TC2]		= DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC2),
+	[DEC_IRQ_TIMER]		= -1,
+	[DEC_IRQ_VIDEO]		= -1,
+	[DEC_IRQ_ASC_MERR]	= IO_IRQ_NR(IO_INR_ASC_MERR),
+	[DEC_IRQ_ASC_ERR]	= IO_IRQ_NR(IO_INR_ASC_ERR),
+	[DEC_IRQ_ASC_DMA]	= IO_IRQ_NR(IO_INR_ASC_DMA),
+	[DEC_IRQ_FLOPPY_ERR]	= -1,
+	[DEC_IRQ_ISDN_ERR]	= -1,
+	[DEC_IRQ_ISDN_RXDMA]	= -1,
+	[DEC_IRQ_ISDN_TXDMA]	= -1,
+	[DEC_IRQ_LANCE_MERR]	= IO_IRQ_NR(IO_INR_LANCE_MERR),
+	[DEC_IRQ_SCC0A_RXERR]	= IO_IRQ_NR(IO_INR_SCC0A_RXERR),
+	[DEC_IRQ_SCC0A_RXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_RXDMA),
+	[DEC_IRQ_SCC0A_TXERR]	= IO_IRQ_NR(IO_INR_SCC0A_TXERR),
+	[DEC_IRQ_SCC0A_TXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_TXDMA),
+	[DEC_IRQ_AB_RXERR]	= -1,
+	[DEC_IRQ_AB_RXDMA]	= -1,
+	[DEC_IRQ_AB_TXERR]	= -1,
+	[DEC_IRQ_AB_TXDMA]	= -1,
+	[DEC_IRQ_SCC1A_RXERR]	= IO_IRQ_NR(IO_INR_SCC1A_RXERR),
+	[DEC_IRQ_SCC1A_RXDMA]	= IO_IRQ_NR(IO_INR_SCC1A_RXDMA),
+	[DEC_IRQ_SCC1A_TXERR]	= IO_IRQ_NR(IO_INR_SCC1A_TXERR),
+	[DEC_IRQ_SCC1A_TXDMA]	= IO_IRQ_NR(IO_INR_SCC1A_TXDMA),
+};
+
+static int_ptr kn02ba_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_CASCADE) },
+		{ .p = kn02xa_io_int } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC2) },
+		{ .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC2) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC1) },
+		{ .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC1) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02BA_CPU_INR_TC0) },
+		{ .i = DEC_CPU_IRQ_NR(KN02BA_CPU_INR_TC0) } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static int_ptr kn02ba_asic_mask_nr_tbl[][2] __initdata = {
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_BUS) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_BUS) } },
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_RTC) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_RTC) } },
+	{ { .i = IO_IRQ_DMA },
+		{ .p = asic_dma_int } },
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_SCC0) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_SCC0) } },
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_SCC1) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_SCC1) } },
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_ASC) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_ASC) } },
+	{ { .i = IO_IRQ_MASK(KN02BA_IO_INR_LANCE) },
+		{ .i = IO_IRQ_NR(KN02BA_IO_INR_LANCE) } },
+	{ { .i = IO_IRQ_ALL },
+		{ .p = asic_all_int } },
+};
+
+static void __init dec_init_kn02ba(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn02ba_interrupt,
+		sizeof(kn02ba_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn02ba_cpu_mask_nr_tbl,
+		sizeof(kn02ba_cpu_mask_nr_tbl));
+
+	/* I/O ASIC IRQ priorities. */
+	memcpy(&asic_mask_nr_tbl, &kn02ba_asic_mask_nr_tbl,
+		sizeof(kn02ba_asic_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+	init_ioasic_irqs(IO_IRQ_BASE);
+
+}				/* dec_init_kn02ba */
+
+
+/*
+ * Machine-specific initialisation for KN02-CA, aka DS5000/xx,
+ * (xx = 20, 25, 33), aka MAXine.  Also applies to KN04(-CA), aka
+ * DS5000/50, aka 4MAXine.
+ */
+static int kn02ca_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= DEC_CPU_IRQ_NR(KN02CA_CPU_INR_CASCADE),
+	[DEC_IRQ_AB_RECV]	= IO_IRQ_NR(KN02CA_IO_INR_AB_RECV),
+	[DEC_IRQ_AB_XMIT]	= IO_IRQ_NR(KN02CA_IO_INR_AB_XMIT),
+	[DEC_IRQ_DZ11]		= -1,
+	[DEC_IRQ_ASC]		= IO_IRQ_NR(KN02CA_IO_INR_ASC),
+	[DEC_IRQ_FLOPPY]	= IO_IRQ_NR(KN02CA_IO_INR_FLOPPY),
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= DEC_CPU_IRQ_NR(KN02CA_CPU_INR_HALT),
+	[DEC_IRQ_ISDN]		= IO_IRQ_NR(KN02CA_IO_INR_ISDN),
+	[DEC_IRQ_LANCE]		= IO_IRQ_NR(KN02CA_IO_INR_LANCE),
+	[DEC_IRQ_BUS]		= DEC_CPU_IRQ_NR(KN02CA_CPU_INR_BUS),
+	[DEC_IRQ_PSU]		= -1,
+	[DEC_IRQ_RTC]		= DEC_CPU_IRQ_NR(KN02CA_CPU_INR_RTC),
+	[DEC_IRQ_SCC0]		= IO_IRQ_NR(KN02CA_IO_INR_SCC0),
+	[DEC_IRQ_SCC1]		= -1,
+	[DEC_IRQ_SII]		= -1,
+	[DEC_IRQ_TC0]		= IO_IRQ_NR(KN02CA_IO_INR_TC0),
+	[DEC_IRQ_TC1]		= IO_IRQ_NR(KN02CA_IO_INR_TC1),
+	[DEC_IRQ_TC2]		= -1,
+	[DEC_IRQ_TIMER]		= DEC_CPU_IRQ_NR(KN02CA_CPU_INR_TIMER),
+	[DEC_IRQ_VIDEO]		= IO_IRQ_NR(KN02CA_IO_INR_VIDEO),
+	[DEC_IRQ_ASC_MERR]	= IO_IRQ_NR(IO_INR_ASC_MERR),
+	[DEC_IRQ_ASC_ERR]	= IO_IRQ_NR(IO_INR_ASC_ERR),
+	[DEC_IRQ_ASC_DMA]	= IO_IRQ_NR(IO_INR_ASC_DMA),
+	[DEC_IRQ_FLOPPY_ERR]	= IO_IRQ_NR(IO_INR_FLOPPY_ERR),
+	[DEC_IRQ_ISDN_ERR]	= IO_IRQ_NR(IO_INR_ISDN_ERR),
+	[DEC_IRQ_ISDN_RXDMA]	= IO_IRQ_NR(IO_INR_ISDN_RXDMA),
+	[DEC_IRQ_ISDN_TXDMA]	= IO_IRQ_NR(IO_INR_ISDN_TXDMA),
+	[DEC_IRQ_LANCE_MERR]	= IO_IRQ_NR(IO_INR_LANCE_MERR),
+	[DEC_IRQ_SCC0A_RXERR]	= IO_IRQ_NR(IO_INR_SCC0A_RXERR),
+	[DEC_IRQ_SCC0A_RXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_RXDMA),
+	[DEC_IRQ_SCC0A_TXERR]	= IO_IRQ_NR(IO_INR_SCC0A_TXERR),
+	[DEC_IRQ_SCC0A_TXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_TXDMA),
+	[DEC_IRQ_AB_RXERR]	= IO_IRQ_NR(IO_INR_AB_RXERR),
+	[DEC_IRQ_AB_RXDMA]	= IO_IRQ_NR(IO_INR_AB_RXDMA),
+	[DEC_IRQ_AB_TXERR]	= IO_IRQ_NR(IO_INR_AB_TXERR),
+	[DEC_IRQ_AB_TXDMA]	= IO_IRQ_NR(IO_INR_AB_TXDMA),
+	[DEC_IRQ_SCC1A_RXERR]	= -1,
+	[DEC_IRQ_SCC1A_RXDMA]	= -1,
+	[DEC_IRQ_SCC1A_TXERR]	= -1,
+	[DEC_IRQ_SCC1A_TXDMA]	= -1,
+};
+
+static int_ptr kn02ca_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_BUS) },
+		{ .i = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_BUS) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_RTC) },
+		{ .i = DEC_CPU_IRQ_NR(KN02CA_CPU_INR_RTC) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN02CA_CPU_INR_CASCADE) },
+		{ .p = kn02xa_io_int } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static int_ptr kn02ca_asic_mask_nr_tbl[][2] __initdata = {
+	{ { .i = IO_IRQ_DMA },
+		{ .p = asic_dma_int } },
+	{ { .i = IO_IRQ_MASK(KN02CA_IO_INR_SCC0) },
+		{ .i = IO_IRQ_NR(KN02CA_IO_INR_SCC0) } },
+	{ { .i = IO_IRQ_MASK(KN02CA_IO_INR_ASC) },
+		{ .i = IO_IRQ_NR(KN02CA_IO_INR_ASC) } },
+	{ { .i = IO_IRQ_MASK(KN02CA_IO_INR_LANCE) },
+		{ .i = IO_IRQ_NR(KN02CA_IO_INR_LANCE) } },
+	{ { .i = IO_IRQ_MASK(KN02CA_IO_INR_TC1) },
+		{ .i = IO_IRQ_NR(KN02CA_IO_INR_TC1) } },
+	{ { .i = IO_IRQ_MASK(KN02CA_IO_INR_TC0) },
+		{ .i = IO_IRQ_NR(KN02CA_IO_INR_TC0) } },
+	{ { .i = IO_IRQ_ALL },
+		{ .p = asic_all_int } },
+};
+
+static void __init dec_init_kn02ca(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn02ca_interrupt,
+		sizeof(kn02ca_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn02ca_cpu_mask_nr_tbl,
+		sizeof(kn02ca_cpu_mask_nr_tbl));
+
+	/* I/O ASIC IRQ priorities. */
+	memcpy(&asic_mask_nr_tbl, &kn02ca_asic_mask_nr_tbl,
+		sizeof(kn02ca_asic_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+	init_ioasic_irqs(IO_IRQ_BASE);
+
+}				/* dec_init_kn02ca */
+
+
+/*
+ * Machine-specific initialisation for KN03, aka DS5000/240,
+ * aka 3max+ and DS5900, aka BIGmax.  Also applies to KN05, aka
+ * DS5000/260, aka 4max+ and DS5900/260.
+ */
+static int kn03_interrupt[DEC_NR_INTS] __initdata = {
+	[DEC_IRQ_CASCADE]	= DEC_CPU_IRQ_NR(KN03_CPU_INR_CASCADE),
+	[DEC_IRQ_AB_RECV]	= -1,
+	[DEC_IRQ_AB_XMIT]	= -1,
+	[DEC_IRQ_DZ11]		= -1,
+	[DEC_IRQ_ASC]		= IO_IRQ_NR(KN03_IO_INR_ASC),
+	[DEC_IRQ_FLOPPY]	= -1,
+	[DEC_IRQ_FPU]		= DEC_CPU_IRQ_NR(DEC_CPU_INR_FPU),
+	[DEC_IRQ_HALT]		= DEC_CPU_IRQ_NR(KN03_CPU_INR_HALT),
+	[DEC_IRQ_ISDN]		= -1,
+	[DEC_IRQ_LANCE]		= IO_IRQ_NR(KN03_IO_INR_LANCE),
+	[DEC_IRQ_BUS]		= DEC_CPU_IRQ_NR(KN03_CPU_INR_BUS),
+	[DEC_IRQ_PSU]		= IO_IRQ_NR(KN03_IO_INR_PSU),
+	[DEC_IRQ_RTC]		= DEC_CPU_IRQ_NR(KN03_CPU_INR_RTC),
+	[DEC_IRQ_SCC0]		= IO_IRQ_NR(KN03_IO_INR_SCC0),
+	[DEC_IRQ_SCC1]		= IO_IRQ_NR(KN03_IO_INR_SCC1),
+	[DEC_IRQ_SII]		= -1,
+	[DEC_IRQ_TC0]		= IO_IRQ_NR(KN03_IO_INR_TC0),
+	[DEC_IRQ_TC1]		= IO_IRQ_NR(KN03_IO_INR_TC1),
+	[DEC_IRQ_TC2]		= IO_IRQ_NR(KN03_IO_INR_TC2),
+	[DEC_IRQ_TIMER]		= -1,
+	[DEC_IRQ_VIDEO]		= -1,
+	[DEC_IRQ_ASC_MERR]	= IO_IRQ_NR(IO_INR_ASC_MERR),
+	[DEC_IRQ_ASC_ERR]	= IO_IRQ_NR(IO_INR_ASC_ERR),
+	[DEC_IRQ_ASC_DMA]	= IO_IRQ_NR(IO_INR_ASC_DMA),
+	[DEC_IRQ_FLOPPY_ERR]	= -1,
+	[DEC_IRQ_ISDN_ERR]	= -1,
+	[DEC_IRQ_ISDN_RXDMA]	= -1,
+	[DEC_IRQ_ISDN_TXDMA]	= -1,
+	[DEC_IRQ_LANCE_MERR]	= IO_IRQ_NR(IO_INR_LANCE_MERR),
+	[DEC_IRQ_SCC0A_RXERR]	= IO_IRQ_NR(IO_INR_SCC0A_RXERR),
+	[DEC_IRQ_SCC0A_RXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_RXDMA),
+	[DEC_IRQ_SCC0A_TXERR]	= IO_IRQ_NR(IO_INR_SCC0A_TXERR),
+	[DEC_IRQ_SCC0A_TXDMA]	= IO_IRQ_NR(IO_INR_SCC0A_TXDMA),
+	[DEC_IRQ_AB_RXERR]	= -1,
+	[DEC_IRQ_AB_RXDMA]	= -1,
+	[DEC_IRQ_AB_TXERR]	= -1,
+	[DEC_IRQ_AB_TXDMA]	= -1,
+	[DEC_IRQ_SCC1A_RXERR]	= IO_IRQ_NR(IO_INR_SCC1A_RXERR),
+	[DEC_IRQ_SCC1A_RXDMA]	= IO_IRQ_NR(IO_INR_SCC1A_RXDMA),
+	[DEC_IRQ_SCC1A_TXERR]	= IO_IRQ_NR(IO_INR_SCC1A_TXERR),
+	[DEC_IRQ_SCC1A_TXDMA]	= IO_IRQ_NR(IO_INR_SCC1A_TXDMA),
+};
+
+static int_ptr kn03_cpu_mask_nr_tbl[][2] __initdata = {
+	{ { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_BUS) },
+		{ .i = DEC_CPU_IRQ_NR(KN03_CPU_INR_BUS) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_RTC) },
+		{ .i = DEC_CPU_IRQ_NR(KN03_CPU_INR_RTC) } },
+	{ { .i = DEC_CPU_IRQ_MASK(KN03_CPU_INR_CASCADE) },
+		{ .p = kn03_io_int } },
+	{ { .i = DEC_CPU_IRQ_ALL },
+		{ .p = cpu_all_int } },
+};
+
+static int_ptr kn03_asic_mask_nr_tbl[][2] __initdata = {
+	{ { .i = IO_IRQ_DMA },
+		{ .p = asic_dma_int } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_SCC0) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_SCC0) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_SCC1) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_SCC1) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_ASC) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_ASC) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_LANCE) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_LANCE) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_TC2) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_TC2) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_TC1) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_TC1) } },
+	{ { .i = IO_IRQ_MASK(KN03_IO_INR_TC0) },
+		{ .i = IO_IRQ_NR(KN03_IO_INR_TC0) } },
+	{ { .i = IO_IRQ_ALL },
+		{ .p = asic_all_int } },
+};
+
+static void __init dec_init_kn03(void)
+{
+	/* IRQ routing. */
+	memcpy(&dec_interrupt, &kn03_interrupt,
+		sizeof(kn03_interrupt));
+
+	/* CPU IRQ priorities. */
+	memcpy(&cpu_mask_nr_tbl, &kn03_cpu_mask_nr_tbl,
+		sizeof(kn03_cpu_mask_nr_tbl));
+
+	/* I/O ASIC IRQ priorities. */
+	memcpy(&asic_mask_nr_tbl, &kn03_asic_mask_nr_tbl,
+		sizeof(kn03_asic_mask_nr_tbl));
+
+	mips_cpu_irq_init();
+	init_ioasic_irqs(IO_IRQ_BASE);
+
+}				/* dec_init_kn03 */
+
+
+void __init arch_init_irq(void)
+{
+	switch (mips_machtype) {
+	case MACH_DS23100:	/* DS2100/DS3100 Pmin/Pmax */
+		dec_init_kn01();
+		break;
+	case MACH_DS5100:	/* DS5100 MIPSmate */
+		dec_init_kn230();
+		break;
+	case MACH_DS5000_200:	/* DS5000/200 3max */
+		dec_init_kn02();
+		break;
+	case MACH_DS5000_1XX:	/* DS5000/1xx 3min */
+		dec_init_kn02ba();
+		break;
+	case MACH_DS5000_2X0:	/* DS5000/240 3max+ */
+	case MACH_DS5900:	/* DS5900 bigmax */
+		dec_init_kn03();
+		break;
+	case MACH_DS5000_XX:	/* Personal DS5000/xx */
+		dec_init_kn02ca();
+		break;
+	case MACH_DS5800:	/* DS5800 Isis */
+		panic("Don't know how to set this up!");
+		break;
+	case MACH_DS5400:	/* DS5400 MIPSfair */
+		panic("Don't know how to set this up!");
+		break;
+	case MACH_DS5500:	/* DS5500 MIPSfair-2 */
+		panic("Don't know how to set this up!");
+		break;
+	}
+
+	/* Free the FPU interrupt if the exception is present. */
+	if (!cpu_has_nofpuex) {
+		cpu_fpu_mask = 0;
+		dec_interrupt[DEC_IRQ_FPU] = -1;
+	}
+	/* Free the halt interrupt unused on R4k systems.  */
+	if (current_cpu_type() == CPU_R4000SC ||
+	    current_cpu_type() == CPU_R4400SC)
+		dec_interrupt[DEC_IRQ_HALT] = -1;
+
+	/* Register board interrupts: FPU and cascade. */
+	if (dec_interrupt[DEC_IRQ_FPU] >= 0 && cpu_has_fpu) {
+		struct irq_desc *desc_fpu;
+		int irq_fpu;
+
+		irq_fpu = dec_interrupt[DEC_IRQ_FPU];
+		setup_irq(irq_fpu, &fpuirq);
+		desc_fpu = irq_to_desc(irq_fpu);
+		fpu_kstat_irq = this_cpu_ptr(desc_fpu->kstat_irqs);
+	}
+	if (dec_interrupt[DEC_IRQ_CASCADE] >= 0)
+		setup_irq(dec_interrupt[DEC_IRQ_CASCADE], &ioirq);
+
+	/* Register the bus error interrupt. */
+	if (dec_interrupt[DEC_IRQ_BUS] >= 0 && busirq.handler)
+		setup_irq(dec_interrupt[DEC_IRQ_BUS], &busirq);
+
+	/* Register the HALT interrupt. */
+	if (dec_interrupt[DEC_IRQ_HALT] >= 0)
+		setup_irq(dec_interrupt[DEC_IRQ_HALT], &haltirq);
+}
+
+asmlinkage unsigned int dec_irq_dispatch(unsigned int irq)
+{
+	do_IRQ(irq);
+	return 0;
+}
diff --git a/arch/mips/dec/tc.c b/arch/mips/dec/tc.c
new file mode 100644
index 0000000..732027c
--- /dev/null
+++ b/arch/mips/dec/tc.c
@@ -0,0 +1,95 @@
+/*
+ *	TURBOchannel architecture calls.
+ *
+ *	Copyright (c) Harald Koerfgen, 1998
+ *	Copyright (c) 2001, 2003, 2005, 2006  Maciej W. Rozycki
+ *	Copyright (c) 2005  James Simmons
+ *
+ *	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.
+ */
+#include <linux/compiler.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/tc.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/paccess.h>
+
+#include <asm/dec/interrupts.h>
+#include <asm/dec/prom.h>
+#include <asm/dec/system.h>
+
+/*
+ * Protected read byte from TURBOchannel slot space.
+ */
+int tc_preadb(u8 *valp, void __iomem *addr)
+{
+	return get_dbe(*valp, (u8 *)addr);
+}
+
+/*
+ * Get TURBOchannel bus information as specified by the spec, plus
+ * the slot space base address and the number of slots.
+ */
+int __init tc_bus_get_info(struct tc_bus *tbus)
+{
+	if (!dec_tc_bus)
+		return -ENXIO;
+
+	memcpy(&tbus->info, rex_gettcinfo(), sizeof(tbus->info));
+	tbus->slot_base = CPHYSADDR((long)rex_slot_address(0));
+
+	switch (mips_machtype) {
+	case MACH_DS5000_200:
+		tbus->num_tcslots = 7;
+		break;
+	case MACH_DS5000_2X0:
+	case MACH_DS5900:
+		tbus->ext_slot_base = 0x20000000;
+		tbus->ext_slot_size = 0x20000000;
+		/* fall through */
+	case MACH_DS5000_1XX:
+		tbus->num_tcslots = 3;
+		break;
+	case MACH_DS5000_XX:
+		tbus->num_tcslots = 2;
+	default:
+		break;
+	}
+	return 0;
+}
+
+/*
+ * Get the IRQ for the specified slot.
+ */
+void __init tc_device_get_irq(struct tc_dev *tdev)
+{
+	switch (tdev->slot) {
+	case 0:
+		tdev->interrupt = dec_interrupt[DEC_IRQ_TC0];
+		break;
+	case 1:
+		tdev->interrupt = dec_interrupt[DEC_IRQ_TC1];
+		break;
+	case 2:
+		tdev->interrupt = dec_interrupt[DEC_IRQ_TC2];
+		break;
+	/*
+	 * Yuck! DS5000/200 onboard devices
+	 */
+	case 5:
+		tdev->interrupt = dec_interrupt[DEC_IRQ_TC5];
+		break;
+	case 6:
+		tdev->interrupt = dec_interrupt[DEC_IRQ_TC6];
+		break;
+	default:
+		tdev->interrupt = -1;
+		break;
+	}
+}
diff --git a/arch/mips/dec/time.c b/arch/mips/dec/time.c
new file mode 100644
index 0000000..c38686f
--- /dev/null
+++ b/arch/mips/dec/time.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
+ *  Copyright (C) 2000, 2003  Maciej W. Rozycki
+ *
+ * This file contains the time handling details for PC-style clocks as
+ * found in some MIPS systems.
+ *
+ */
+#include <linux/bcd.h>
+#include <linux/init.h>
+#include <linux/mc146818rtc.h>
+#include <linux/param.h>
+
+#include <asm/cpu-features.h>
+#include <asm/ds1287.h>
+#include <asm/time.h>
+#include <asm/dec/interrupts.h>
+#include <asm/dec/ioasic.h>
+#include <asm/dec/machtype.h>
+
+void read_persistent_clock64(struct timespec64 *ts)
+{
+	unsigned int year, mon, day, hour, min, sec, real_year;
+	unsigned long flags;
+
+	spin_lock_irqsave(&rtc_lock, flags);
+
+	do {
+		sec = CMOS_READ(RTC_SECONDS);
+		min = CMOS_READ(RTC_MINUTES);
+		hour = CMOS_READ(RTC_HOURS);
+		day = CMOS_READ(RTC_DAY_OF_MONTH);
+		mon = CMOS_READ(RTC_MONTH);
+		year = CMOS_READ(RTC_YEAR);
+		/*
+		 * The PROM will reset the year to either '72 or '73.
+		 * Therefore we store the real year separately, in one
+		 * of unused BBU RAM locations.
+		 */
+		real_year = CMOS_READ(RTC_DEC_YEAR);
+	} while (sec != CMOS_READ(RTC_SECONDS));
+
+	spin_unlock_irqrestore(&rtc_lock, flags);
+
+	if (!(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+		sec = bcd2bin(sec);
+		min = bcd2bin(min);
+		hour = bcd2bin(hour);
+		day = bcd2bin(day);
+		mon = bcd2bin(mon);
+		year = bcd2bin(year);
+	}
+
+	year += real_year - 72 + 2000;
+
+	ts->tv_sec = mktime64(year, mon, day, hour, min, sec);
+	ts->tv_nsec = 0;
+}
+
+/*
+ * In order to set the CMOS clock precisely, update_persistent_clock64 has to
+ * be called 500 ms after the second nowtime has started, because when
+ * nowtime is written into the registers of the CMOS clock, it will
+ * jump to the next second precisely 500 ms later.  Check the Dallas
+ * DS1287 data sheet for details.
+ */
+int update_persistent_clock64(struct timespec64 now)
+{
+	time64_t nowtime = now.tv_sec;
+	int retval = 0;
+	int real_seconds, real_minutes, cmos_minutes;
+	unsigned char save_control, save_freq_select;
+
+	/* irq are locally disabled here */
+	spin_lock(&rtc_lock);
+	/* tell the clock it's being set */
+	save_control = CMOS_READ(RTC_CONTROL);
+	CMOS_WRITE((save_control | RTC_SET), RTC_CONTROL);
+
+	/* stop and reset prescaler */
+	save_freq_select = CMOS_READ(RTC_FREQ_SELECT);
+	CMOS_WRITE((save_freq_select | RTC_DIV_RESET2), RTC_FREQ_SELECT);
+
+	cmos_minutes = CMOS_READ(RTC_MINUTES);
+	if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD)
+		cmos_minutes = bcd2bin(cmos_minutes);
+
+	/*
+	 * since we're only adjusting minutes and seconds,
+	 * don't interfere with hour overflow. This avoids
+	 * messing with unknown time zones but requires your
+	 * RTC not to be off by more than 15 minutes
+	 */
+	real_minutes = div_s64_rem(nowtime, 60, &real_seconds);
+	if (((abs(real_minutes - cmos_minutes) + 15) / 30) & 1)
+		real_minutes += 30;	/* correct for half hour time zone */
+	real_minutes %= 60;
+
+	if (abs(real_minutes - cmos_minutes) < 30) {
+		if (!(save_control & RTC_DM_BINARY) || RTC_ALWAYS_BCD) {
+			real_seconds = bin2bcd(real_seconds);
+			real_minutes = bin2bcd(real_minutes);
+		}
+		CMOS_WRITE(real_seconds, RTC_SECONDS);
+		CMOS_WRITE(real_minutes, RTC_MINUTES);
+	} else {
+		printk_once(KERN_NOTICE
+		       "set_rtc_mmss: can't update from %d to %d\n",
+		       cmos_minutes, real_minutes);
+		retval = -1;
+	}
+
+	/* The following flags have to be released exactly in this order,
+	 * otherwise the DS1287 will not reset the oscillator and will not
+	 * update precisely 500 ms later.  You won't find this mentioned
+	 * in the Dallas Semiconductor data sheets, but who believes data
+	 * sheets anyway ...                           -- Markus Kuhn
+	 */
+	CMOS_WRITE(save_control, RTC_CONTROL);
+	CMOS_WRITE(save_freq_select, RTC_FREQ_SELECT);
+	spin_unlock(&rtc_lock);
+
+	return retval;
+}
+
+void __init plat_time_init(void)
+{
+	int ioasic_clock = 0;
+	u32 start, end;
+	int i = HZ / 8;
+
+	/* Set up the rate of periodic DS1287 interrupts. */
+	ds1287_set_base_clock(HZ);
+
+	/* On some I/O ASIC systems we have the I/O ASIC's counter.  */
+	if (IOASIC)
+		ioasic_clock = dec_ioasic_clocksource_init() == 0;
+	if (cpu_has_counter) {
+		ds1287_timer_state();
+		while (!ds1287_timer_state())
+			;
+
+		start = read_c0_count();
+
+		while (i--)
+			while (!ds1287_timer_state())
+				;
+
+		end = read_c0_count();
+
+		mips_hpt_frequency = (end - start) * 8;
+		printk(KERN_INFO "MIPS counter frequency %dHz\n",
+			mips_hpt_frequency);
+
+		/*
+		 * All R4k DECstations suffer from the CP0 Count erratum,
+		 * so we can't use the timer as a clock source, and a clock
+		 * event both at a time.  An accurate wall clock is more
+		 * important than a high-precision interval timer so only
+		 * use the timer as a clock source, and not a clock event
+		 * if there's no I/O ASIC counter available to serve as a
+		 * clock source.
+		 */
+		if (!ioasic_clock) {
+			init_r4k_clocksource();
+			mips_hpt_frequency = 0;
+		}
+	}
+
+	ds1287_clockevent_init(dec_interrupt[DEC_IRQ_RTC]);
+}
diff --git a/arch/mips/dec/wbflush.c b/arch/mips/dec/wbflush.c
new file mode 100644
index 0000000..dad64d1
--- /dev/null
+++ b/arch/mips/dec/wbflush.c
@@ -0,0 +1,92 @@
+/*
+ * Setup the right wbflush routine for the different DECstations.
+ *
+ * Created with information from:
+ *	DECstation 3100 Desktop Workstation Functional Specification
+ *	DECstation 5000/200 KN02 System Module Functional Specification
+ *	mipsel-linux-objdump --disassemble vmunix | grep "wbflush" :-)
+ *
+ * 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) 1998 Harald Koerfgen
+ * Copyright (C) 2002 Maciej W. Rozycki
+ */
+
+#include <linux/export.h>
+#include <linux/init.h>
+
+#include <asm/bootinfo.h>
+#include <asm/wbflush.h>
+#include <asm/barrier.h>
+
+static void wbflush_kn01(void);
+static void wbflush_kn210(void);
+static void wbflush_mips(void);
+
+void (*__wbflush) (void);
+
+void __init wbflush_setup(void)
+{
+	switch (mips_machtype) {
+	case MACH_DS23100:
+	case MACH_DS5000_200:	/* DS5000 3max */
+		__wbflush = wbflush_kn01;
+		break;
+	case MACH_DS5100:	/* DS5100 MIPSMATE */
+		__wbflush = wbflush_kn210;
+		break;
+	case MACH_DS5000_1XX:	/* DS5000/100 3min */
+	case MACH_DS5000_XX:	/* Personal DS5000/2x */
+	case MACH_DS5000_2X0:	/* DS5000/240 3max+ */
+	case MACH_DS5900:	/* DS5900 bigmax */
+	default:
+		__wbflush = wbflush_mips;
+		break;
+	}
+}
+
+/*
+ * For the DS3100 and DS5000/200 the R2020/R3220 writeback buffer functions
+ * as part of Coprocessor 0.
+ */
+static void wbflush_kn01(void)
+{
+    asm(".set\tpush\n\t"
+	".set\tnoreorder\n\t"
+	"1:\tbc0f\t1b\n\t"
+	"nop\n\t"
+	".set\tpop");
+}
+
+/*
+ * For the DS5100 the writeback buffer seems to be a part of Coprocessor 3.
+ * But CP3 has to enabled first.
+ */
+static void wbflush_kn210(void)
+{
+    asm(".set\tpush\n\t"
+	".set\tnoreorder\n\t"
+	"mfc0\t$2,$12\n\t"
+	"lui\t$3,0x8000\n\t"
+	"or\t$3,$2,$3\n\t"
+	"mtc0\t$3,$12\n\t"
+	"nop\n"
+	"1:\tbc3f\t1b\n\t"
+	"nop\n\t"
+	"mtc0\t$2,$12\n\t"
+	"nop\n\t"
+	".set\tpop"
+	: : : "$2", "$3");
+}
+
+/*
+ * I/O ASIC systems use a standard writeback buffer that gets flushed
+ * upon an uncached read.
+ */
+static void wbflush_mips(void)
+{
+	__fast_iob();
+}
+EXPORT_SYMBOL(__wbflush);