[MIPS] RM: Collected changes

- EISA support for non PCI RMs (RM200 and RM400-xxx). The major part
  is the splitting of the EISA and onboard ISA of the RM200, which
  makes the EISA bus on the RM200 look like on other RMs.
- 64bit kernel support
- system type detection is now common for big and little endian
- moved sniprom code to arch/mips/fw
- added call_o32 function to arch/mips/fw/lib, which uses a private
  stack for calling prom functions
- fix problem with ISA interrupts, which makes using PIT clockevent
  possible

Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 01740ef..d2d9cd8 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -602,6 +602,7 @@
 	bool "SNI RM200/300/400"
 	select ARC if CPU_LITTLE_ENDIAN
 	select ARC32 if CPU_LITTLE_ENDIAN
+	select SNIPROM if CPU_BIG_ENDIAN
 	select ARCH_MAY_HAVE_PC_FDC
 	select BOOT_ELF32
 	select CEVT_R4K
@@ -1003,6 +1004,9 @@
 config ARC32
 	bool
 
+config SNIPROM
+	bool
+
 config BOOT_ELF32
 	bool
 
diff --git a/arch/mips/Makefile b/arch/mips/Makefile
index dd668f3..2df68cd 100644
--- a/arch/mips/Makefile
+++ b/arch/mips/Makefile
@@ -156,6 +156,8 @@
 #
 libs-$(CONFIG_ARC)		+= arch/mips/fw/arc/
 libs-$(CONFIG_CFE)		+= arch/mips/fw/cfe/
+libs-$(CONFIG_SNIPROM)		+= arch/mips/fw/sni/
+libs-y				+= arch/mips/fw/lib/
 libs-$(CONFIG_SIBYTE_CFE)	+= arch/mips/sibyte/cfe/
 
 #
