Linux-2.6.12-rc2

Initial git repository build. I'm not bothering with the full history,
even though we have it. We can create a separate "historical" git
archive of that later if we want to, and in the meantime it's about
3.2GB when imported into git - space that would just make the early
git days unnecessarily complicated, when we don't have a lot of good
infrastructure for it.

Let it rip!
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
new file mode 100644
index 0000000..eb0820f
--- /dev/null
+++ b/arch/mips/sgi-ip22/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the SGI specific kernel interface routines
+# under Linux.
+#
+
+obj-y	+= ip22-mc.o ip22-hpc.o ip22-int.o ip22-irq.o ip22-berr.o \
+	   ip22-time.o ip22-nvram.o ip22-reset.o ip22-setup.o
+
+obj-$(CONFIG_EISA)	+= ip22-eisa.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c
new file mode 100644
index 0000000..a28dc78
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-berr.c
@@ -0,0 +1,114 @@
+/*
+ * ip22-berr.c: Bus error handling.
+ *
+ * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/addrspace.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+#include <asm/branch.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+
+static unsigned int cpu_err_stat;	/* Status reg for CPU */
+static unsigned int gio_err_stat;	/* Status reg for GIO */
+static unsigned int cpu_err_addr;	/* Error address reg for CPU */
+static unsigned int gio_err_addr;	/* Error address reg for GIO */
+static unsigned int extio_stat;
+static unsigned int hpc3_berr_stat;	/* Bus error interrupt status */
+
+static void save_and_clear_buserr(void)
+{
+	/* save status registers */
+	cpu_err_addr = sgimc->cerr;
+	cpu_err_stat = sgimc->cstat;
+	gio_err_addr = sgimc->gerr;
+	gio_err_stat = sgimc->gstat;
+	extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4);
+	hpc3_berr_stat = hpc3c0->bestat;
+
+	sgimc->cstat = sgimc->gstat = 0;
+}
+
+#define GIO_ERRMASK	0xff00
+#define CPU_ERRMASK	0x3f00
+
+static void print_buserr(void)
+{
+	if (extio_stat & EXTIO_MC_BUSERR)
+		printk(KERN_ERR "MC Bus Error\n");
+	if (extio_stat & EXTIO_HPC3_BUSERR)
+		printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n",
+			hpc3_berr_stat,
+			(hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >>
+					  HPC3_BESTAT_PIDSHIFT,
+			(hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA",
+			hpc3_berr_stat & HPC3_BESTAT_BLMASK);
+	if (extio_stat & EXTIO_EISA_BUSERR)
+		printk(KERN_ERR "EISA Bus Error\n");
+	if (cpu_err_stat & CPU_ERRMASK)
+		printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n",
+			cpu_err_stat,
+			cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "",
+			cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "",
+			cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "",
+			cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "",
+			cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "",
+			cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "",
+			cpu_err_addr);
+	if (gio_err_stat & GIO_ERRMASK)
+		printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n",
+			gio_err_stat,
+			gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "",
+			gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "",
+			gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "",
+			gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "",
+			gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "",
+			gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "",
+			gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "",
+			gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "",
+			gio_err_addr);
+}
+
+/*
+ * MC sends an interrupt whenever bus or parity errors occur. In addition,
+ * if the error happened during a CPU read, it also asserts the bus error
+ * pin on the R4K. Code in bus error handler save the MC bus error registers
+ * and then clear the interrupt when this happens.
+ */
+
+void ip22_be_interrupt(int irq, struct pt_regs *regs)
+{
+	const int field = 2 * sizeof(unsigned long);
+
+	save_and_clear_buserr();
+	print_buserr();
+	printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
+	       (regs->cp0_cause & 4) ? "Data" : "Instruction",
+	       field, regs->cp0_epc, field, regs->regs[31]);
+	/* Assume it would be too dangerous to continue ... */
+	die_if_kernel("Oops", regs);
+	force_sig(SIGBUS, current);
+}
+
+static int ip22_be_handler(struct pt_regs *regs, int is_fixup)
+{
+	save_and_clear_buserr();
+	if (is_fixup)
+		return MIPS_BE_FIXUP;
+	print_buserr();
+	return MIPS_BE_FATAL;
+}
+
+void __init ip22_be_init(void)
+{
+	board_be_handler = ip22_be_handler;
+}
diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c
new file mode 100644
index 0000000..0ab4abf
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-eisa.c
@@ -0,0 +1,308 @@
+/*
+ * Basic EISA bus support for the SGI Indigo-2.
+ *
+ * (C) 2002 Pascal Dameme <netinet@freesurf.fr>
+ *      and Marc Zyngier <mzyngier@freesurf.fr>
+ *
+ * This code is released under both the GPL version 2 and BSD
+ * licenses.  Either license may be used.
+ *
+ * This code offers a very basic support for this EISA bus present in
+ * the SGI Indigo-2. It currently only supports PIO (forget about DMA
+ * for the time being). This is enough for a low-end ethernet card,
+ * but forget about your favorite SCSI card...
+ *
+ * TODO :
+ * - Fix bugs...
+ * - Add ISA support
+ * - Add DMA (yeah, right...).
+ * - Fix more bugs.
+ */
+
+#include <linux/config.h>
+#include <linux/eisa.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/processor.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+#define EISA_MAX_SLOTS		  4
+#define EISA_MAX_IRQ             16
+
+#define EISA_TO_PHYS(x)  (0x00080000 | (x))
+#define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x))))
+
+#define EIU_MODE_REG     0x0009ffc0
+#define EIU_STAT_REG     0x0009ffc4
+#define EIU_PREMPT_REG   0x0009ffc8
+#define EIU_QUIET_REG    0x0009ffcc
+#define EIU_INTRPT_ACK   0x00090004
+
+#define EISA_DMA1_STATUS            8
+#define EISA_INT1_CTRL           0x20
+#define EISA_INT1_MASK           0x21
+#define EISA_INT2_CTRL           0xA0
+#define EISA_INT2_MASK           0xA1
+#define EISA_DMA2_STATUS         0xD0
+#define EISA_DMA2_WRITE_SINGLE   0xD4
+#define EISA_EXT_NMI_RESET_CTRL 0x461
+#define EISA_INT1_EDGE_LEVEL    0x4D0
+#define EISA_INT2_EDGE_LEVEL    0x4D1
+#define EISA_VENDOR_ID_OFFSET   0xC80
+
+#define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); }
+#define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x))
+#define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); }
+#define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x))
+
+static char *decode_eisa_sig(u8 * sig)
+{
+	static char sig_str[8];
+	u16 rev;
+
+	if (sig[0] & 0x80)
+		return NULL;
+
+	sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1);
+	sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1);
+	sig_str[2] = (sig[1] & 0x1f) + ('A' - 1);
+	rev = (sig[2] << 8) | sig[3];
+	sprintf(sig_str + 3, "%04X", rev);
+
+	return sig_str;
+}
+
+static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u8 eisa_irq;
+	u8 dma1, dma2;
+
+	eisa_irq = EIU_READ_8(EIU_INTRPT_ACK);
+	dma1 = EISA_READ_8(EISA_DMA1_STATUS);
+	dma2 = EISA_READ_8(EISA_DMA2_STATUS);
+
+	if (eisa_irq >= EISA_MAX_IRQ) {
+		/* Oops, Bad Stuff Happened... */
+		printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
+
+		EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
+		EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+	} else
+		do_IRQ(eisa_irq, regs);
+}
+
+static void enable_eisa1_irq(unsigned int irq)
+{
+	unsigned long flags;
+	u8 mask;
+
+	local_irq_save(flags);
+
+	mask = EISA_READ_8(EISA_INT1_MASK);
+	mask &= ~((u8) (1 << irq));
+	EISA_WRITE_8(EISA_INT1_MASK, mask);
+
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_eisa1_irq(unsigned int irq)
+{
+	u8 edge;
+
+	/* Only use edge interrupts for EISA */
+
+	edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL);
+	edge &= ~((u8) (1 << irq));
+	EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge);
+
+	enable_eisa1_irq(irq);
+	return 0;
+}
+
+static void disable_eisa1_irq(unsigned int irq)
+{
+	u8 mask;
+
+	mask = EISA_READ_8(EISA_INT1_MASK);
+	mask |= ((u8) (1 << irq));
+	EISA_WRITE_8(EISA_INT1_MASK, mask);
+}
+
+#define shutdown_eisa1_irq	disable_eisa1_irq
+
+static void mask_and_ack_eisa1_irq(unsigned int irq)
+{
+	disable_eisa1_irq(irq);
+
+	EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+}
+
+static void end_eisa1_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+		enable_eisa1_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_eisa1_irq_type = {
+	.typename	= "IP22 EISA",
+	.startup	= startup_eisa1_irq,
+	.shutdown	= shutdown_eisa1_irq,
+	.enable		= enable_eisa1_irq,
+	.disable	= disable_eisa1_irq,
+	.ack		= mask_and_ack_eisa1_irq,
+	.end		= end_eisa1_irq,
+};
+
+static void enable_eisa2_irq(unsigned int irq)
+{
+	unsigned long flags;
+	u8 mask;
+
+	local_irq_save(flags);
+
+	mask = EISA_READ_8(EISA_INT2_MASK);
+	mask &= ~((u8) (1 << (irq - 8)));
+	EISA_WRITE_8(EISA_INT2_MASK, mask);
+
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_eisa2_irq(unsigned int irq)
+{
+	u8 edge;
+
+	/* Only use edge interrupts for EISA */
+
+	edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL);
+	edge &= ~((u8) (1 << (irq - 8)));
+	EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge);
+
+	enable_eisa2_irq(irq);
+	return 0;
+}
+
+static void disable_eisa2_irq(unsigned int irq)
+{
+	u8 mask;
+
+	mask = EISA_READ_8(EISA_INT2_MASK);
+	mask |= ((u8) (1 << (irq - 8)));
+	EISA_WRITE_8(EISA_INT2_MASK, mask);
+}
+
+#define shutdown_eisa2_irq	disable_eisa2_irq
+
+static void mask_and_ack_eisa2_irq(unsigned int irq)
+{
+	disable_eisa2_irq(irq);
+
+	EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
+	EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+}
+
+static void end_eisa2_irq(unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+		enable_eisa2_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_eisa2_irq_type = {
+	.typename	= "IP22 EISA",
+	.startup	= startup_eisa2_irq,
+	.shutdown	= shutdown_eisa2_irq,
+	.enable		= enable_eisa2_irq,
+	.disable	= disable_eisa2_irq,
+	.ack		= mask_and_ack_eisa2_irq,
+	.end		= end_eisa2_irq,
+};
+
+static struct irqaction eisa_action = {
+	.handler	= ip22_eisa_intr,
+	.name		= "EISA",
+};
+
+static struct irqaction cascade_action = {
+	.handler	= no_action,
+	.name		= "EISA cascade",
+};
+
+int __init ip22_eisa_init(void)
+{
+	int i, c;
+	char *str;
+	u8 *slot_addr;
+	
+	if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+		printk(KERN_INFO "EISA: bus not present.\n");
+		return 1;
+	}
+
+	printk(KERN_INFO "EISA: Probing bus...\n");
+	for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) {
+		slot_addr =
+		    (u8 *) EISA_TO_KSEG1((0x1000 * i) +
+					 EISA_VENDOR_ID_OFFSET);
+		if ((str = decode_eisa_sig(slot_addr))) {
+			printk(KERN_INFO "EISA: slot %d : %s detected.\n",
+			       i, str);
+			c++;
+		}
+	}
+	printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s");
+#ifdef CONFIG_ISA
+	printk(KERN_INFO "ISA support compiled in.\n");
+#endif
+
+	/* Warning : BlackMagicAhead(tm).
+	   Please wave your favorite dead chicken over the busses */
+
+	/* First say hello to the EIU */
+	EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF);
+	EIU_WRITE_32(EIU_QUIET_REG, 1);
+	EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F);
+
+	/* Now be nice to the EISA chipset */
+	EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1);
+	for (i = 0; i < 10000; i++);	/* Wait long enough for the dust to settle */
+	EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0);
+	EISA_WRITE_8(EISA_INT1_CTRL, 0x11);
+	EISA_WRITE_8(EISA_INT2_CTRL, 0x11);
+	EISA_WRITE_8(EISA_INT1_MASK, 0);
+	EISA_WRITE_8(EISA_INT2_MASK, 8);
+	EISA_WRITE_8(EISA_INT1_MASK, 4);
+	EISA_WRITE_8(EISA_INT2_MASK, 2);
+	EISA_WRITE_8(EISA_INT1_MASK, 1);
+	EISA_WRITE_8(EISA_INT2_MASK, 1);
+	EISA_WRITE_8(EISA_INT1_MASK, 0xfb);
+	EISA_WRITE_8(EISA_INT2_MASK, 0xff);
+	EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0);
+
+	for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = 0;
+		irq_desc[i].depth = 1;
+		if (i < (SGINT_EISA + 8))
+			irq_desc[i].handler = &ip22_eisa1_irq_type;
+		else
+			irq_desc[i].handler = &ip22_eisa2_irq_type;
+	}
+
+	/* Cannot use request_irq because of kmalloc not being ready at such
+	 * an early stage. Yes, I've been bitten... */
+	setup_irq(SGI_EISA_IRQ, &eisa_action);
+	setup_irq(SGINT_EISA + 2, &cascade_action);
+
+	EISA_bus = 1;
+	return 0;
+}
diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c
new file mode 100644
index 0000000..c0afecc
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-hpc.c
@@ -0,0 +1,63 @@
+/*
+ * ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1998 Ralf Baechle
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+struct hpc3_regs *hpc3c0, *hpc3c1;
+
+EXPORT_SYMBOL(hpc3c0);
+EXPORT_SYMBOL(hpc3c1);
+
+struct sgioc_regs *sgioc;
+
+EXPORT_SYMBOL(sgioc);
+
+/* We need software copies of these because they are write only. */
+u8 sgi_ioc_reset, sgi_ioc_write;
+
+extern char *system_type;
+
+void __init sgihpc_init(void)
+{
+	/* ioremap can't fail */
+	hpc3c0 = (struct hpc3_regs *)
+		 ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs));
+	hpc3c1 = (struct hpc3_regs *)
+		 ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs));
+	/* IOC lives in PBUS PIO channel 6 */
+	sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6];
+
+	hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16;
+	if (ip22_is_fullhouse()) {
+		/* Full House comes with INT2 which lives in PBUS PIO
+		 * channel 4 */
+		sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4];
+		system_type = "SGI Indigo2";
+	} else {
+		/* Guiness comes with INT3 which is part of IOC */
+		sgint = &sgioc->int3;
+		system_type = "SGI Indy";
+	}
+	
+	sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE |
+			 SGIOC_RESET_EISA | SGIOC_RESET_ISDN |
+			 SGIOC_RESET_LC0OFF);
+
+	sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH |
+			 SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL |
+			 SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE);
+
+	sgioc->reset = sgi_ioc_reset;
+	sgioc->write = sgi_ioc_write;
+}
diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c
new file mode 100644
index 0000000..ea2844d
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-int.c
@@ -0,0 +1,410 @@
+/*
+ * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
+ *             found on INDY and Indigo2 workstations.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
+ *                    - Indigo2 changes
+ *                    - Interrupt handling fixes
+ * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/* #define DEBUG_SGINT */
+
+/* So far nothing hangs here */
+#undef USE_LIO3_IRQ 
+
+struct sgint_regs *sgint;
+
+static char lc0msk_to_irqnr[256];
+static char lc1msk_to_irqnr[256];
+static char lc2msk_to_irqnr[256];
+static char lc3msk_to_irqnr[256];
+
+extern asmlinkage void indyIRQ(void);
+extern int ip22_eisa_init(void);
+
+static void enable_local0_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/* don't allow mappable interrupt to be enabled from setup_irq,
+	 * we have our own way to do so */
+	if (irq != SGI_MAP_0_IRQ)
+		sgint->imask0 |= (1 << (irq - SGINT_LOCAL0));
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_local0_irq(unsigned int irq)
+{
+	enable_local0_irq(irq);
+	return 0;		/* Never anything pending  */
+}
+
+static void disable_local0_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->imask0 &= ~(1 << (irq - SGINT_LOCAL0));
+	local_irq_restore(flags);
+}
+
+#define shutdown_local0_irq	disable_local0_irq
+#define mask_and_ack_local0_irq	disable_local0_irq
+
+static void end_local0_irq (unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		enable_local0_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local0_irq_type = {
+	.typename	= "IP22 local 0",
+	.startup	= startup_local0_irq,
+	.shutdown	= shutdown_local0_irq,
+	.enable		= enable_local0_irq,
+	.disable	= disable_local0_irq,
+	.ack		= mask_and_ack_local0_irq,
+	.end		= end_local0_irq,
+};
+
+static void enable_local1_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	/* don't allow mappable interrupt to be enabled from setup_irq,
+	 * we have our own way to do so */
+	if (irq != SGI_MAP_1_IRQ)
+		sgint->imask1 |= (1 << (irq - SGINT_LOCAL1));
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_local1_irq(unsigned int irq)
+{
+	enable_local1_irq(irq);
+	return 0;		/* Never anything pending  */
+}
+
+void disable_local1_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->imask1 &= ~(1 << (irq - SGINT_LOCAL1));
+	local_irq_restore(flags);
+}
+
+#define shutdown_local1_irq	disable_local1_irq
+#define mask_and_ack_local1_irq	disable_local1_irq
+
+static void end_local1_irq (unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		enable_local1_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local1_irq_type = {
+	.typename	= "IP22 local 1",
+	.startup	= startup_local1_irq,
+	.shutdown	= shutdown_local1_irq,
+	.enable		= enable_local1_irq,
+	.disable	= disable_local1_irq,
+	.ack		= mask_and_ack_local1_irq,
+	.end		= end_local1_irq,
+};
+
+static void enable_local2_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+	sgint->cmeimask0 |= (1 << (irq - SGINT_LOCAL2));
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_local2_irq(unsigned int irq)
+{
+	enable_local2_irq(irq);
+	return 0;		/* Never anything pending  */
+}
+
+void disable_local2_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->cmeimask0 &= ~(1 << (irq - SGINT_LOCAL2));
+	if (!sgint->cmeimask0)
+		sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+	local_irq_restore(flags);
+}
+
+#define shutdown_local2_irq disable_local2_irq
+#define mask_and_ack_local2_irq	disable_local2_irq
+
+static void end_local2_irq (unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		enable_local2_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local2_irq_type = {
+	.typename	= "IP22 local 2",
+	.startup	= startup_local2_irq,
+	.shutdown	= shutdown_local2_irq,
+	.enable		= enable_local2_irq,
+	.disable	= disable_local2_irq,
+	.ack		= mask_and_ack_local2_irq,
+	.end		= end_local2_irq,
+};
+
+static void enable_local3_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+	sgint->cmeimask1 |= (1 << (irq - SGINT_LOCAL3));
+	local_irq_restore(flags);
+}
+
+static unsigned int startup_local3_irq(unsigned int irq)
+{
+	enable_local3_irq(irq);
+	return 0;		/* Never anything pending  */
+}
+
+void disable_local3_irq(unsigned int irq)
+{
+	unsigned long flags;
+
+	local_irq_save(flags);
+	sgint->cmeimask1 &= ~(1 << (irq - SGINT_LOCAL3));
+	if (!sgint->cmeimask1)
+		sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+	local_irq_restore(flags);
+}
+
+#define shutdown_local3_irq disable_local3_irq
+#define mask_and_ack_local3_irq	disable_local3_irq
+
+static void end_local3_irq (unsigned int irq)
+{
+	if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+		enable_local3_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local3_irq_type = {
+	.typename	= "IP22 local 3",
+	.startup	= startup_local3_irq,
+	.shutdown	= shutdown_local3_irq,
+	.enable		= enable_local3_irq,
+	.disable	= disable_local3_irq,
+	.ack		= mask_and_ack_local3_irq,
+	.end		= end_local3_irq,
+};
+
+void indy_local0_irqdispatch(struct pt_regs *regs)
+{
+	u8 mask = sgint->istat0 & sgint->imask0;
+	u8 mask2;
+	int irq;
+
+	if (mask & SGINT_ISTAT0_LIO2) {
+		mask2 = sgint->vmeistat & sgint->cmeimask0;
+		irq = lc2msk_to_irqnr[mask2];
+	} else
+		irq = lc0msk_to_irqnr[mask];
+
+	/* if irq == 0, then the interrupt has already been cleared */
+	if (irq)
+		do_IRQ(irq, regs);
+	return;
+}
+
+void indy_local1_irqdispatch(struct pt_regs *regs)
+{
+	u8 mask = sgint->istat1 & sgint->imask1;
+	u8 mask2;
+	int irq;
+
+	if (mask & SGINT_ISTAT1_LIO3) {
+		mask2 = sgint->vmeistat & sgint->cmeimask1;
+		irq = lc3msk_to_irqnr[mask2];
+	} else
+		irq = lc1msk_to_irqnr[mask];
+
+	/* if irq == 0, then the interrupt has already been cleared */
+	if (irq)
+		do_IRQ(irq, regs);
+	return;
+}
+
+extern void ip22_be_interrupt(int irq, struct pt_regs *regs);
+
+void indy_buserror_irq(struct pt_regs *regs)
+{
+	int irq = SGI_BUSERR_IRQ;
+
+	irq_enter();
+	kstat_this_cpu.irqs[irq]++;
+	ip22_be_interrupt(irq, regs);
+	irq_exit();
+}
+
+static struct irqaction local0_cascade = { 
+	.handler	= no_action,
+	.flags		= SA_INTERRUPT,
+	.name		= "local0 cascade",
+};
+
+static struct irqaction local1_cascade = { 
+	.handler	= no_action,
+	.flags		= SA_INTERRUPT,
+	.name		= "local1 cascade",
+};
+
+static struct irqaction buserr = { 
+	.handler	= no_action,
+	.flags		= SA_INTERRUPT,
+	.name		= "Bus Error",
+};
+
+static struct irqaction map0_cascade = { 
+	.handler	= no_action,
+	.flags		= SA_INTERRUPT,
+	.name		= "mapable0 cascade",
+};
+
+#ifdef USE_LIO3_IRQ
+static struct irqaction map1_cascade = { 
+	.handler	= no_action,
+	.flags		= SA_INTERRUPT,
+	.name		= "mapable1 cascade",
+};
+#define SGI_INTERRUPTS	SGINT_END
+#else
+#define SGI_INTERRUPTS	SGINT_LOCAL3
+#endif
+
+extern void mips_cpu_irq_init(unsigned int irq_base);
+
+void __init arch_init_irq(void)
+{
+	int i;
+
+	/* Init local mask --> irq tables. */
+	for (i = 0; i < 256; i++) {
+		if (i & 0x80) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
+		} else if (i & 0x40) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
+		} else if (i & 0x20) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
+		} else if (i & 0x10) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
+		} else if (i & 0x08) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
+		} else if (i & 0x04) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
+		} else if (i & 0x02) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
+		} else if (i & 0x01) {
+			lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
+			lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
+			lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
+			lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
+		} else {
+			lc0msk_to_irqnr[i] = 0;
+			lc1msk_to_irqnr[i] = 0;
+			lc2msk_to_irqnr[i] = 0;
+			lc3msk_to_irqnr[i] = 0;
+		}
+	}
+
+	/* Mask out all interrupts. */
+	sgint->imask0 = 0;
+	sgint->imask1 = 0;
+	sgint->cmeimask0 = 0;
+	sgint->cmeimask1 = 0;
+
+	set_except_vector(0, indyIRQ);
+
+	/* init CPU irqs */
+	mips_cpu_irq_init(SGINT_CPU);
+
+	for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
+		hw_irq_controller *handler;
+
+		if (i < SGINT_LOCAL1)
+			handler		= &ip22_local0_irq_type;
+		else if (i < SGINT_LOCAL2)
+			handler		= &ip22_local1_irq_type;
+		else if (i < SGINT_LOCAL3)
+			handler		= &ip22_local2_irq_type;
+		else
+			handler		= &ip22_local3_irq_type;
+
+		irq_desc[i].status	= IRQ_DISABLED;
+		irq_desc[i].action	= 0;
+		irq_desc[i].depth	= 1;
+		irq_desc[i].handler	= handler;
+	}
+
+	/* vector handler. this register the IRQ as non-sharable */
+	setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade);
+	setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade);
+	setup_irq(SGI_BUSERR_IRQ, &buserr);
+
+	/* cascade in cascade. i love Indy ;-) */
+	setup_irq(SGI_MAP_0_IRQ, &map0_cascade);
+#ifdef USE_LIO3_IRQ
+	setup_irq(SGI_MAP_1_IRQ, &map1_cascade);
+#endif
+
+#ifdef CONFIG_EISA
+	if (ip22_is_fullhouse())	/* Only Indigo-2 has EISA stuff */
+	        ip22_eisa_init ();
+#endif
+}
diff --git a/arch/mips/sgi-ip22/ip22-irq.S b/arch/mips/sgi-ip22/ip22-irq.S
new file mode 100644
index 0000000..6ccbd9e1
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-irq.S
@@ -0,0 +1,118 @@
+/*
+ * ip22-irq.S: Interrupt exception dispatch code for FullHouse and
+ *             Guiness.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+/* A lot of complication here is taken away because:
+ *
+ * 1) We handle one interrupt and return, sitting in a loop and moving across
+ *    all the pending IRQ bits in the cause register is _NOT_ the answer, the
+ *    common case is one pending IRQ so optimize in that direction.
+ *
+ * 2) We need not check against bits in the status register IRQ mask, that
+ *    would make this routine slow as hell.
+ *
+ * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in
+ *    between like BSD spl() brain-damage.
+ *
+ * Furthermore, the IRQs on the INDY look basically (barring software IRQs
+ * which we don't use at all) like:
+ *
+ *	MIPS IRQ	Source
+ *      --------        ------
+ *             0	Software (ignored)
+ *             1        Software (ignored)
+ *             2        Local IRQ level zero
+ *             3        Local IRQ level one
+ *             4        8254 Timer zero
+ *             5        8254 Timer one
+ *             6        Bus Error
+ *             7        R4k timer (what we use)
+ *
+ * We handle the IRQ according to _our_ priority which is:
+ *
+ * Highest ----     R4k Timer
+ *                  Local IRQ zero
+ *                  Local IRQ one
+ *                  Bus Error
+ *                  8254 Timer zero
+ * Lowest  ----     8254 Timer one
+ *
+ * then we just return, if multiple IRQs are pending then we will just take
+ * another exception, big deal.
+ */
+
+	.text
+	.set	noreorder
+	.set	noat
+	.align	5
+	NESTED(indyIRQ, PT_SIZE, sp)
+	SAVE_ALL
+	CLI
+	.set	at
+	mfc0	s0, CP0_CAUSE		# get irq mask
+
+	/* First we check for r4k counter/timer IRQ. */
+	andi	a0, s0, CAUSEF_IP7
+	beq	a0, zero, 1f
+	 andi	a0, s0, CAUSEF_IP2	# delay slot, check local level zero
+
+	/* Wheee, a timer interrupt. */
+	jal	indy_r4k_timer_interrupt
+	 move	a0, sp			# delay slot
+	j	ret_from_irq
+	 nop				# delay slot
+
+1:
+	beq	a0, zero, 1f
+	 andi	a0, s0, CAUSEF_IP3	# delay slot, check local level one
+
+	/* Wheee, local level zero interrupt. */
+	jal	indy_local0_irqdispatch
+	 move	a0, sp			# delay slot
+
+	j	ret_from_irq
+	 nop				# delay slot
+
+1:
+	beq	a0, zero, 1f
+	 andi	a0, s0, CAUSEF_IP6	# delay slot, check bus error
+
+	/* Wheee, local level one interrupt. */
+	jal	indy_local1_irqdispatch
+	 move	a0, sp			# delay slot
+	j	ret_from_irq
+	 nop				# delay slot
+
+1:
+	beq	a0, zero, 1f
+	 andi	a0, s0, (CAUSEF_IP4 | CAUSEF_IP5)	# delay slot
+
+	/* Wheee, an asynchronous bus error... */
+	jal	indy_buserror_irq
+	 move	a0, sp			# delay slot
+	j	ret_from_irq
+	 nop				# delay slot
+
+1:
+	/* Here by mistake? It is possible, that by the time we take
+	 * the exception the IRQ pin goes low, so just leave if this
+	 * is the case.
+	 */
+	beq	a0, zero, 1f
+	 nop			  	# delay slot
+
+	/* Must be one of the 8254 timers... */
+	jal	indy_8254timer_irq
+	 move	a0, sp			# delay slot
+1:
+	j	ret_from_irq
+	 nop				# delay slot
+	END(indyIRQ)
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
new file mode 100644
index 0000000..b58bd52
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -0,0 +1,208 @@
+/*
+ * ip22-mc.c: Routines for manipulating SGI Memory Controller.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
+ * Copyright (C) 2003 Ladislav Michl  (ladis@linux-mips.org)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+struct sgimc_regs *sgimc;
+
+EXPORT_SYMBOL(sgimc);
+
+static inline unsigned long get_bank_addr(unsigned int memconfig)
+{
+	return ((memconfig & SGIMC_MCONFIG_BASEADDR) <<
+		((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 24 : 22));
+}
+
+static inline unsigned long get_bank_size(unsigned int memconfig)
+{
+	return ((memconfig & SGIMC_MCONFIG_RMASK) + 0x0100) <<
+		((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 16 : 14);
+}
+
+static inline unsigned int get_bank_config(int bank)
+{
+	unsigned int res = bank > 1 ? sgimc->mconfig1 : sgimc->mconfig0;
+	return bank % 2 ? res & 0xffff : res >> 16;
+}
+
+struct mem {
+	unsigned long addr;
+	unsigned long size;
+};
+
+/*
+ * Detect installed memory, do some sanity checks and notify kernel about it
+ */
+static void probe_memory(void)
+{
+	int i, j, found, cnt = 0;
+	struct mem bank[4];
+	struct mem space[2] = {{SGIMC_SEG0_BADDR, 0}, {SGIMC_SEG1_BADDR, 0}};
+
+	printk(KERN_INFO "MC: Probing memory configuration:\n");
+	for (i = 0; i < ARRAY_SIZE(bank); i++) {
+		unsigned int tmp = get_bank_config(i);
+		if (!(tmp & SGIMC_MCONFIG_BVALID))
+			continue;
+
+		bank[cnt].size = get_bank_size(tmp);
+		bank[cnt].addr = get_bank_addr(tmp);
+		printk(KERN_INFO " bank%d: %3ldM @ %08lx\n",
+			i, bank[cnt].size / 1024 / 1024, bank[cnt].addr);
+		cnt++;
+	}
+
+	/* And you thought bubble sort is dead algorithm... */
+	do {
+		unsigned long addr, size;
+
+		found = 0;
+		for (i = 1; i < cnt; i++)
+			if (bank[i-1].addr > bank[i].addr) {
+				addr = bank[i].addr;
+				size = bank[i].size;
+				bank[i].addr = bank[i-1].addr;
+				bank[i].size = bank[i-1].size;
+				bank[i-1].addr = addr;
+				bank[i-1].size = size;
+				found = 1;
+			}
+	} while (found);
+
+	/* Figure out how are memory banks mapped into spaces */
+	for (i = 0; i < cnt; i++) {
+		found = 0;
+		for (j = 0; j < ARRAY_SIZE(space) && !found; j++)
+			if (space[j].addr + space[j].size == bank[i].addr) {
+				space[j].size += bank[i].size;
+				found = 1;
+			}
+		/* There is either hole or overlapping memory */
+		if (!found)
+			printk(KERN_CRIT "MC: Memory configuration mismatch "
+					 "(%08lx), expect Bus Error soon\n",
+					 bank[i].addr);
+	}
+
+	for (i = 0; i < ARRAY_SIZE(space); i++)
+		if (space[i].size)
+			add_memory_region(space[i].addr, space[i].size,
+					  BOOT_MEM_RAM);
+}
+
+void __init sgimc_init(void)
+{
+	u32 tmp;
+
+	/* ioremap can't fail */
+	sgimc = (struct sgimc_regs *)
+		ioremap(SGIMC_BASE, sizeof(struct sgimc_regs));
+
+	printk(KERN_INFO "MC: SGI memory controller Revision %d\n",
+	       (int) sgimc->systemid & SGIMC_SYSID_MASKREV);
+
+	/* Place the MC into a known state.  This must be done before
+	 * interrupts are first enabled etc.
+	 */
+
+	/* Step 0: Make sure we turn off the watchdog in case it's
+	 *         still running (which might be the case after a
+	 *         soft reboot).
+	 */
+	tmp = sgimc->cpuctrl0;
+	tmp &= ~SGIMC_CCTRL0_WDOG;
+	sgimc->cpuctrl0 = tmp;
+
+	/* Step 1: The CPU/GIO error status registers will not latch
+	 *         up a new error status until the register has been
+	 *         cleared by the cpu.  These status registers are
+	 *         cleared by writing any value to them.
+	 */
+	sgimc->cstat = sgimc->gstat = 0;
+
+	/* Step 2: Enable all parity checking in cpu control register
+	 *         zero.
+	 */
+	tmp = sgimc->cpuctrl0;
+	tmp |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
+		SGIMC_CCTRL0_R4KNOCHKPARR);
+	sgimc->cpuctrl0 = tmp;
+
+	/* Step 3: Setup the MC write buffer depth, this is controlled
+	 *         in cpu control register 1 in the lower 4 bits.
+	 */
+	tmp = sgimc->cpuctrl1;
+	tmp &= ~0xf;
+	tmp |= 0xd;
+	sgimc->cpuctrl1 = tmp;
+
+	/* Step 4: Initialize the RPSS divider register to run as fast
+	 *         as it can correctly operate.  The register is laid
+	 *         out as follows:
+	 *
+	 *         ----------------------------------------
+	 *         |  RESERVED  |   INCREMENT   | DIVIDER |
+	 *         ----------------------------------------
+	 *          31        16 15            8 7       0
+	 *
+	 *         DIVIDER determines how often a 'tick' happens,
+	 *         INCREMENT determines by how the RPSS increment
+	 *         registers value increases at each 'tick'. Thus,
+	 *         for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101
+	 */
+	sgimc->divider = 0x101;
+
+	/* Step 5: Initialize GIO64 arbitrator configuration register.
+	 *
+	 * NOTE: HPC init code in sgihpc_init() must run before us because
+	 *       we need to know Guiness vs. FullHouse and the board
+	 *       revision on this machine. You have been warned.
+	 */
+
+	/* First the basic invariants across all GIO64 implementations. */
+	tmp = SGIMC_GIOPAR_HPC64;	/* All 1st HPC's interface at 64bits */
+	tmp |= SGIMC_GIOPAR_ONEBUS;	/* Only one physical GIO bus exists */
+
+	if (ip22_is_fullhouse()) {
+		/* Fullhouse specific settings. */
+		if (SGIOC_SYSID_BOARDREV(sgioc->sysid) < 2) {
+			tmp |= SGIMC_GIOPAR_HPC264;	/* 2nd HPC at 64bits */
+			tmp |= SGIMC_GIOPAR_PLINEEXP0;	/* exp0 pipelines */
+			tmp |= SGIMC_GIOPAR_MASTEREXP1;	/* exp1 masters */
+			tmp |= SGIMC_GIOPAR_RTIMEEXP0;	/* exp0 is realtime */
+		} else {
+			tmp |= SGIMC_GIOPAR_HPC264;	/* 2nd HPC 64bits */
+			tmp |= SGIMC_GIOPAR_PLINEEXP0;	/* exp[01] pipelined */
+			tmp |= SGIMC_GIOPAR_PLINEEXP1;
+			tmp |= SGIMC_GIOPAR_MASTEREISA;	/* EISA masters */
+			tmp |= SGIMC_GIOPAR_GFX64;	/* GFX at 64 bits */
+		}
+	} else {
+		/* Guiness specific settings. */
+		tmp |= SGIMC_GIOPAR_EISA64;	/* MC talks to EISA at 64bits */
+		tmp |= SGIMC_GIOPAR_MASTEREISA;	/* EISA bus can act as master */
+	}
+	sgimc->giopar = tmp;	/* poof */
+
+	probe_memory();
+}
+
+void __init prom_meminit(void) {}
+unsigned long __init prom_free_prom_memory(void)
+{
+	return 0;
+}
diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c
new file mode 100644
index 0000000..de43e86
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-nvram.c
@@ -0,0 +1,119 @@
+/*
+ * ip22-nvram.c: NVRAM and serial EEPROM handling.
+ *
+ * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+#include <linux/module.h>
+
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/* Control opcode for serial eeprom  */
+#define EEPROM_READ	0xc000	/* serial memory read */
+#define EEPROM_WEN	0x9800	/* write enable before prog modes */
+#define EEPROM_WRITE	0xa000	/* serial memory write */
+#define EEPROM_WRALL	0x8800	/* write all registers */
+#define EEPROM_WDS	0x8000	/* disable all programming */
+#define	EEPROM_PRREAD	0xc000	/* read protect register */
+#define	EEPROM_PREN	0x9800	/* enable protect register mode */
+#define	EEPROM_PRCLEAR	0xffff	/* clear protect register */
+#define	EEPROM_PRWRITE	0xa000	/* write protect register */
+#define	EEPROM_PRDS	0x8000	/* disable protect register, forever */
+
+#define EEPROM_EPROT	0x01	/* Protect register enable */
+#define EEPROM_CSEL	0x02	/* Chip select */
+#define EEPROM_ECLK	0x04	/* EEPROM clock */
+#define EEPROM_DATO	0x08	/* Data out */
+#define EEPROM_DATI	0x10	/* Data in */
+
+/* We need to use these functions early... */
+#define delay()	({						\
+	int x;							\
+	for (x=0; x<100000; x++) __asm__ __volatile__(""); })
+
+#define eeprom_cs_on(ptr) ({	\
+	*ptr &= ~EEPROM_DATO;	\
+	*ptr &= ~EEPROM_ECLK;	\
+	*ptr &= ~EEPROM_EPROT;	\
+	delay();		\
+	*ptr |= EEPROM_CSEL;	\
+	*ptr |= EEPROM_ECLK; })
+
+		
+#define eeprom_cs_off(ptr) ({	\
+	*ptr &= ~EEPROM_ECLK;	\
+	*ptr &= ~EEPROM_CSEL;	\
+	*ptr |= EEPROM_EPROT;	\
+	*ptr |= EEPROM_ECLK; })
+
+#define	BITS_IN_COMMAND	11
+/*
+ * clock in the nvram command and the register number. For the
+ * national semiconductor nv ram chip the op code is 3 bits and
+ * the address is 6/8 bits. 
+ */
+static inline void eeprom_cmd(volatile unsigned int *ctrl, unsigned cmd,
+			      unsigned reg)
+{
+	unsigned short ser_cmd;
+	int i;
+
+	ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND));
+	for (i = 0; i < BITS_IN_COMMAND; i++) {
+		if (ser_cmd & (1<<15))	/* if high order bit set */
+			*ctrl |= EEPROM_DATO;
+		else
+			*ctrl &= ~EEPROM_DATO;
+		*ctrl &= ~EEPROM_ECLK;
+		*ctrl |= EEPROM_ECLK;
+		ser_cmd <<= 1;
+	}
+	*ctrl &= ~EEPROM_DATO;	/* see data sheet timing diagram */
+}
+
+unsigned short ip22_eeprom_read(volatile unsigned int *ctrl, int reg)
+{
+	unsigned short res = 0;
+	int i;
+
+	*ctrl &= ~EEPROM_EPROT;
+	eeprom_cs_on(ctrl);
+	eeprom_cmd(ctrl, EEPROM_READ, reg);
+
+	/* clock the data ouf of serial mem */
+	for (i = 0; i < 16; i++) {
+		*ctrl &= ~EEPROM_ECLK;
+		delay();
+		*ctrl |= EEPROM_ECLK;
+		delay();
+		res <<= 1;
+		if (*ctrl & EEPROM_DATI)
+			res |= 1;
+	}
+		
+	eeprom_cs_off(ctrl);
+
+	return res;
+}
+
+EXPORT_SYMBOL(ip22_eeprom_read);
+
+/*
+ * Read specified register from main NVRAM
+ */
+unsigned short ip22_nvram_read(int reg)
+{
+	if (ip22_is_fullhouse())
+		/* IP22 (Indigo2 aka FullHouse) stores env variables into
+		 * 93CS56 Microwire Bus EEPROM 2048 Bit (128x16) */
+		return ip22_eeprom_read(&hpc3c0->eeprom, reg);
+	else {
+		unsigned short tmp;
+		/* IP24 (Indy aka Guiness) uses DS1386 8K version */
+		reg <<= 1;
+		tmp = hpc3c0->bbram[reg++] & 0xff;
+		return (tmp << 8) | (hpc3c0->bbram[reg] & 0xff);
+	}		
+}
+
+EXPORT_SYMBOL(ip22_nvram_read);
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c
new file mode 100644
index 0000000..ed5c60a
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-reset.c
@@ -0,0 +1,247 @@
+/*
+ * 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) 1997, 1998, 2001, 2003 by Ralf Baechle
+ */
+#include <linux/init.h>
+#include <linux/ds1286.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/timer.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/reboot.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds.
+ * I'm not sure if this feature is a good idea, for now it's here just to
+ * make the power button make behave just like under IRIX.
+ */
+#define POWERDOWN_TIMEOUT	120
+
+/*
+ * Blink frequency during reboot grace period and when paniced.
+ */
+#define POWERDOWN_FREQ		(HZ / 4)
+#define PANIC_FREQ		(HZ / 8)
+
+static struct timer_list power_timer, blink_timer, debounce_timer, volume_timer;
+
+#define MACHINE_PANICED		1
+#define MACHINE_SHUTTING_DOWN	2
+static int machine_state = 0;
+
+static void sgi_machine_restart(char *command) __attribute__((noreturn));
+static void sgi_machine_halt(void) __attribute__((noreturn));
+static void sgi_machine_power_off(void) __attribute__((noreturn));
+
+static void sgi_machine_restart(char *command)
+{
+	if (machine_state & MACHINE_SHUTTING_DOWN)
+		sgi_machine_power_off();
+	sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
+	while (1);
+}
+
+static void sgi_machine_halt(void)
+{
+	if (machine_state & MACHINE_SHUTTING_DOWN)
+		sgi_machine_power_off();
+	ArcEnterInteractiveMode();
+}
+
+static void sgi_machine_power_off(void)
+{
+	unsigned int tmp;
+
+	local_irq_disable();
+
+	/* Disable watchdog */
+	tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff;
+	hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM;
+	hpc3c0->rtcregs[RTC_WSEC] = 0;
+	hpc3c0->rtcregs[RTC_WHSEC] = 0;
+
+	while (1) {
+		sgioc->panel = ~SGIOC_PANEL_POWERON;
+		/* Good bye cruel world ...  */
+
+		/* If we're still running, we probably got sent an alarm
+		   interrupt.  Read the flag to clear it.  */
+		tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM];
+	}
+}
+
+static void power_timeout(unsigned long data)
+{
+	sgi_machine_power_off();
+}
+
+static void blink_timeout(unsigned long data)
+{
+	/* XXX fix this for fullhouse  */
+	sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
+	sgioc->reset = sgi_ioc_reset;
+
+	mod_timer(&blink_timer, jiffies+data);
+}
+
+static void debounce(unsigned long data)
+{
+	del_timer(&debounce_timer);
+	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+		/* Interrupt still being sent. */
+		debounce_timer.expires = jiffies + 5; /* 0.05s  */
+		add_timer(&debounce_timer);
+
+		sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
+			       SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
+			       SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
+
+		return;
+	}
+
+	if (machine_state & MACHINE_PANICED)
+		sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
+
+	enable_irq(SGI_PANEL_IRQ);
+}
+
+static inline void power_button(void)
+{
+	if (machine_state & MACHINE_PANICED)
+		return;
+
+	if ((machine_state & MACHINE_SHUTTING_DOWN) || kill_proc(1,SIGINT,1)) {
+		/* No init process or button pressed twice.  */
+		sgi_machine_power_off();
+	}
+
+	machine_state |= MACHINE_SHUTTING_DOWN;
+	blink_timer.data = POWERDOWN_FREQ;
+	blink_timeout(POWERDOWN_FREQ);
+
+	init_timer(&power_timer);
+	power_timer.function = power_timeout;
+	power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
+	add_timer(&power_timer);
+}
+
+void (*indy_volume_button)(int) = NULL;
+
+EXPORT_SYMBOL(indy_volume_button);
+
+static inline void volume_up_button(unsigned long data)
+{
+	del_timer(&volume_timer);
+
+	if (indy_volume_button)
+		indy_volume_button(1);
+
+	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+		volume_timer.expires = jiffies + 1;
+		add_timer(&volume_timer);
+	}
+}
+
+static inline void volume_down_button(unsigned long data)
+{
+	del_timer(&volume_timer);
+
+	if (indy_volume_button)
+		indy_volume_button(-1);
+
+	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+		volume_timer.expires = jiffies + 1;
+		add_timer(&volume_timer);
+	}
+}
+
+static irqreturn_t panel_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned int buttons;
+
+	buttons = sgioc->panel;
+	sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
+
+	if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+		/* Wait until interrupt goes away */
+		disable_irq(SGI_PANEL_IRQ);
+		init_timer(&debounce_timer);
+		debounce_timer.function = debounce;
+		debounce_timer.expires = jiffies + 5;
+		add_timer(&debounce_timer);
+	}
+
+	/* Power button was pressed 
+	 * ioc.ps page 22: "The Panel Register is called Power Control by Full
+	 * House. Only lowest 2 bits are used. Guiness uses upper four bits
+	 * for volume control". This is not true, all bits are pulled high
+	 * on fullhouse */
+	if (ip22_is_fullhouse() || !(buttons & SGIOC_PANEL_POWERINTR)) {
+		power_button();
+		return IRQ_HANDLED;
+	}
+	/* TODO: mute/unmute */
+	/* Volume up button was pressed */
+	if (!(buttons & SGIOC_PANEL_VOLUPINTR)) {
+		init_timer(&volume_timer);
+		volume_timer.function = volume_up_button;
+		volume_timer.expires = jiffies + 1;
+		add_timer(&volume_timer);
+	}
+	/* Volume down button was pressed */
+	if (!(buttons & SGIOC_PANEL_VOLDNINTR)) {
+		init_timer(&volume_timer);
+		volume_timer.function = volume_down_button;
+		volume_timer.expires = jiffies + 1;
+		add_timer(&volume_timer);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+                      void *ptr)
+{
+	if (machine_state & MACHINE_PANICED)
+		return NOTIFY_DONE;
+	machine_state |= MACHINE_PANICED;
+
+	blink_timer.data = PANIC_FREQ;
+	blink_timeout(PANIC_FREQ);
+
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+	.notifier_call	= panic_event,
+};
+
+static int __init reboot_setup(void)
+{
+	_machine_restart = sgi_machine_restart;
+	_machine_halt = sgi_machine_halt;
+	_machine_power_off = sgi_machine_power_off;
+
+	request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL);
+	init_timer(&blink_timer);
+	blink_timer.function = blink_timeout;
+	notifier_chain_register(&panic_notifier_list, &panic_block);
+
+	return 0;
+}
+
+subsys_initcall(reboot_setup);
diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c
new file mode 100644
index 0000000..0e96a5d
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-setup.c
@@ -0,0 +1,144 @@
+/*
+ * ip22-setup.c: SGI specific setup, including init of the feature struct.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
+ */
+#include <linux/config.h>
+#include <linux/ds1286.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/console.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <asm/addrspace.h>
+#include <asm/bcache.h>
+#include <asm/bootinfo.h>
+#include <asm/irq.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/gdb-stub.h>
+#include <asm/io.h>
+#include <asm/traps.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+unsigned long sgi_gfxaddr;
+
+/*
+ * Stop-A is originally a Sun thing that isn't standard on IP22 so to avoid
+ * accidents it's disabled by default on IP22.
+ *
+ * FIXME: provide a mechanism to change the value of stop_a_enabled.
+ */
+int stop_a_enabled;
+
+void ip22_do_break(void)
+{
+	if (!stop_a_enabled)
+		return;
+
+	printk("\n");
+	ArcEnterInteractiveMode();
+}
+
+EXPORT_SYMBOL(ip22_do_break);
+
+extern void ip22_be_init(void) __init;
+extern void ip22_time_init(void) __init;
+
+static int __init ip22_setup(void)
+{
+	char *ctype;
+
+	board_be_init = ip22_be_init;
+	ip22_time_init();
+
+	/* Init the INDY HPC I/O controller.  Need to call this before
+	 * fucking with the memory controller because it needs to know the
+	 * boardID and whether this is a Guiness or a FullHouse machine.
+	 */
+	sgihpc_init();
+
+	/* Init INDY memory controller. */
+	sgimc_init();
+
+#ifdef CONFIG_BOARD_SCACHE
+	/* Now enable boardcaches, if any. */
+	indy_sc_init();
+#endif
+
+	/* Set EISA IO port base for Indigo2
+	 * ioremap cannot fail */
+	set_io_port_base((unsigned long)ioremap(0x00080000,
+						0x1fffffff - 0x00080000));
+	/* ARCS console environment variable is set to "g?" for
+	 * graphics console, it is set to "d" for the first serial
+	 * line and "d2" for the second serial line.
+	 */
+	ctype = ArcGetEnvironmentVariable("console");
+	if (ctype && *ctype == 'd') {
+		static char options[8];
+		char *baud = ArcGetEnvironmentVariable("dbaud");
+		if (baud)
+			strcpy(options, baud);
+		add_preferred_console("ttyS", *(ctype + 1) == '2' ? 1 : 0,
+				      baud ? options : NULL);
+	} else if (!ctype || *ctype != 'g') {
+		/* Use ARC if we don't want serial ('d') or Newport ('g'). */
+		prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
+		add_preferred_console("arc", 0, NULL);
+	}
+
+#ifdef CONFIG_KGDB
+	{
+	char *kgdb_ttyd = prom_getcmdline();
+
+	if ((kgdb_ttyd = strstr(kgdb_ttyd, "kgdb=ttyd")) != NULL) {
+		int line;
+		kgdb_ttyd += strlen("kgdb=ttyd");
+		if (*kgdb_ttyd != '1' && *kgdb_ttyd != '2')
+			printk(KERN_INFO "KGDB: Uknown serial line /dev/ttyd%c"
+			       ", falling back to /dev/ttyd1\n", *kgdb_ttyd);
+		line = *kgdb_ttyd == '2' ? 0 : 1;
+		printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
+		       "session\n", line ? 1 : 2);
+		rs_kgdb_hook(line);
+
+		printk(KERN_INFO "KGDB: Using serial line /dev/ttyd%d for "
+		       "session, please connect your debugger\n", line ? 1:2);
+
+		kgdb_enabled = 1;
+		/* Breakpoints and stuff are in sgi_irq_setup() */
+	}
+	}
+#endif
+
+#if defined(CONFIG_VT) && defined(CONFIG_SGI_NEWPORT_CONSOLE)
+	{
+		ULONG *gfxinfo;
+		ULONG * (*__vec)(void) = (void *) (long)
+			*((_PULONG *)(long)((PROMBLOCK)->pvector + 0x20));
+
+		gfxinfo = __vec();
+		sgi_gfxaddr = ((gfxinfo[1] >= 0xa0000000
+			       && gfxinfo[1] <= 0xc0000000)
+			       ? gfxinfo[1] - 0xa0000000 : 0);
+
+		/* newport addresses? */
+		if (sgi_gfxaddr == 0x1f0f0000 || sgi_gfxaddr == 0x1f4f0000) {
+			conswitchp = &newport_con;
+		}
+	}
+#endif
+
+	return 0;
+}
+
+early_initcall(ip22_setup);
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c
new file mode 100644
index 0000000..173f768
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-time.c
@@ -0,0 +1,214 @@
+/*
+ * 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.
+ *
+ * Time operations for IP22 machines. Original code may come from
+ * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure)
+ *
+ * Copyright (C) 2001 by Ladislav Michl
+ * Copyright (C) 2003 Ralf Baechle (ralf@linux-mips.org)
+ */
+#include <linux/bcd.h>
+#include <linux/ds1286.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/time.h>
+
+#include <asm/cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/time.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * note that mktime uses month from 1 to 12 while to_tm
+ * uses 0 to 11.
+ */
+static unsigned long indy_rtc_get_time(void)
+{
+	unsigned int yrs, mon, day, hrs, min, sec;
+	unsigned int save_control;
+
+	save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
+	hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
+
+	sec = BCD2BIN(hpc3c0->rtcregs[RTC_SECONDS] & 0xff);
+	min = BCD2BIN(hpc3c0->rtcregs[RTC_MINUTES] & 0xff);
+	hrs = BCD2BIN(hpc3c0->rtcregs[RTC_HOURS] & 0x3f);
+	day = BCD2BIN(hpc3c0->rtcregs[RTC_DATE] & 0xff);
+	mon = BCD2BIN(hpc3c0->rtcregs[RTC_MONTH] & 0x1f);
+	yrs = BCD2BIN(hpc3c0->rtcregs[RTC_YEAR] & 0xff);
+
+	hpc3c0->rtcregs[RTC_CMD] = save_control;
+
+	if (yrs < 45)
+		yrs += 30;
+	if ((yrs += 40) < 70)
+		yrs += 100;
+
+	return mktime(yrs + 1900, mon, day, hrs, min, sec);
+}
+
+static int indy_rtc_set_time(unsigned long tim)
+{
+	struct rtc_time tm;
+	unsigned int save_control;
+
+	to_tm(tim, &tm);
+
+	tm.tm_mon += 1;		/* tm_mon starts at zero */
+	tm.tm_year -= 1940;
+	if (tm.tm_year >= 100)
+		tm.tm_year -= 100;
+
+	save_control = hpc3c0->rtcregs[RTC_CMD] & 0xff;
+	hpc3c0->rtcregs[RTC_CMD] = save_control | RTC_TE;
+
+	hpc3c0->rtcregs[RTC_YEAR] = BIN2BCD(tm.tm_sec);
+	hpc3c0->rtcregs[RTC_MONTH] = BIN2BCD(tm.tm_mon);
+	hpc3c0->rtcregs[RTC_DATE] = BIN2BCD(tm.tm_mday);
+	hpc3c0->rtcregs[RTC_HOURS] = BIN2BCD(tm.tm_hour);
+	hpc3c0->rtcregs[RTC_MINUTES] = BIN2BCD(tm.tm_min);
+	hpc3c0->rtcregs[RTC_SECONDS] = BIN2BCD(tm.tm_sec);
+	hpc3c0->rtcregs[RTC_HUNDREDTH_SECOND] = 0;
+
+	hpc3c0->rtcregs[RTC_CMD] = save_control;
+
+	return 0;
+}
+
+static unsigned long dosample(void)
+{
+	u32 ct0, ct1;
+	volatile u8 msb, lsb;
+
+	/* Start the counter. */
+	sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
+			 SGINT_TCWORD_MRGEN);
+	sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff;
+	sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8;
+
+	/* Get initial counter invariant */
+	ct0 = read_c0_count();
+
+	/* Latch and spin until top byte of counter2 is zero */
+	do {
+		sgint->tcword = SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT;
+		lsb = sgint->tcnt2;
+		msb = sgint->tcnt2;
+		ct1 = read_c0_count();
+	} while (msb);
+
+	/* Stop the counter. */
+	sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
+			 SGINT_TCWORD_MSWST);
+	/*
+	 * Return the difference, this is how far the r4k counter increments
+	 * for every 1/HZ seconds. We round off the nearest 1 MHz of master
+	 * clock (= 1000000 / HZ / 2).
+	 */
+	/*return (ct1 - ct0 + (500000/HZ/2)) / (500000/HZ) * (500000/HZ);*/
+	return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
+}
+
+/*
+ * Here we need to calibrate the cycle counter to at least be close.
+ */
+static __init void indy_time_init(void)
+{
+	unsigned long r4k_ticks[3];
+	unsigned long r4k_tick;
+
+	/* 
+	 * Figure out the r4k offset, the algorithm is very simple and works in
+	 * _all_ cases as long as the 8254 counter register itself works ok (as
+	 * an interrupt driving timer it does not because of bug, this is why
+	 * we are using the onchip r4k counter/compare register to serve this
+	 * purpose, but for r4k_offset calculation it will work ok for us).
+	 * There are other very complicated ways of performing this calculation
+	 * but this one works just fine so I am not going to futz around. ;-)
+	 */
+	printk(KERN_INFO "Calibrating system timer... ");
+	dosample();	/* Prime cache. */
+	dosample();	/* Prime cache. */
+	/* Zero is NOT an option. */
+	do {
+		r4k_ticks[0] = dosample();
+	} while (!r4k_ticks[0]);
+	do {
+		r4k_ticks[1] = dosample();
+	} while (!r4k_ticks[1]);
+
+	if (r4k_ticks[0] != r4k_ticks[1]) {
+		printk("warning: timer counts differ, retrying... ");
+		r4k_ticks[2] = dosample();
+		if (r4k_ticks[2] == r4k_ticks[0]
+		    || r4k_ticks[2] == r4k_ticks[1])
+			r4k_tick = r4k_ticks[2];
+		else {
+			printk("disagreement, using average... ");
+			r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
+				   + r4k_ticks[2]) / 3;
+		}
+	} else
+		r4k_tick = r4k_ticks[0];
+
+	printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick,
+		(int) (r4k_tick / (500000 / HZ)),
+		(int) (r4k_tick % (500000 / HZ)));
+
+	mips_hpt_frequency = r4k_tick * HZ;
+}
+
+/* Generic SGI handler for (spurious) 8254 interrupts */
+void indy_8254timer_irq(struct pt_regs *regs)
+{
+	int irq = SGI_8254_0_IRQ;
+	ULONG cnt;
+	char c;
+
+	irq_enter();
+	kstat_this_cpu.irqs[irq]++;
+	printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
+	ArcRead(0, &c, 1, &cnt);
+	ArcEnterInteractiveMode();
+	irq_exit();
+}
+
+void indy_r4k_timer_interrupt(struct pt_regs *regs)
+{
+	int irq = SGI_TIMER_IRQ;
+
+	irq_enter();
+	kstat_this_cpu.irqs[irq]++;
+	timer_interrupt(irq, NULL, regs);
+	irq_exit();
+}
+
+extern int setup_irq(unsigned int irq, struct irqaction *irqaction);
+
+static void indy_timer_setup(struct irqaction *irq)
+{
+	/* over-write the handler, we use our own way */
+	irq->handler = no_action;
+
+	/* setup irqaction */
+	setup_irq(SGI_TIMER_IRQ, irq);
+}
+
+void __init ip22_time_init(void)
+{
+	/* setup hookup functions */
+	rtc_get_time = indy_rtc_get_time;
+	rtc_set_time = indy_rtc_set_time;
+
+	board_time_init = indy_time_init;
+	board_timer_setup = indy_timer_setup;
+}