diff --git a/arch/mips/fw/lib/Makefile b/arch/mips/fw/lib/Makefile
new file mode 100644
index 0000000..84befc9
--- /dev/null
+++ b/arch/mips/fw/lib/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for generic prom monitor library routines under Linux.
+#
+
+lib-$(CONFIG_64BIT)	+= call_o32.o
diff --git a/arch/mips/fw/lib/call_o32.S b/arch/mips/fw/lib/call_o32.S
new file mode 100644
index 0000000..bdf7d1d
--- /dev/null
+++ b/arch/mips/fw/lib/call_o32.S
@@ -0,0 +1,97 @@
+/*
+ *	arch/mips/dec/prom/call_o32.S
+ *
+ *	O32 interface for the 64 (or N32) ABI.
+ *
+ *	Copyright (C) 2002  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 <asm/asm.h>
+#include <asm/regdef.h>
+
+/* Maximum number of arguments supported.  Must be even!  */
+#define O32_ARGC	32
+/* Number of static registers we save.  */
+#define O32_STATC	11
+/* Frame size for static register  */
+#define O32_FRAMESZ	(SZREG * O32_STATC)
+/* Frame size on new stack */
+#define O32_FRAMESZ_NEW (SZREG + 4 * O32_ARGC)
+
+		.text
+
+/*
+ * O32 function call dispatcher, for interfacing 32-bit ROM routines.
+ *
+ * The standard 64 (N32) calling sequence is supported, with a0
+ * holding a function pointer, a1 a new stack pointer, a2-a7 -- its
+ * first six arguments and the stack -- remaining ones (up to O32_ARGC,
+ * including a2-a7). Static registers, gp and fp are preserved, v0 holds
+ * a result. This code relies on the called o32 function for sp and ra
+ * restoration and this dispatcher has to be placed in a KSEGx (or KUSEG)
+ * address space.  Any pointers passed have to point to addresses within
+ * one of these spaces as well.
+ */
+NESTED(call_o32, O32_FRAMESZ, ra)
+		REG_SUBU	sp,O32_FRAMESZ
+
+		REG_S		ra,O32_FRAMESZ-1*SZREG(sp)
+		REG_S		fp,O32_FRAMESZ-2*SZREG(sp)
+		REG_S		gp,O32_FRAMESZ-3*SZREG(sp)
+		REG_S		s7,O32_FRAMESZ-4*SZREG(sp)
+		REG_S		s6,O32_FRAMESZ-5*SZREG(sp)
+		REG_S		s5,O32_FRAMESZ-6*SZREG(sp)
+		REG_S		s4,O32_FRAMESZ-7*SZREG(sp)
+		REG_S		s3,O32_FRAMESZ-8*SZREG(sp)
+		REG_S		s2,O32_FRAMESZ-9*SZREG(sp)
+		REG_S		s1,O32_FRAMESZ-10*SZREG(sp)
+		REG_S		s0,O32_FRAMESZ-11*SZREG(sp)
+
+		move		jp,a0
+		REG_SUBU	s0,a1,O32_FRAMESZ_NEW
+		REG_S		sp,O32_FRAMESZ_NEW-1*SZREG(s0)
+
+		sll		a0,a2,zero
+		sll		a1,a3,zero
+		sll		a2,a4,zero
+		sll		a3,a5,zero
+		sw		a6,0x10(s0)
+		sw		a7,0x14(s0)
+
+		PTR_LA		t0,O32_FRAMESZ(sp)
+		PTR_LA		t1,0x18(s0)
+		li		t2,O32_ARGC-6
+1:
+		lw		t3,(t0)
+		REG_ADDU	t0,SZREG
+		sw		t3,(t1)
+		REG_SUBU	t2,1
+		REG_ADDU	t1,4
+		bnez		t2,1b
+
+		move		sp,s0
+
+		jalr		jp
+
+		REG_L		sp,O32_FRAMESZ_NEW-1*SZREG(sp)
+
+		REG_L		s0,O32_FRAMESZ-11*SZREG(sp)
+		REG_L		s1,O32_FRAMESZ-10*SZREG(sp)
+		REG_L		s2,O32_FRAMESZ-9*SZREG(sp)
+		REG_L		s3,O32_FRAMESZ-8*SZREG(sp)
+		REG_L		s4,O32_FRAMESZ-7*SZREG(sp)
+		REG_L		s5,O32_FRAMESZ-6*SZREG(sp)
+		REG_L		s6,O32_FRAMESZ-5*SZREG(sp)
+		REG_L		s7,O32_FRAMESZ-4*SZREG(sp)
+		REG_L		gp,O32_FRAMESZ-3*SZREG(sp)
+		REG_L		fp,O32_FRAMESZ-2*SZREG(sp)
+		REG_L		ra,O32_FRAMESZ-1*SZREG(sp)
+
+		REG_ADDU	sp,O32_FRAMESZ
+		jr		ra
+END(call_o32)
diff --git a/arch/mips/fw/sni/Makefile b/arch/mips/fw/sni/Makefile
new file mode 100644
index 0000000..d9740a3
--- /dev/null
+++ b/arch/mips/fw/sni/Makefile
@@ -0,0 +1,5 @@
+#
+# Makefile for the SNI prom monitor routines under Linux.
+#
+
+lib-$(CONFIG_SNIPROM)	+= sniprom.o
diff --git a/arch/mips/fw/sni/sniprom.c b/arch/mips/fw/sni/sniprom.c
new file mode 100644
index 0000000..96ba992
--- /dev/null
+++ b/arch/mips/fw/sni/sniprom.c
@@ -0,0 +1,151 @@
+/*
+ * Big Endian PROM code for SNI RM machines
+ *
+ * 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) 2005-2006 Florian Lohoff (flo@rfc822.org)
+ * Copyright (C) 2005-2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+#include <linux/console.h>
+
+#include <asm/addrspace.h>
+#include <asm/sni.h>
+#include <asm/mipsprom.h>
+#include <asm/mipsregs.h>
+#include <asm/bootinfo.h>
+
+/* special SNI prom calls */
+/*
+ * This does not exist in all proms - SINIX compares
+ * the prom env variable "version" against "2.0008"
+ * or greater. If lesser it tries to probe interesting
+ * registers
+ */
+#define PROM_GET_MEMCONF	58
+#define PROM_GET_HWCONF         61
+
+#define PROM_VEC		(u64 *)CKSEG1ADDR(0x1fc00000)
+#define PROM_ENTRY(x)		(PROM_VEC + (x))
+
+#define ___prom_putchar         ((int *(*)(int))PROM_ENTRY(PROM_PUTCHAR))
+#define ___prom_getenv          ((char *(*)(char *))PROM_ENTRY(PROM_GETENV))
+#define ___prom_get_memconf     ((void (*)(void *))PROM_ENTRY(PROM_GET_MEMCONF))
+#define ___prom_get_hwconf      ((u32 (*)(void))PROM_ENTRY(PROM_GET_HWCONF))
+
+#ifdef CONFIG_64BIT
+
+static u8 o32_stk[16384];
+#define O32_STK   &o32_stk[sizeof(o32_stk)]
+
+#define __PROM_O32(fun, arg) fun arg __asm__(#fun); \
+				     __asm__(#fun " = call_o32")
+
+int   __PROM_O32(__prom_putchar, (int *(*)(int), void *, int));
+char *__PROM_O32(__prom_getenv, (char *(*)(char *), void *, char *));
+void  __PROM_O32(__prom_get_memconf, (void (*)(void *), void *, void *));
+u32   __PROM_O32(__prom_get_hwconf, (u32 (*)(void), void *));
+
+#define _prom_putchar(x)     __prom_putchar(___prom_putchar, O32_STK, x)
+#define _prom_getenv(x)      __prom_getenv(___prom_getenv, O32_STK, x)
+#define _prom_get_memconf(x) __prom_get_memconf(___prom_get_memconf, O32_STK, x)
+#define _prom_get_hwconf()   __prom_get_hwconf(___prom_get_hwconf, O32_STK)
+
+#else
+#define _prom_putchar(x)     ___prom_putchar(x)
+#define _prom_getenv(x)      ___prom_getenv(x)
+#define _prom_get_memconf(x) ___prom_get_memconf(x)
+#define _prom_get_hwconf(x)  ___prom_get_hwconf(x)
+#endif
+
+void prom_putchar(char c)
+{
+	_prom_putchar(c);
+}
+
+
+char *prom_getenv(char *s)
+{
+	return _prom_getenv(s);
+}
+
+void *prom_get_hwconf(void)
+{
+	u32 hwconf = _prom_get_hwconf();
+
+	if (hwconf == 0xffffffff)
+		return NULL;
+
+	return (void *)CKSEG1ADDR(hwconf);
+}
+
+void __init prom_free_prom_memory(void)
+{
+}
+
+/*
+ * /proc/cpuinfo system type
+ *
+ */
+char *system_type = "Unknown";
+const char *get_system_type(void)
+{
+	return system_type;
+}
+
+static void __init sni_mem_init(void)
+{
+	int i, memsize;
+	struct membank {
+		u32		size;
+		u32		base;
+		u32		size2;
+		u32		pad1;
+		u32		pad2;
+	} memconf[8];
+	int brd_type = *(unsigned char *)SNI_IDPROM_BRDTYPE;
+
+
+	/* MemSIZE from prom in 16MByte chunks */
+	memsize = *((unsigned char *) SNI_IDPROM_MEMSIZE) * 16;
+
+	pr_debug("IDProm memsize: %u MByte\n", memsize);
+
+	/* get memory bank layout from prom */
+	_prom_get_memconf(&memconf);
+
+	pr_debug("prom_get_mem_conf memory configuration:\n");
+	for (i = 0; i < 8 && memconf[i].size; i++) {
+		if (brd_type == SNI_BRD_PCI_TOWER ||
+		    brd_type == SNI_BRD_PCI_TOWER_CPLUS) {
+			if (memconf[i].base >= 0x20000000 &&
+			    memconf[i].base <  0x30000000)
+				memconf[i].base -= 0x20000000;
+		}
+		pr_debug("Bank%d: %08x @ %08x\n", i,
+			memconf[i].size, memconf[i].base);
+		add_memory_region(memconf[i].base, memconf[i].size,
+				  BOOT_MEM_RAM);
+	}
+}
+
+void __init prom_init(void)
+{
+	int argc = fw_arg0;
+	u32 *argv = (u32 *)CKSEG0ADDR(fw_arg1);
+	int i;
+
+	sni_mem_init();
+
+	/* copy prom cmdline parameters to kernel cmdline */
+	for (i = 1; i < argc; i++) {
+		strcat(arcs_cmdline, (char *)CKSEG0ADDR(argv[i]));
+		if (i < (argc - 1))
+			strcat(arcs_cmdline, " ");
+	}
+}
diff --git a/arch/mips/sni/Makefile b/arch/mips/sni/Makefile
index 3a99cd6..a7dbeeb 100644
--- a/arch/mips/sni/Makefile
+++ b/arch/mips/sni/Makefile
@@ -3,6 +3,6 @@
 #
 
 obj-y += irq.o reset.o setup.o a20r.o rm200.o pcimt.o pcit.o time.o
-obj-$(CONFIG_CPU_BIG_ENDIAN) += sniprom.o
+obj-$(CONFIG_EISA) += eisa.o
 
 EXTRA_CFLAGS += -Werror
diff --git a/arch/mips/sni/a20r.c b/arch/mips/sni/a20r.c
index b746075..3f8cf5e 100644
--- a/arch/mips/sni/a20r.c
+++ b/arch/mips/sni/a20r.c
@@ -117,10 +117,19 @@
 	}
 };
 
+static unsigned int sc26xx_data[2] = {
+	/* DTR   |   RTS    |   DSR    |   CTS     |   DCD     |   RI    */
+	(8 << 0) | (4 << 4) | (6 << 8) | (0 << 12) | (6 << 16) | (0 << 20),
+	(3 << 0) | (2 << 4) | (1 << 8) | (2 << 12) | (3 << 16) | (4 << 20)
+};
+
 static struct platform_device sc26xx_pdev = {
 	.name           = "SC26xx",
 	.num_resources  = ARRAY_SIZE(sc26xx_rsrc),
-	.resource       = sc26xx_rsrc
+	.resource       = sc26xx_rsrc,
+	.dev			= {
+		.platform_data	= sc26xx_data,
+	}
 };
 
 static u32 a20r_ack_hwint(void)
@@ -231,9 +240,9 @@
 	        platform_device_register(&sc26xx_pdev);
 	        platform_device_register(&a20r_serial8250_device);
 	        platform_device_register(&a20r_ds1216_device);
+		sni_eisa_root_init();
 	        break;
 	}
-
 	return 0;
 }
 
diff --git a/arch/mips/sni/eisa.c b/arch/mips/sni/eisa.c
new file mode 100644
index 0000000..7396cd7
--- /dev/null
+++ b/arch/mips/sni/eisa.c
@@ -0,0 +1,50 @@
+/*
+ * Virtual EISA root driver.
+ * Acts as a placeholder if we don't have a proper EISA bridge.
+ *
+ * (C) 2003 Marc Zyngier <maz@wild-wind.fr.eu.org>
+ * modified for SNI usage by Thomas Bogendoerfer
+ *
+ * This code is released under the GPL version 2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/eisa.h>
+#include <linux/init.h>
+
+/* The default EISA device parent (virtual root device).
+ * Now use a platform device, since that's the obvious choice. */
+
+static struct platform_device eisa_root_dev = {
+	.name = "eisa",
+	.id   = 0,
+};
+
+static struct eisa_root_device eisa_bus_root = {
+	.dev           = &eisa_root_dev.dev,
+	.bus_base_addr = 0,
+	.res	       = &ioport_resource,
+	.slots	       = EISA_MAX_SLOTS,
+	.dma_mask      = 0xffffffff,
+	.force_probe   = 1,
+};
+
+int __init sni_eisa_root_init(void)
+{
+	int r;
+
+	r = platform_device_register(&eisa_root_dev);
+	if (!r)
+		return r;
+
+	eisa_root_dev.dev.driver_data = &eisa_bus_root;
+
+	if (eisa_root_register(&eisa_bus_root)) {
+		/* A real bridge may have been registered before
+		 * us. So quietly unregister. */
+		platform_device_unregister(&eisa_root_dev);
+		return -1;
+	}
+	return 0;
+}
diff --git a/arch/mips/sni/irq.c b/arch/mips/sni/irq.c
index 9ccffdf..e8e72bb 100644
--- a/arch/mips/sni/irq.c
+++ b/arch/mips/sni/irq.c
@@ -35,14 +35,14 @@
 	if (unlikely(irq < 0))
 		return IRQ_NONE;
 
-	do_IRQ(irq);
+	generic_handle_irq(irq);
 	return IRQ_HANDLED;
 }
 
 struct irqaction sni_isa_irq = {
 	.handler = sni_isa_irq_handler,
 	.name = "ISA",
-	.flags = IRQF_SHARED
+	.flags = IRQF_SHARED | IRQF_DISABLED
 };
 
 /*
diff --git a/arch/mips/sni/rm200.c b/arch/mips/sni/rm200.c
index 67b061e..5310aa7 100644
--- a/arch/mips/sni/rm200.c
+++ b/arch/mips/sni/rm200.c
@@ -5,30 +5,36 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ * Copyright (C) 2006,2007 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
+ *
+ * i8259 parts ripped out of arch/mips/kernel/i8259.c
  */
 
+#include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/platform_device.h>
 #include <linux/serial_8250.h>
+#include <linux/io.h>
 
 #include <asm/sni.h>
 #include <asm/time.h>
 #include <asm/irq_cpu.h>
 
-#define PORT(_base,_irq)				\
+#define RM200_I8259A_IRQ_BASE 32
+
+#define MEMPORT(_base,_irq)				\
 	{						\
-		.iobase		= _base,		\
+		.mapbase	= _base,		\
 		.irq		= _irq,			\
 		.uartclk	= 1843200,		\
-		.iotype		= UPIO_PORT,		\
-		.flags		= UPF_BOOT_AUTOCONF,	\
+		.iotype		= UPIO_MEM,		\
+		.flags		= UPF_BOOT_AUTOCONF|UPF_IOREMAP, \
 	}
 
 static struct plat_serial8250_port rm200_data[] = {
-	PORT(0x3f8, 4),
-	PORT(0x2f8, 3),
+	MEMPORT(0x160003f8, RM200_I8259A_IRQ_BASE + 4),
+	MEMPORT(0x160002f8, RM200_I8259A_IRQ_BASE + 3),
 	{ },
 };
 
@@ -112,15 +118,311 @@
 		platform_device_register(&rm200_ds1216_device);
 		platform_device_register(&snirm_82596_rm200_pdev);
 		platform_device_register(&snirm_53c710_rm200_pdev);
+		sni_eisa_root_init();
 	}
 	return 0;
 }
 
 device_initcall(snirm_setup_devinit);
 
+/*
+ * RM200 has an ISA and an EISA bus. The iSA bus is only used
+ * for onboard devices and also has twi i8259 PICs. Since these
+ * PICs are no accessible via inb/outb the following code uses
+ * readb/writeb to access them
+ */
 
-#define SNI_RM200_INT_STAT_REG  0xbc000000
-#define SNI_RM200_INT_ENA_REG   0xbc080000
+DEFINE_SPINLOCK(sni_rm200_i8259A_lock);
+#define PIC_CMD    0x00
+#define PIC_IMR    0x01
+#define PIC_ISR    PIC_CMD
+#define PIC_POLL   PIC_ISR
+#define PIC_OCW3   PIC_ISR
+
+/* i8259A PIC related value */
+#define PIC_CASCADE_IR		2
+#define MASTER_ICW4_DEFAULT	0x01
+#define SLAVE_ICW4_DEFAULT	0x01
+
+/*
+ * This contains the irq mask for both 8259A irq controllers,
+ */
+static unsigned int rm200_cached_irq_mask = 0xffff;
+static __iomem u8 *rm200_pic_master;
+static __iomem u8 *rm200_pic_slave;
+
+#define cached_master_mask	(rm200_cached_irq_mask)
+#define cached_slave_mask	(rm200_cached_irq_mask >> 8)
+
+static void sni_rm200_disable_8259A_irq(unsigned int irq)
+{
+	unsigned int mask;
+	unsigned long flags;
+
+	irq -= RM200_I8259A_IRQ_BASE;
+	mask = 1 << irq;
+	spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+	rm200_cached_irq_mask |= mask;
+	if (irq & 8)
+		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+	else
+		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+	spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+static void sni_rm200_enable_8259A_irq(unsigned int irq)
+{
+	unsigned int mask;
+	unsigned long flags;
+
+	irq -= RM200_I8259A_IRQ_BASE;
+	mask = ~(1 << irq);
+	spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+	rm200_cached_irq_mask &= mask;
+	if (irq & 8)
+		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+	else
+		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+	spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+static inline int sni_rm200_i8259A_irq_real(unsigned int irq)
+{
+	int value;
+	int irqmask = 1 << irq;
+
+	if (irq < 8) {
+		writeb(0x0B, rm200_pic_master + PIC_CMD);
+		value = readb(rm200_pic_master + PIC_CMD) & irqmask;
+		writeb(0x0A, rm200_pic_master + PIC_CMD);
+		return value;
+	}
+	writeb(0x0B, rm200_pic_slave + PIC_CMD); /* ISR register */
+	value = readb(rm200_pic_slave + PIC_CMD) & (irqmask >> 8);
+	writeb(0x0A, rm200_pic_slave + PIC_CMD);
+	return value;
+}
+
+/*
+ * Careful! The 8259A is a fragile beast, it pretty
+ * much _has_ to be done exactly like this (mask it
+ * first, _then_ send the EOI, and the order of EOI
+ * to the two 8259s is important!
+ */
+void sni_rm200_mask_and_ack_8259A(unsigned int irq)
+{
+	unsigned int irqmask;
+	unsigned long flags;
+
+	irq -= RM200_I8259A_IRQ_BASE;
+	irqmask = 1 << irq;
+	spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+	/*
+	 * Lightweight spurious IRQ detection. We do not want
+	 * to overdo spurious IRQ handling - it's usually a sign
+	 * of hardware problems, so we only do the checks we can
+	 * do without slowing down good hardware unnecessarily.
+	 *
+	 * Note that IRQ7 and IRQ15 (the two spurious IRQs
+	 * usually resulting from the 8259A-1|2 PICs) occur
+	 * even if the IRQ is masked in the 8259A. Thus we
+	 * can check spurious 8259A IRQs without doing the
+	 * quite slow i8259A_irq_real() call for every IRQ.
+	 * This does not cover 100% of spurious interrupts,
+	 * but should be enough to warn the user that there
+	 * is something bad going on ...
+	 */
+	if (rm200_cached_irq_mask & irqmask)
+		goto spurious_8259A_irq;
+	rm200_cached_irq_mask |= irqmask;
+
+handle_real_irq:
+	if (irq & 8) {
+		readb(rm200_pic_slave + PIC_IMR);
+		writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+		writeb(0x60+(irq & 7), rm200_pic_slave + PIC_CMD);
+		writeb(0x60+PIC_CASCADE_IR, rm200_pic_master + PIC_CMD);
+	} else {
+		readb(rm200_pic_master + PIC_IMR);
+		writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+		writeb(0x60+irq, rm200_pic_master + PIC_CMD);
+	}
+	spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+	return;
+
+spurious_8259A_irq:
+	/*
+	 * this is the slow path - should happen rarely.
+	 */
+	if (sni_rm200_i8259A_irq_real(irq))
+		/*
+		 * oops, the IRQ _is_ in service according to the
+		 * 8259A - not spurious, go handle it.
+		 */
+		goto handle_real_irq;
+
+	{
+		static int spurious_irq_mask;
+		/*
+		 * At this point we can be sure the IRQ is spurious,
+		 * lets ACK and report it. [once per IRQ]
+		 */
+		if (!(spurious_irq_mask & irqmask)) {
+			printk(KERN_DEBUG
+			       "spurious RM200 8259A interrupt: IRQ%d.\n", irq);
+			spurious_irq_mask |= irqmask;
+		}
+		atomic_inc(&irq_err_count);
+		/*
+		 * Theoretically we do not have to handle this IRQ,
+		 * but in Linux this does not cause problems and is
+		 * simpler for us.
+		 */
+		goto handle_real_irq;
+	}
+}
+
+static struct irq_chip sni_rm200_i8259A_chip = {
+	.name		= "RM200-XT-PIC",
+	.mask		= sni_rm200_disable_8259A_irq,
+	.unmask		= sni_rm200_enable_8259A_irq,
+	.mask_ack	= sni_rm200_mask_and_ack_8259A,
+};
+
+/*
+ * Do the traditional i8259 interrupt polling thing.  This is for the few
+ * cases where no better interrupt acknowledge method is available and we
+ * absolutely must touch the i8259.
+ */
+static inline int sni_rm200_i8259_irq(void)
+{
+	int irq;
+
+	spin_lock(&sni_rm200_i8259A_lock);
+
+	/* Perform an interrupt acknowledge cycle on controller 1. */
+	writeb(0x0C, rm200_pic_master + PIC_CMD);	/* prepare for poll */
+	irq = readb(rm200_pic_master + PIC_CMD) & 7;
+	if (irq == PIC_CASCADE_IR) {
+		/*
+		 * Interrupt is cascaded so perform interrupt
+		 * acknowledge on controller 2.
+		 */
+		writeb(0x0C, rm200_pic_slave + PIC_CMD); /* prepare for poll */
+		irq = (readb(rm200_pic_slave + PIC_CMD) & 7) + 8;
+	}
+
+	if (unlikely(irq == 7)) {
+		/*
+		 * This may be a spurious interrupt.
+		 *
+		 * Read the interrupt status register (ISR). If the most
+		 * significant bit is not set then there is no valid
+		 * interrupt.
+		 */
+		writeb(0x0B, rm200_pic_master + PIC_ISR); /* ISR register */
+		if (~readb(rm200_pic_master + PIC_ISR) & 0x80)
+			irq = -1;
+	}
+
+	spin_unlock(&sni_rm200_i8259A_lock);
+
+	return likely(irq >= 0) ? irq + RM200_I8259A_IRQ_BASE : irq;
+}
+
+void sni_rm200_init_8259A(void)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&sni_rm200_i8259A_lock, flags);
+
+	writeb(0xff, rm200_pic_master + PIC_IMR);
+	writeb(0xff, rm200_pic_slave + PIC_IMR);
+
+	writeb(0x11, rm200_pic_master + PIC_CMD);
+	writeb(0, rm200_pic_master + PIC_IMR);
+	writeb(1U << PIC_CASCADE_IR, rm200_pic_master + PIC_IMR);
+	writeb(MASTER_ICW4_DEFAULT, rm200_pic_master + PIC_IMR);
+	writeb(0x11, rm200_pic_slave + PIC_CMD);
+	writeb(8, rm200_pic_slave + PIC_IMR);
+	writeb(PIC_CASCADE_IR, rm200_pic_slave + PIC_IMR);
+	writeb(SLAVE_ICW4_DEFAULT, rm200_pic_slave + PIC_IMR);
+	udelay(100);		/* wait for 8259A to initialize */
+
+	writeb(cached_master_mask, rm200_pic_master + PIC_IMR);
+	writeb(cached_slave_mask, rm200_pic_slave + PIC_IMR);
+
+	spin_unlock_irqrestore(&sni_rm200_i8259A_lock, flags);
+}
+
+/*
+ * IRQ2 is cascade interrupt to second interrupt controller
+ */
+static struct irqaction sni_rm200_irq2 = {
+	no_action, 0, CPU_MASK_NONE, "cascade", NULL, NULL
+};
+
+static struct resource sni_rm200_pic1_resource = {
+	.name = "onboard ISA pic1",
+	.start = 0x16000020,
+	.end = 0x16000023,
+	.flags = IORESOURCE_BUSY
+};
+
+static struct resource sni_rm200_pic2_resource = {
+	.name = "onboard ISA pic2",
+	.start = 0x160000a0,
+	.end = 0x160000a3,
+	.flags = IORESOURCE_BUSY
+};
+
+/* ISA irq handler */
+static irqreturn_t sni_rm200_i8259A_irq_handler(int dummy, void *p)
+{
+	int irq;
+
+	irq = sni_rm200_i8259_irq();
+	if (unlikely(irq < 0))
+		return IRQ_NONE;
+
+	do_IRQ(irq);
+	return IRQ_HANDLED;
+}
+
+struct irqaction sni_rm200_i8259A_irq = {
+	.handler = sni_rm200_i8259A_irq_handler,
+	.name = "onboard ISA",
+	.flags = IRQF_SHARED
+};
+
+void __init sni_rm200_i8259_irqs(void)
+{
+	int i;
+
+	rm200_pic_master = ioremap_nocache(0x16000020, 4);
+	if (!rm200_pic_master)
+		return;
+	rm200_pic_slave = ioremap_nocache(0x160000a0, 4);
+	if (!rm200_pic_master) {
+		iounmap(rm200_pic_master);
+		return;
+	}
+
+	insert_resource(&iomem_resource, &sni_rm200_pic1_resource);
+	insert_resource(&iomem_resource, &sni_rm200_pic2_resource);
+
+	sni_rm200_init_8259A();
+
+	for (i = RM200_I8259A_IRQ_BASE; i < RM200_I8259A_IRQ_BASE + 16; i++)
+		set_irq_chip_and_handler(i, &sni_rm200_i8259A_chip,
+					 handle_level_irq);
+
+	setup_irq(RM200_I8259A_IRQ_BASE + PIC_CASCADE_IR, &sni_rm200_irq2);
+}
+
+
+#define SNI_RM200_INT_STAT_REG  CKSEG1ADDR(0xbc000000)
+#define SNI_RM200_INT_ENA_REG   CKSEG1ADDR(0xbc080000)
 
 #define SNI_RM200_INT_START  24
 #define SNI_RM200_INT_END    28
@@ -181,17 +483,17 @@
 
 	* (volatile u8 *)SNI_RM200_INT_ENA_REG = 0x1f;
 
+	sni_rm200_i8259_irqs();
 	mips_cpu_irq_init();
 	/* Actually we've got more interrupts to handle ...  */
 	for (i = SNI_RM200_INT_START; i <= SNI_RM200_INT_END; i++)
 		set_irq_chip(i, &rm200_irq_type);
 	sni_hwint = sni_rm200_hwint;
 	change_c0_status(ST0_IM, IE_IRQ0);
-	setup_irq(SNI_RM200_INT_START + 0, &sni_isa_irq);
+	setup_irq(SNI_RM200_INT_START + 0, &sni_rm200_i8259A_irq);
+	setup_irq(SNI_RM200_INT_START + 1, &sni_isa_irq);
 }
 
 void __init sni_rm200_init(void)
 {
-	set_io_port_base(SNI_PORT_BASE + 0x02000000);
-	ioport_resource.end += 0x02000000;
 }
diff --git a/arch/mips/sni/setup.c b/arch/mips/sni/setup.c
index e8b26bd..5484e1c 100644
--- a/arch/mips/sni/setup.c
+++ b/arch/mips/sni/setup.c
@@ -19,11 +19,17 @@
 #include <asm/sgialib.h>
 #endif
 
+#ifdef CONFIG_SNIPROM
+#include <asm/mipsprom.h>
+#endif
+
+#include <asm/bootinfo.h>
 #include <asm/io.h>
 #include <asm/reboot.h>
 #include <asm/sni.h>
 
 unsigned int sni_brd_type;
+EXPORT_SYMBOL(sni_brd_type);
 
 extern void sni_machine_restart(char *command);
 extern void sni_machine_power_off(void);
@@ -47,20 +53,152 @@
 #endif
 }
 
+static void __init sni_console_setup(void)
+{
+#ifndef CONFIG_ARC
+	char *ctype;
+	char *cdev;
+	char *baud;
+	int port;
+	static char options[8];
+
+	cdev = prom_getenv("console_dev");
+	if (strncmp(cdev, "tty", 3) == 0) {
+		ctype = prom_getenv("console");
+		switch (*ctype) {
+		default:
+		case 'l':
+			port = 0;
+			baud = prom_getenv("lbaud");
+			break;
+		case 'r':
+			port = 1;
+			baud = prom_getenv("rbaud");
+			break;
+		}
+		if (baud)
+			strcpy(options, baud);
+		if (strncmp(cdev, "tty552", 6) == 0)
+			add_preferred_console("ttyS", port,
+					      baud ? options : NULL);
+		else
+			add_preferred_console("ttySC", port,
+					      baud ? options : NULL);
+	}
+#endif
+}
+
+#ifdef DEBUG
+static void __init sni_idprom_dump(void)
+{
+	int	i;
+
+	pr_debug("SNI IDProm dump:\n");
+	for (i = 0; i < 256; i++) {
+		if (i%16 == 0)
+			pr_debug("%04x ", i);
+
+		printk("%02x ", *(unsigned char *) (SNI_IDPROM_BASE + i));
+
+		if (i % 16 == 15)
+			printk("\n");
+	}
+}
+#endif
 
 void __init plat_mem_setup(void)
 {
+	int cputype;
+
 	set_io_port_base(SNI_PORT_BASE);
 //	ioport_resource.end = sni_io_resource.end;
 
 	/*
 	 * Setup (E)ISA I/O memory access stuff
 	 */
-	isa_slot_offset = 0xb0000000;
+	isa_slot_offset = CKSEG1ADDR(0xb0000000);
 #ifdef CONFIG_EISA
 	EISA_bus = 1;
 #endif
 
+	sni_brd_type = *(unsigned char *)SNI_IDPROM_BRDTYPE;
+	cputype = *(unsigned char *)SNI_IDPROM_CPUTYPE;
+	switch (sni_brd_type) {
+	case SNI_BRD_TOWER_OASIC:
+		switch (cputype) {
+		case SNI_CPU_M8030:
+			system_type = "RM400-330";
+			break;
+		case SNI_CPU_M8031:
+			system_type = "RM400-430";
+			break;
+		case SNI_CPU_M8037:
+			system_type = "RM400-530";
+			break;
+		case SNI_CPU_M8034:
+			system_type = "RM400-730";
+			break;
+		default:
+			system_type = "RM400-xxx";
+			break;
+		}
+		break;
+	case SNI_BRD_MINITOWER:
+		switch (cputype) {
+		case SNI_CPU_M8021:
+		case SNI_CPU_M8043:
+			system_type = "RM400-120";
+			break;
+		case SNI_CPU_M8040:
+			system_type = "RM400-220";
+			break;
+		case SNI_CPU_M8053:
+			system_type = "RM400-225";
+			break;
+		case SNI_CPU_M8050:
+			system_type = "RM400-420";
+			break;
+		default:
+			system_type = "RM400-xxx";
+			break;
+		}
+		break;
+	case SNI_BRD_PCI_TOWER:
+		system_type = "RM400-Cxx";
+		break;
+	case SNI_BRD_RM200:
+		system_type = "RM200-xxx";
+		break;
+	case SNI_BRD_PCI_MTOWER:
+		system_type = "RM300-Cxx";
+		break;
+	case SNI_BRD_PCI_DESKTOP:
+		switch (read_c0_prid() & 0xff00) {
+		case PRID_IMP_R4600:
+		case PRID_IMP_R4700:
+			system_type = "RM200-C20";
+			break;
+		case PRID_IMP_R5000:
+			system_type = "RM200-C40";
+			break;
+		default:
+			system_type = "RM200-Cxx";
+			break;
+		}
+		break;
+	case SNI_BRD_PCI_TOWER_CPLUS:
+		system_type = "RM400-Exx";
+		break;
+	case SNI_BRD_PCI_MTOWER_CPLUS:
+		system_type = "RM300-Exx";
+		break;
+	}
+	pr_debug("Found SNI brdtype %02x name %s\n", sni_brd_type, system_type);
+
+#ifdef DEBUG
+	sni_idprom_dump();
+#endif
+
 	switch (sni_brd_type) {
 	case SNI_BRD_10:
 	case SNI_BRD_10NEW:
@@ -89,9 +227,10 @@
 	pm_power_off = sni_machine_power_off;
 
 	sni_display_setup();
+	sni_console_setup();
 }
 
-#if CONFIG_PCI
+#ifdef CONFIG_PCI
 
 #include <linux/pci.h>
 #include <video/vga.h>
diff --git a/arch/mips/sni/sniprom.c b/arch/mips/sni/sniprom.c
deleted file mode 100644
index eff4b89..0000000
--- a/arch/mips/sni/sniprom.c
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * Big Endian PROM code for SNI RM machines
- *
- * 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) 2005-2006 Florian Lohoff (flo@rfc822.org)
- * Copyright (C) 2005-2006 Thomas Bogendoerfer (tsbogend@alpha.franken.de)
- */
-
-#define DEBUG
-
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/string.h>
-#include <linux/console.h>
-
-#include <asm/addrspace.h>
-#include <asm/sni.h>
-#include <asm/mipsprom.h>
-#include <asm/mipsregs.h>
-#include <asm/bootinfo.h>
-
-/* special SNI prom calls */
-/*
- * This does not exist in all proms - SINIX compares
- * the prom env variable "version" against "2.0008"
- * or greater. If lesser it tries to probe interesting
- * registers
- */
-#define PROM_GET_MEMCONF	58
-
-#define PROM_VEC		(u64 *)CKSEG1ADDR(0x1fc00000)
-#define PROM_ENTRY(x)		(PROM_VEC + (x))
-
-
-static int *(*__prom_putchar)(int)        = (int *(*)(int))PROM_ENTRY(PROM_PUTCHAR);
-
-void prom_putchar(char c)
-{
-	__prom_putchar(c);
-}
-
-static char *(*__prom_getenv)(char *)     = (char *(*)(char *))PROM_ENTRY(PROM_GETENV);
-static void (*__prom_get_memconf)(void *) = (void (*)(void *))PROM_ENTRY(PROM_GET_MEMCONF);
-
-char *prom_getenv(char *s)
-{
-	return __prom_getenv(s);
-}
-
-void __init prom_free_prom_memory(void)
-{
-}
-
-/*
- * /proc/cpuinfo system type
- *
- */
-static const char *systype = "Unknown";
-const char *get_system_type(void)
-{
-	return systype;
-}
-
-#define SNI_IDPROM_BASE                0xbff00000
-#define SNI_IDPROM_MEMSIZE             (SNI_IDPROM_BASE+0x28)  /* Memsize in 16MB quantities */
-#define SNI_IDPROM_BRDTYPE             (SNI_IDPROM_BASE+0x29)  /* Board Type */
-#define SNI_IDPROM_CPUTYPE             (SNI_IDPROM_BASE+0x30)  /* CPU Type */
-
-#define SNI_IDPROM_SIZE	0x1000
-
-#ifdef DEBUG
-static void __init sni_idprom_dump(void)
-{
-	int	i;
-
-	pr_debug("SNI IDProm dump:\n");
-	for (i = 0; i < 256; i++) {
-		if (i%16 == 0)
-			pr_debug("%04x ", i);
-
-		printk("%02x ", *(unsigned char *) (SNI_IDPROM_BASE + i));
-
-		if (i % 16 == 15)
-			printk("\n");
-	}
-}
-#endif
-
-static void __init sni_mem_init(void )
-{
-	int i, memsize;
-	struct membank {
-	        u32		size;
-	        u32		base;
-	        u32		size2;
-	        u32		pad1;
-	        u32		pad2;
-	} memconf[8];
-
-	/* MemSIZE from prom in 16MByte chunks */
-	memsize = *((unsigned char *) SNI_IDPROM_MEMSIZE) * 16;
-
-	pr_debug("IDProm memsize: %lu MByte\n", memsize);
-
-	/* get memory bank layout from prom */
-	__prom_get_memconf(&memconf);
-
-	pr_debug("prom_get_mem_conf memory configuration:\n");
-	for (i = 0;i < 8 && memconf[i].size; i++) {
-		if (sni_brd_type == SNI_BRD_PCI_TOWER ||
-		    sni_brd_type == SNI_BRD_PCI_TOWER_CPLUS) {
-			if (memconf[i].base >= 0x20000000 &&
-			    memconf[i].base <  0x30000000) {
-				memconf[i].base -= 0x20000000;
-			}
-	}
-		pr_debug("Bank%d: %08x @ %08x\n", i,
-			memconf[i].size, memconf[i].base);
-		add_memory_region(memconf[i].base, memconf[i].size, BOOT_MEM_RAM);
-	}
-}
-
-static void __init sni_console_setup(void)
-{
-	char *ctype;
-	char *cdev;
-	char *baud;
-	int port;
-	static char options[8];
-
-	cdev = prom_getenv("console_dev");
-	if (strncmp (cdev, "tty", 3) == 0) {
-		ctype = prom_getenv("console");
-		switch (*ctype) {
-		default:
-		case 'l':
-	                port = 0;
-	                baud = prom_getenv("lbaud");
-	                break;
-		case 'r':
-	                port = 1;
-	                baud = prom_getenv("rbaud");
-	                break;
-		}
-		if (baud)
-			strcpy(options, baud);
-		if (strncmp (cdev, "tty552", 6) == 0)
-			add_preferred_console("ttyS", port, baud ? options : NULL);
-		else
-			add_preferred_console("ttySC", port, baud ? options : NULL);
-	}
-}
-
-void __init prom_init(void)
-{
-	int argc = fw_arg0;
-	char **argv = (void *)fw_arg1;
-	int i;
-	int cputype;
-
-	sni_brd_type = *(unsigned char *)SNI_IDPROM_BRDTYPE;
-	cputype = *(unsigned char *)SNI_IDPROM_CPUTYPE;
-	switch (sni_brd_type) {
-	case SNI_BRD_TOWER_OASIC:
-	        switch (cputype) {
-		case SNI_CPU_M8030:
-		        systype = "RM400-330";
-		        break;
-		case SNI_CPU_M8031:
-		        systype = "RM400-430";
-		        break;
-		case SNI_CPU_M8037:
-		        systype = "RM400-530";
-		        break;
-		case SNI_CPU_M8034:
-		        systype = "RM400-730";
-		        break;
-		default:
-			systype = "RM400-xxx";
-			break;
-		}
-	        break;
-	case SNI_BRD_MINITOWER:
-	        switch (cputype) {
-		case SNI_CPU_M8021:
-		case SNI_CPU_M8043:
-		        systype = "RM400-120";
-		        break;
-		case SNI_CPU_M8040:
-		        systype = "RM400-220";
-		        break;
-		case SNI_CPU_M8053:
-		        systype = "RM400-225";
-		        break;
-		case SNI_CPU_M8050:
-		        systype = "RM400-420";
-		        break;
-		default:
-			systype = "RM400-xxx";
-			break;
-		}
-	        break;
-	case SNI_BRD_PCI_TOWER:
-	        systype = "RM400-Cxx";
-	        break;
-	case SNI_BRD_RM200:
-	        systype = "RM200-xxx";
-	        break;
-	case SNI_BRD_PCI_MTOWER:
-	        systype = "RM300-Cxx";
-	        break;
-	case SNI_BRD_PCI_DESKTOP:
-	        switch (read_c0_prid() & 0xff00) {
-		case PRID_IMP_R4600:
-		case PRID_IMP_R4700:
-		        systype = "RM200-C20";
-		        break;
-		case PRID_IMP_R5000:
-		        systype = "RM200-C40";
-		        break;
-		default:
-		        systype = "RM200-Cxx";
-		        break;
-		}
-	        break;
-	case SNI_BRD_PCI_TOWER_CPLUS:
-	        systype = "RM400-Exx";
-	        break;
-	case SNI_BRD_PCI_MTOWER_CPLUS:
-	        systype = "RM300-Exx";
-	        break;
-	}
-	pr_debug("Found SNI brdtype %02x name %s\n", sni_brd_type, systype);
-
-#ifdef DEBUG
-	sni_idprom_dump();
-#endif
-	sni_mem_init();
-	sni_console_setup();
-
-	/* copy prom cmdline parameters to kernel cmdline */
-	for (i = 1; i < argc; i++) {
-		strcat(arcs_cmdline, argv[i]);
-		if (i < (argc - 1))
-			strcat(arcs_cmdline, " ");
-	}
-}
-
diff --git a/arch/mips/sni/time.c b/arch/mips/sni/time.c
index 6f339af..796e3ce 100644
--- a/arch/mips/sni/time.c
+++ b/arch/mips/sni/time.c
@@ -178,6 +178,7 @@
 		sni_a20r_timer_setup();
 		break;
 	}
+	setup_pit_timer();
 }
 
 unsigned long read_persistent_clock(void)