summaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'arch/mips/sgi/kernel')
-rw-r--r--arch/mips/sgi/kernel/.cvsignore1
-rw-r--r--arch/mips/sgi/kernel/Makefile30
-rw-r--r--arch/mips/sgi/kernel/indyIRQ.S138
-rw-r--r--arch/mips/sgi/kernel/indy_hpc.c114
-rw-r--r--arch/mips/sgi/kernel/indy_int.c579
-rw-r--r--arch/mips/sgi/kernel/indy_mc.c153
-rw-r--r--arch/mips/sgi/kernel/indy_timer.c298
-rw-r--r--arch/mips/sgi/kernel/reset.c14
-rw-r--r--arch/mips/sgi/kernel/setup.c83
-rw-r--r--arch/mips/sgi/kernel/system.c168
-rw-r--r--arch/mips/sgi/kernel/time.c14
11 files changed, 1592 insertions, 0 deletions
diff --git a/arch/mips/sgi/kernel/.cvsignore b/arch/mips/sgi/kernel/.cvsignore
new file mode 100644
index 000000000..4671378ae
--- /dev/null
+++ b/arch/mips/sgi/kernel/.cvsignore
@@ -0,0 +1 @@
+.depend
diff --git a/arch/mips/sgi/kernel/Makefile b/arch/mips/sgi/kernel/Makefile
new file mode 100644
index 000000000..c79023b23
--- /dev/null
+++ b/arch/mips/sgi/kernel/Makefile
@@ -0,0 +1,30 @@
+# $Id: Makefile,v 1.5 1996/06/08 12:08:38 dm Exp $
+# Makefile for the SGI specific kernel interface routines
+# under Linux.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+.S.s:
+ $(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+ $(CC) $(CFLAGS) -c $< -o $*.o
+
+OBJS = indy_mc.o indy_hpc.o indy_int.o system.o indy_timer.o indyIRQ.o \
+ reset.o setup.o time.o
+
+all: sgikern.a
+
+sgikern.a: $(OBJS)
+ $(AR) rcs sgikern.a $(OBJS)
+ sync
+
+indyIRQ.o: indyIRQ.S
+
+dep:
+ $(CPP) -M *.c > .depend
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/mips/sgi/kernel/indyIRQ.S b/arch/mips/sgi/kernel/indyIRQ.S
new file mode 100644
index 000000000..a7058d3ee
--- /dev/null
+++ b/arch/mips/sgi/kernel/indyIRQ.S
@@ -0,0 +1,138 @@
+/* $Id: indyIRQ.S,v 1.5 1996/06/29 12:41:12 dm Exp $
+ * indyIRQ.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/mipsconfig.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
+ lui s3, %hi(intr_count)
+
+ /* 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. */
+ lw s7, %lo(intr_count)(s3)
+ move a0, sp
+ addiu t0, s7, 1
+ jal indy_timer_interrupt
+ sw t0, %lo(intr_count)(s3) # delay slot, set intr_count
+
+ j ret_from_sys_call
+ sw s7, %lo(intr_count)(s3) # delay slot, restore intr_count
+
+1:
+ beq a0, zero, 1f
+ andi a0, s0, CAUSEF_IP3 # delay slot, check local level one
+
+ /* Wheee, local level zero interrupt. */
+ lw s7, %lo(intr_count)(s3)
+ move a0, sp
+ addiu t0, s7, 1
+
+ jal indy_local0_irqdispatch
+ sw t0, %lo(intr_count)(s3)
+
+ j ret_from_sys_call
+ sw s7, %lo(intr_count)(s3) # delay slot, restore intr_count
+
+1:
+ beq a0, zero, 1f
+ andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
+
+ /* Wheee, local level one interrupt. */
+ lw s7, %lo(intr_count)(s3)
+ move a0, sp
+ addiu t0, s7, 1
+ jal indy_local1_irqdispatch
+ sw t0, %lo(intr_count)(s3)
+
+ j ret_from_sys_call
+ sw s7, %lo(intr_count)(s3)
+
+1:
+ beq a0, zero, 1f
+ lw s7, %lo(intr_count)(s3)
+
+ /* Wheee, an asynchronous bus error... */
+ addiu t0, s7, 1
+ move a0, sp
+ jal indy_buserror_irq
+ sw t0, %lo(intr_count)(s3)
+
+ j ret_from_sys_call
+ sw s7, %lo(intr_count)(s3)
+
+1:
+ /* Here by mistake? This is possible, what can happen
+ * is that by the time we take the exception the IRQ
+ * pin goes low, so just leave if this is the case.
+ */
+ andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5)
+ beq a0, zero, 1f
+ addiu t0, s7, 1
+
+ /* Must be one of the 8254 timers... */
+ move a0, sp
+ jal indy_8254timer_irq
+ sw t0, %lo(intr_count)(s3)
+1:
+ j ret_from_sys_call
+ sw s7, %lo(intr_count)(s3)
+ END(indyIRQ)
diff --git a/arch/mips/sgi/kernel/indy_hpc.c b/arch/mips/sgi/kernel/indy_hpc.c
new file mode 100644
index 000000000..30f3fe36b
--- /dev/null
+++ b/arch/mips/sgi/kernel/indy_hpc.c
@@ -0,0 +1,114 @@
+/* $Id: indy_hpc.c,v 1.4 1996/06/29 07:06:50 dm Exp $
+ * indy_hpc.c: Routines for generic manipulation of the HPC controllers.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/sgihpc.h>
+#include <asm/sgint23.h>
+#include <asm/sgialib.h>
+
+/* #define DEBUG_SGIHPC */
+
+struct hpc3_regs *hpc3c0, *hpc3c1;
+struct hpc3_miscregs *hpc3mregs;
+
+/* We need software copies of these because they are write only. */
+static unsigned long write1, write2;
+
+/* Machine specific identifier knobs. */
+int sgi_has_ioc2 = 0;
+int sgi_guiness = 0;
+int sgi_boardid;
+
+void sgihpc_write1_modify(int set, int clear)
+{
+ write1 |= set;
+ write1 &= ~clear;
+ hpc3mregs->write1 = write1;
+}
+
+void sgihpc_write2_modify(int set, int clear)
+{
+ write2 |= set;
+ write2 &= ~clear;
+ hpc3mregs->write2 = write2;
+}
+
+void sgihpc_init(void)
+{
+ unsigned long sid, crev, brev;
+
+ hpc3c0 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP0_PBASE);
+ hpc3c1 = (struct hpc3_regs *) (KSEG1 + HPC3_CHIP1_PBASE);
+ hpc3mregs = (struct hpc3_miscregs *) (KSEG1 + HPC3_MREGS_PBASE);
+ sid = hpc3mregs->sysid;
+
+ sid &= 0xff;
+ crev = (sid & 0xe0) >> 5;
+ brev = (sid & 0x1e) >> 1;
+
+#ifdef DEBUG_SGIHPC
+ prom_printf("sgihpc_init: crev<%2x> brev<%2x>\n", crev, brev);
+ prom_printf("sgihpc_init: ");
+#endif
+
+ if(sid & 1) {
+#ifdef DEBUG_SGIHPC
+ prom_printf("GUINESS ");
+#endif
+ sgi_guiness = 1;
+ } else {
+#ifdef DEBUG_SGIHPC
+ prom_printf("FULLHOUSE ");
+#endif
+ sgi_guiness = 0;
+ }
+ sgi_boardid = brev;
+
+#ifdef DEBUG_SGIHPC
+ prom_printf("sgi_boardid<%d> ", sgi_boardid);
+#endif
+
+ if(crev == 1) {
+ if((sid & 1) || (brev >= 2)) {
+#ifdef DEBUG_SGIHPC
+ prom_printf("IOC2 ");
+#endif
+ sgi_has_ioc2 = 1;
+ } else {
+#ifdef DEBUG_SGIHPC
+ prom_printf("IOC1 revision 1 ");
+#endif
+ }
+ } else {
+#ifdef DEBUG_SGIHPC
+ prom_printf("IOC1 revision 0 ");
+#endif
+ }
+#ifdef DEBUG_SGIHPC
+ prom_printf("\n");
+#endif
+
+ write1 = (HPC3_WRITE1_PRESET |
+ HPC3_WRITE1_KMRESET |
+ HPC3_WRITE1_ERESET |
+ HPC3_WRITE1_LC0OFF);
+
+ write2 = (HPC3_WRITE2_EASEL |
+ HPC3_WRITE2_NTHRESH |
+ HPC3_WRITE2_TPSPEED |
+ HPC3_WRITE2_EPSEL |
+ HPC3_WRITE2_U0AMODE |
+ HPC3_WRITE2_U1AMODE);
+
+ if(!sgi_guiness)
+ write1 |= HPC3_WRITE1_GRESET;
+ hpc3mregs->write1 = write1;
+ hpc3mregs->write2 = write2;
+
+ hpc3c0->pbus_piocfgs[0][6] |= HPC3_PIOPCFG_HW;
+}
diff --git a/arch/mips/sgi/kernel/indy_int.c b/arch/mips/sgi/kernel/indy_int.c
new file mode 100644
index 000000000..45600d2de
--- /dev/null
+++ b/arch/mips/sgi/kernel/indy_int.c
@@ -0,0 +1,579 @@
+/* $Id: indy_int.c,v 1.12 1996/08/07 02:54:11 dm Exp $
+ * indy_int.c: Routines for generic manipulation of the INT[23] ASIC
+ * found on INDY workstations..
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+#include <linux/config.h>
+
+#include <linux/errno.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/malloc.h>
+#include <linux/random.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/vector.h>
+
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/sgi.h>
+#include <asm/sgihpc.h>
+#include <asm/sgint23.h>
+#include <asm/sgialib.h>
+
+/* #define DEBUG_SGINT */
+
+struct sgi_int2_regs *sgi_i2regs;
+struct sgi_int3_regs *sgi_i3regs;
+struct sgi_ioc_ints *ioc_icontrol;
+struct sgi_ioc_timers *ioc_timers;
+volatile unsigned char *ioc_tclear;
+
+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);
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern void rs_kgdb_hook(int);
+#endif
+
+unsigned long spurious_count = 0;
+
+/* Local IRQ's are layed out logically like this:
+ *
+ * 0 --> 7 == local 0 interrupts
+ * 8 --> 15 == local 1 interrupts
+ * 16 --> 23 == vectored level 2 interrupts
+ * 24 --> 31 == vectored level 3 interrupts (not used)
+ */
+void disable_local_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ switch(irq_nr) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ ioc_icontrol->imask0 &= ~(1 << irq_nr);
+ break;
+
+ case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
+ ioc_icontrol->imask1 &= ~(1 << (irq_nr - 8));
+ break;
+
+ case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23:
+ ioc_icontrol->cmeimask0 &= ~(1 << (irq_nr - 16));
+ break;
+
+ default:
+ /* This way we'll see if anyone would ever want vectored
+ * level 3 interrupts. Highly unlikely.
+ */
+ printk("Yeeee, got passed irq_nr %d at disable_irq\n", irq_nr);
+ panic("INVALID IRQ level!");
+ };
+ restore_flags(flags);
+}
+
+void enable_local_irq(unsigned int irq_nr)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ switch(irq_nr) {
+ case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
+ ioc_icontrol->imask0 |= (1 << irq_nr);
+ break;
+
+ case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15:
+ ioc_icontrol->imask1 |= (1 << (irq_nr - 8));
+ break;
+
+ case 16: case 17: case 18: case 19: case 20: case 21: case 22: case 23:
+ enable_local_irq(7);
+ ioc_icontrol->cmeimask0 |= (1 << (irq_nr - 16));
+ break;
+
+ default:
+ printk("Yeeee, got passed irq_nr %d at disable_irq\n", irq_nr);
+ panic("INVALID IRQ level!");
+ };
+ restore_flags(flags);
+}
+
+void disable_gio_irq(unsigned int irq_nr)
+{
+ /* XXX TODO XXX */
+}
+
+void enable_gio_irq(unsigned int irq_nr)
+{
+ /* XXX TODO XXX */
+}
+
+void disable_hpcdma_irq(unsigned int irq_nr)
+{
+ /* XXX TODO XXX */
+}
+
+void enable_hpcdma_irq(unsigned int irq_nr)
+{
+ /* XXX TODO XXX */
+}
+
+void disable_irq(unsigned int irq_nr)
+{
+ unsigned int n = irq_nr;
+ if(n >= SGINT_END) {
+ printk("whee, invalid irq_nr %d\n", irq_nr);
+ panic("IRQ, you lose...");
+ }
+ if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
+ disable_local_irq(n - SGINT_LOCAL0);
+ } else if(n >= SGINT_GIO && n < SGINT_HPCDMA) {
+ disable_gio_irq(n - SGINT_GIO);
+ } else if(n >= SGINT_HPCDMA && n < SGINT_END) {
+ disable_hpcdma_irq(n - SGINT_HPCDMA);
+ } else {
+ panic("how did I get here?");
+ }
+}
+
+void enable_irq(unsigned int irq_nr)
+{
+ unsigned int n = irq_nr;
+ if(n >= SGINT_END) {
+ printk("whee, invalid irq_nr %d\n", irq_nr);
+ panic("IRQ, you lose...");
+ }
+ if(n >= SGINT_LOCAL0 && n < SGINT_GIO) {
+ enable_local_irq(n - SGINT_LOCAL0);
+ } else if(n >= SGINT_GIO && n < SGINT_HPCDMA) {
+ enable_gio_irq(n - SGINT_GIO);
+ } else if(n >= SGINT_HPCDMA && n < SGINT_END) {
+ enable_hpcdma_irq(n - SGINT_HPCDMA);
+ } else {
+ panic("how did I get here?");
+ }
+}
+
+static void local_unex(int irq, void *data, struct pt_regs *regs)
+{
+ printk("Whee: unexpected local IRQ at %08lx\n",
+ (unsigned long) regs->cp0_epc);
+ printk("DUMP: stat0<%x> stat1<%x> vmeistat<%x>\n",
+ ioc_icontrol->istat0, ioc_icontrol->istat1,
+ ioc_icontrol->vmeistat);
+}
+
+static struct irqaction *local_irq_action[24] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
+int setup_indy_irq(int irq, struct irqaction * new)
+{
+ printk("setup_indy_irq: Yeee, don't know how to setup irq<%d> for %s %p\n",
+ irq, new->name, new->handler);
+ return 0;
+}
+
+static struct irqaction r4ktimer_action = {
+ NULL, 0, 0, "R4000 timer/counter", NULL, NULL,
+};
+
+static struct irqaction indy_berr_action = {
+ NULL, 0, 0, "IP22 Bus Error", NULL, NULL,
+};
+
+static struct irqaction *irq_action[16] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, &indy_berr_action, &r4ktimer_action,
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
+int get_irq_list(char *buf)
+{
+ int i, len = 0;
+ int num = 0;
+ struct irqaction * action;
+
+ for (i = 0 ; i < 16 ; i++, num++) {
+ action = irq_action[i];
+ if (!action)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s",
+ num, kstat.interrupts[num],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ for (action=action->next; action; action = action->next) {
+ len += sprintf(buf+len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ len += sprintf(buf+len, " [on-chip]\n");
+ }
+ for (i = 0 ; i < 24 ; i++, num++) {
+ action = local_irq_action[i];
+ if (!action)
+ continue;
+ len += sprintf(buf+len, "%2d: %8d %c %s",
+ num, kstat.interrupts[num],
+ (action->flags & SA_INTERRUPT) ? '+' : ' ',
+ action->name);
+ for (action=action->next; action; action = action->next) {
+ len += sprintf(buf+len, ",%s %s",
+ (action->flags & SA_INTERRUPT) ? " +" : "",
+ action->name);
+ }
+ len += sprintf(buf+len, " [local]\n");
+ }
+ return len;
+}
+
+/*
+ * do_IRQ handles IRQ's that have been installed without the
+ * SA_INTERRUPT flag: it uses the full signal-handling return
+ * and runs with other interrupts enabled. All relatively slow
+ * IRQ's should use this format: notably the keyboard/timer
+ * routines.
+ */
+asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+{
+ struct irqaction * action = *(irq + irq_action);
+ kstat.interrupts[irq]++;
+ printk("Got irq %d, press a key.", irq);
+ prom_getchar();
+ romvec->imode();
+ while (action) {
+ if (action->flags & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ action->handler(irq, action->dev_id, regs);
+ action = action->next;
+ }
+}
+
+/*
+ * do_fast_IRQ handles IRQ's that don't need the fancy interrupt return
+ * stuff - the handler is also running with interrupts disabled unless
+ * it explicitly enables them later.
+ */
+asmlinkage void do_fast_IRQ(int irq)
+{
+ struct irqaction * action = *(irq + irq_action);
+
+ printk("Got irq %d, press a key.", irq);
+ prom_getchar();
+ romvec->imode();
+ kstat.interrupts[irq]++;
+ while (action) {
+ if (action->flags & SA_SAMPLE_RANDOM)
+ add_interrupt_randomness(irq);
+ action->handler(irq, action->dev_id, NULL);
+ action = action->next;
+ }
+}
+
+int request_local_irq(unsigned int lirq, void (*func)(int, void *, struct pt_regs *),
+ unsigned long iflags, const char *dname, void *devid)
+{
+ struct irqaction *action;
+
+ lirq -= SGINT_LOCAL0;
+ if(lirq >= 24 || !func)
+ return -EINVAL;
+
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if(!action)
+ return -ENOMEM;
+
+ action->handler = func;
+ action->flags = iflags;
+ action->mask = 0;
+ action->name = dname;
+ action->dev_id = devid;
+ action->next = 0;
+ local_irq_action[lirq] = action;
+ enable_irq(lirq + SGINT_LOCAL0);
+ return 0;
+}
+
+void free_local_irq(unsigned int lirq, void *dev_id)
+{
+ struct irqaction *action;
+
+ lirq -= SGINT_LOCAL0;
+ if(lirq >= 24) {
+ printk("Aieee: trying to free bogus local irq %d\n",
+ lirq + SGINT_LOCAL0);
+ return;
+ }
+ action = local_irq_action[lirq];
+ local_irq_action[lirq] = NULL;
+ disable_irq(lirq + SGINT_LOCAL0);
+ kfree(action);
+}
+
+int request_irq(unsigned int irq,
+ void (*handler)(int, void *, struct pt_regs *),
+ unsigned long irqflags,
+ const char * devname,
+ void *dev_id)
+{
+ int retval;
+ struct irqaction * action;
+
+ if (irq >= SGINT_END)
+ return -EINVAL;
+ if (!handler)
+ return -EINVAL;
+
+ if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO))
+ return request_local_irq(irq, handler, irqflags, devname, dev_id);
+
+ action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+ if (!action)
+ return -ENOMEM;
+
+ action->handler = handler;
+ action->flags = irqflags;
+ action->mask = 0;
+ action->name = devname;
+ action->next = NULL;
+ action->dev_id = dev_id;
+
+ retval = setup_indy_irq(irq, action);
+
+ if (retval)
+ kfree(action);
+ return retval;
+}
+
+void free_irq(unsigned int irq, void *dev_id)
+{
+ struct irqaction * action, **p;
+ unsigned long flags;
+
+ if (irq >= SGINT_END) {
+ printk("Trying to free IRQ%d\n",irq);
+ return;
+ }
+ if((irq >= SGINT_LOCAL0) && (irq < SGINT_GIO)) {
+ free_local_irq(irq, dev_id);
+ return;
+ }
+ for (p = irq + irq_action; (action = *p) != NULL; p = &action->next) {
+ if (action->dev_id != dev_id)
+ continue;
+
+ /* Found it - now free it */
+ save_flags(flags);
+ cli();
+ *p = action->next;
+ restore_flags(flags);
+ kfree(action);
+ return;
+ }
+ printk("Trying to free free IRQ%d\n",irq);
+}
+
+void init_IRQ(void)
+{
+ int i;
+
+ for (i = 0; i < 16 ; i++)
+ set_int_vector(i, 0);
+ irq_setup();
+}
+
+void indy_local0_irqdispatch(struct pt_regs *regs)
+{
+ struct irqaction *action;
+ unsigned char mask = ioc_icontrol->istat0;
+ unsigned char mask2 = 0;
+ int irq;
+
+ mask &= ioc_icontrol->imask0;
+ if(mask & ISTAT0_LIO2) {
+ mask2 = ioc_icontrol->vmeistat;
+ mask2 &= ioc_icontrol->cmeimask0;
+ irq = lc2msk_to_irqnr[mask2];
+ action = local_irq_action[irq];
+ } else {
+ irq = lc0msk_to_irqnr[mask];
+ action = local_irq_action[irq];
+ }
+#if 0
+ printk("local0_dispatch: got irq %d mask %2x mask2 %2x\n",
+ irq, mask, mask2);
+ prom_getchar();
+#endif
+ kstat.interrupts[irq + 16]++;
+ action->handler(irq, action->dev_id, regs);
+}
+
+void indy_local1_irqdispatch(struct pt_regs *regs)
+{
+ struct irqaction *action;
+ unsigned char mask = ioc_icontrol->istat1;
+ unsigned char mask2 = 0;
+ int irq;
+
+ mask &= ioc_icontrol->imask1;
+ if(mask & ISTAT1_LIO3) {
+ printk("WHee: Got an LIO3 irq, winging it...\n");
+ mask2 = ioc_icontrol->vmeistat;
+ mask2 &= ioc_icontrol->cmeimask1;
+ irq = lc3msk_to_irqnr[ioc_icontrol->vmeistat];
+ action = local_irq_action[irq];
+ } else {
+ irq = lc1msk_to_irqnr[mask];
+ action = local_irq_action[irq];
+ }
+#if 0
+ printk("local1_dispatch: got irq %d mask %2x mask2 %2x\n",
+ irq, mask, mask2);
+ prom_getchar();
+#endif
+ kstat.interrupts[irq + 24]++;
+ action->handler(irq, action->dev_id, regs);
+}
+
+void indy_buserror_irq(struct pt_regs *regs)
+{
+ kstat.interrupts[6]++;
+ printk("Got a bus error IRQ, shouldn't happen yet\n");
+ show_regs(regs);
+ printk("Spinning...\n");
+ while(1)
+ ;
+}
+
+/* Misc. crap just to keep the kernel linking... */
+unsigned long probe_irq_on (void)
+{
+ return 0;
+}
+
+int probe_irq_off (unsigned long irqs)
+{
+ return 0;
+}
+
+void sgint_init(void)
+{
+ int i;
+#ifdef CONFIG_REMOTE_DEBUG
+ char *ctype;
+#endif
+
+ sgi_i2regs = (struct sgi_int2_regs *) (KSEG1 + SGI_INT2_BASE);
+ sgi_i3regs = (struct sgi_int3_regs *) (KSEG1 + SGI_INT3_BASE);
+
+ /* Init local mask --> irq tables. */
+ for(i = 0; i < 256; i++) {
+ if(i & 0x80) {
+ lc0msk_to_irqnr[i] = 7;
+ lc1msk_to_irqnr[i] = 15;
+ lc2msk_to_irqnr[i] = 23;
+ lc3msk_to_irqnr[i] = 31;
+ } else if(i & 0x40) {
+ lc0msk_to_irqnr[i] = 6;
+ lc1msk_to_irqnr[i] = 14;
+ lc2msk_to_irqnr[i] = 22;
+ lc3msk_to_irqnr[i] = 30;
+ } else if(i & 0x20) {
+ lc0msk_to_irqnr[i] = 5;
+ lc1msk_to_irqnr[i] = 13;
+ lc2msk_to_irqnr[i] = 21;
+ lc3msk_to_irqnr[i] = 29;
+ } else if(i & 0x10) {
+ lc0msk_to_irqnr[i] = 4;
+ lc1msk_to_irqnr[i] = 12;
+ lc2msk_to_irqnr[i] = 20;
+ lc3msk_to_irqnr[i] = 28;
+ } else if(i & 0x08) {
+ lc0msk_to_irqnr[i] = 3;
+ lc1msk_to_irqnr[i] = 11;
+ lc2msk_to_irqnr[i] = 19;
+ lc3msk_to_irqnr[i] = 27;
+ } else if(i & 0x04) {
+ lc0msk_to_irqnr[i] = 2;
+ lc1msk_to_irqnr[i] = 10;
+ lc2msk_to_irqnr[i] = 18;
+ lc3msk_to_irqnr[i] = 26;
+ } else if(i & 0x02) {
+ lc0msk_to_irqnr[i] = 1;
+ lc1msk_to_irqnr[i] = 9;
+ lc2msk_to_irqnr[i] = 17;
+ lc3msk_to_irqnr[i] = 25;
+ } else if(i & 0x01) {
+ lc0msk_to_irqnr[i] = 0;
+ lc1msk_to_irqnr[i] = 8;
+ lc2msk_to_irqnr[i] = 16;
+ lc3msk_to_irqnr[i] = 24;
+ } else {
+ lc0msk_to_irqnr[i] = 0;
+ lc1msk_to_irqnr[i] = 0;
+ lc2msk_to_irqnr[i] = 0;
+ lc3msk_to_irqnr[i] = 0;
+ }
+ }
+
+ ioc_icontrol = &sgi_i3regs->ints;
+ ioc_timers = &sgi_i3regs->timers;
+ ioc_tclear = &sgi_i3regs->tclear;
+
+ /* Mask out all interrupts. */
+ ioc_icontrol->imask0 = 0;
+ ioc_icontrol->imask1 = 0;
+ ioc_icontrol->cmeimask0 = 0;
+ ioc_icontrol->cmeimask1 = 0;
+
+ /* Now safe to set the exception vector. */
+ set_except_vector(0, indyIRQ);
+
+#ifdef CONFIG_REMOTE_DEBUG
+ ctype = prom_getcmdline();
+ for(i = 0; i < strlen(ctype); i++) {
+ if(ctype[i]=='k' && ctype[i+1]=='g' &&
+ ctype[i+2]=='d' && ctype[i+3]=='b' &&
+ ctype[i+4]=='=' && ctype[i+5]=='t' &&
+ ctype[i+6]=='t' && ctype[i+7]=='y' &&
+ ctype[i+8]=='d' &&
+ (ctype[i+9] == '1' || ctype[i+9] == '2')) {
+ printk("KGDB: Using serial line /dev/ttyd%d for "
+ "session\n", (ctype[i+9] - '0'));
+ if(ctype[i+9]=='1')
+ rs_kgdb_hook(1);
+ else if(ctype[i+9]=='2')
+ rs_kgdb_hook(0);
+ else {
+ printk("KGDB: whoops bogon tty line "
+ "requested, disabling session\n");
+ }
+
+ }
+ }
+#endif
+}
diff --git a/arch/mips/sgi/kernel/indy_mc.c b/arch/mips/sgi/kernel/indy_mc.c
new file mode 100644
index 000000000..b2f35018c
--- /dev/null
+++ b/arch/mips/sgi/kernel/indy_mc.c
@@ -0,0 +1,153 @@
+/* $Id: indy_mc.c,v 1.5 1996/06/29 07:06:51 dm Exp $
+ * indy_mc.c: Routines for manipulating the INDY memory controller.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <asm/segment.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/sgimc.h>
+#include <asm/sgihpc.h>
+#include <asm/sgialib.h>
+
+/* #define DEBUG_SGIMC */
+
+struct sgimc_misc_ctrl *mcmisc_regs;
+unsigned long *rpsscounter;
+struct sgimc_dma_ctrl *dmactrlregs;
+
+static inline char *mconfig_string(unsigned long val)
+{
+ switch(val & SGIMC_MCONFIG_RMASK) {
+ case SGIMC_MCONFIG_FOURMB:
+ return "4MB";
+
+ case SGIMC_MCONFIG_EIGHTMB:
+ return "8MB";
+
+ case SGIMC_MCONFIG_SXTEENMB:
+ return "16MB";
+
+ case SGIMC_MCONFIG_TTWOMB:
+ return "32MB";
+
+ case SGIMC_MCONFIG_SFOURMB:
+ return "64MB";
+
+ case SGIMC_MCONFIG_OTEIGHTMB:
+ return "128MB";
+
+ default:
+ return "wheee, unknown";
+ };
+}
+
+void sgimc_init(void)
+{
+ unsigned long tmpreg;
+
+ mcmisc_regs = (struct sgimc_misc_ctrl *)(KSEG1+0x1fa00000);
+ rpsscounter = (unsigned long *) (KSEG1 + 0x1fa01004);
+ dmactrlregs = (struct sgimc_dma_ctrl *) (KSEG1+0x1fa02000);
+
+ printk("MC: SGI memory controller Revision %d\n",
+ (int) mcmisc_regs->systemid & SGIMC_SYSID_MASKREV);
+
+#if 0 /* XXX Until I figure out what this bit really indicates XXX */
+ /* XXX Is this systemid bit reliable? */
+ if(mcmisc_regs->systemid & SGIMC_SYSID_EPRESENT) {
+ EISA_bus = 1;
+ printk("with EISA\n");
+ } else {
+ EISA_bus = 0;
+ printk("no EISA\n");
+ }
+#endif
+
+#ifdef DEBUG_SGIMC
+ prom_printf("sgimc_init: memconfig0<%s> mconfig1<%s>\n",
+ mconfig_string(mcmisc_regs->mconfig0),
+ mconfig_string(mcmisc_regs->mconfig1));
+
+ prom_printf("mcdump: cpuctrl0<%08lx> cpuctrl1<%08lx>\n",
+ mcmisc_regs->cpuctrl0, mcmisc_regs->cpuctrl1);
+ prom_printf("mcdump: divider<%08lx>, gioparm<%04x>\n",
+ mcmisc_regs->divider, mcmisc_regs->gioparm);
+#endif
+
+ /* Place the MC into a known state. This must be done before
+ * interrupts are first enabled etc.
+ */
+
+ /* 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.
+ */
+ mcmisc_regs->cstat = mcmisc_regs->gstat = 0;
+
+ /* Step 2: Enable all parity checking in cpu control register
+ * zero.
+ */
+ tmpreg = mcmisc_regs->cpuctrl0;
+ tmpreg |= (SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM |
+ SGIMC_CCTRL0_R4KNOCHKPARR);
+ mcmisc_regs->cpuctrl0 = tmpreg;
+
+ /* Step 3: Setup the MC write buffer depth, this is controlled
+ * in cpu control register 1 in the lower 4 bits.
+ */
+ tmpreg = mcmisc_regs->cpuctrl1;
+ tmpreg &= ~0xf;
+ tmpreg |= 0xd;
+ mcmisc_regs->cpuctrl1 = tmpreg;
+
+ /* 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
+ */
+ mcmisc_regs->divider = 0x101;
+
+ /* Step 5: Initialize GIO64 arbitrator configuration register.
+ *
+ * NOTE: If you dork with startup code the HPC init code in
+ * sgihpc_init() must run before us because of how 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. */
+ tmpreg = SGIMC_GIOPARM_HPC64; /* All 1st HPC's interface at 64bits. */
+ tmpreg |= SGIMC_GIOPARM_ONEBUS; /* Only one physical GIO bus exists. */
+
+ if(sgi_guiness) {
+ /* Guiness specific settings. */
+ tmpreg |= SGIMC_GIOPARM_EISA64; /* MC talks to EISA at 64bits */
+ tmpreg |= SGIMC_GIOPARM_MASTEREISA; /* EISA bus can act as master */
+ } else {
+ /* Fullhouse specific settings. */
+ if(sgi_boardid < 2) {
+ tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC at 64bits */
+ tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp0 pipelines */
+ tmpreg |= SGIMC_GIOPARM_MASTEREXP1;/* exp1 masters */
+ tmpreg |= SGIMC_GIOPARM_RTIMEEXP0; /* exp0 is realtime */
+ } else {
+ tmpreg |= SGIMC_GIOPARM_HPC264; /* 2nd HPC 64bits */
+ tmpreg |= SGIMC_GIOPARM_PLINEEXP0; /* exp[01] pipelined */
+ tmpreg |= SGIMC_GIOPARM_PLINEEXP1;
+ tmpreg |= SGIMC_GIOPARM_MASTEREISA;/* EISA masters */
+ }
+ }
+ mcmisc_regs->gioparm = tmpreg; /* poof */
+}
diff --git a/arch/mips/sgi/kernel/indy_timer.c b/arch/mips/sgi/kernel/indy_timer.c
new file mode 100644
index 000000000..9aa88cb18
--- /dev/null
+++ b/arch/mips/sgi/kernel/indy_timer.c
@@ -0,0 +1,298 @@
+/* $Id: indy_timer.c,v 1.10 1996/08/07 02:54:11 dm Exp $
+ * indy_timer.c: Setting up the clock on the INDY 8254 controller.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/param.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/timex.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/bootinfo.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/ptrace.h>
+#include <asm/system.h>
+#include <asm/sgi.h>
+#include <asm/sgialib.h>
+#include <asm/sgihpc.h>
+#include <asm/sgint23.h>
+
+/* The layout of registers for the INDY Dallas 1286 clock chipset. */
+struct indy_clock {
+ volatile unsigned int hsec;
+ volatile unsigned int sec;
+ volatile unsigned int min;
+ volatile unsigned int malarm;
+ volatile unsigned int hr;
+ volatile unsigned int halarm;
+ volatile unsigned int day;
+ volatile unsigned int dalarm;
+ volatile unsigned int date;
+ volatile unsigned int month;
+ volatile unsigned int year;
+ volatile unsigned int cmd;
+ volatile unsigned int whsec;
+ volatile unsigned int wsec;
+ volatile unsigned int _unused0[50];
+};
+
+#define INDY_CLOCK_REGS ((struct indy_clock *)(KSEG1ADDR(0x1fbe0000)))
+
+/* Because of a bug in the i8254 timer we need to use the onchip r4k
+ * counter as our system wide timer interrupt running at 100HZ.
+ */
+static unsigned long r4k_offset; /* Amount to increment compare reg each time */
+static unsigned long r4k_cur; /* What counter should be at next timer irq */
+
+static inline void ack_r4ktimer(unsigned long newval)
+{
+ write_32bit_cp0_register(CP0_COMPARE, newval);
+}
+
+static int set_rtc_mmss(unsigned long nowtime)
+{
+ struct indy_clock *clock = INDY_CLOCK_REGS;
+ int retval = 0;
+ int real_seconds, real_minutes, clock_minutes;
+
+#define FROB_FROM_CLOCK(x) (((x) & 0xf) | ((((x) & 0xf0) >> 4) * 10));
+#define FROB_TO_CLOCK(x) ((((((x) & 0xff) / 10)<<4) | (((x) & 0xff) % 10)) & 0xff)
+
+ clock->cmd &= ~(0x80);
+ clock_minutes = clock->min;
+ clock->cmd |= (0x80);
+
+ clock_minutes = FROB_FROM_CLOCK(clock_minutes);
+ real_seconds = nowtime % 60;
+ real_minutes = nowtime / 60;
+
+ if(((abs(real_minutes - clock_minutes) + 15)/30) & 1)
+ real_minutes += 30; /* correct for half hour time zone */
+
+ real_minutes %= 60;
+ if(abs(real_minutes - clock_minutes) < 30) {
+ /* Force clock oscillator to be on. */
+ clock->month &= ~(0x80);
+
+ /* Write real_seconds and real_minutes into the Dallas. */
+ clock->cmd &= ~(0x80);
+ clock->sec = real_seconds;
+ clock->min = real_minutes;
+ clock->cmd |= (0x80);
+ } else
+ return -1;
+
+#undef FROB_FROM_CLOCK
+#undef FROB_TO_CLOCK
+
+ return retval;
+}
+
+static long last_rtc_update = 0;
+
+void indy_timer_interrupt(struct pt_regs *regs)
+{
+ /* Ack timer and compute new compare. */
+ r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
+ ack_r4ktimer(r4k_cur);
+ kstat.interrupts[7]++;
+ do_timer(regs);
+
+ /* We update the Dallas time of day approx. every 11 minutes,
+ * because of how the numbers work out we need to make
+ * absolutely sure we do this update within 500ms before the
+ * next second starts, thus the following code.
+ */
+ if (time_state != TIME_BAD && xtime.tv_sec > last_rtc_update + 660 &&
+ xtime.tv_usec > 500000 - (tick >> 1) &&
+ xtime.tv_usec < 500000 + (tick >> 1))
+ if (set_rtc_mmss(xtime.tv_sec) == 0)
+ last_rtc_update = xtime.tv_sec;
+ else
+ last_rtc_update = xtime.tv_sec - 600; /* do it again in 60 s */
+}
+
+static inline unsigned long dosample(volatile unsigned char *tcwp,
+ volatile unsigned char *tc2p)
+{
+ unsigned long ct0, ct1;
+ unsigned char msb, lsb;
+
+ /* Start the counter. */
+ *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MRGEN);
+ *tc2p = (SGINT_TCSAMP_COUNTER & 0xff);
+ *tc2p = (SGINT_TCSAMP_COUNTER >> 8);
+
+ /* Get initial counter invariant */
+ ct0 = read_32bit_cp0_register(CP0_COUNT);
+
+ /* Latch and spin until top byte of counter2 is zero */
+ *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT);
+ ct1 = read_32bit_cp0_register(CP0_COUNT);
+ lsb = *tc2p;
+ msb = *tc2p;
+ while(msb) {
+ *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT);
+ ct1 = read_32bit_cp0_register(CP0_COUNT);
+ lsb = *tc2p;
+ msb = *tc2p;
+ }
+
+ /* Stop the counter. */
+ *tcwp = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST);
+
+ /* Return the difference, this is how far the r4k counter increments
+ * for every one HZ.
+ */
+ return ct1 - ct0;
+}
+
+/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
+ * Assumes input in normal date format, i.e. 1980-12-31 23:59:59
+ * => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
+ *
+ * [For the Julian calendar (which was used in Russia before 1917,
+ * Britain & colonies before 1752, anywhere else before 1582,
+ * and is still in use by some communities) leave out the
+ * -year/100+year/400 terms, and add 10.]
+ *
+ * This algorithm was first published by Gauss (I think).
+ *
+ * WARNING: this function will overflow on 2106-02-07 06:28:16 on
+ * machines were long is 32-bit! (However, as time_t is signed, we
+ * will already get problems at other places on 2038-01-19 03:14:08)
+ */
+static inline unsigned long mktime(unsigned int year, unsigned int mon,
+ unsigned int day, unsigned int hour,
+ unsigned int min, unsigned int sec)
+{
+ if (0 >= (int) (mon -= 2)) { /* 1..12 -> 11,12,1..10 */
+ mon += 12; /* Puts Feb last since it has leap day */
+ year -= 1;
+ }
+ return (((
+ (unsigned long)(year/4 - year/100 + year/400 + 367*mon/12 + day) +
+ year*365 - 719499
+ )*24 + hour /* now have hours */
+ )*60 + min /* now have minutes */
+ )*60 + sec; /* finally seconds */
+}
+
+unsigned long get_indy_time(void)
+{
+ struct indy_clock *clock = INDY_CLOCK_REGS;
+ unsigned int year, mon, day, hour, min, sec;
+
+ /* Freeze it. */
+ clock->cmd &= ~(0x80);
+
+ /* Read regs. */
+ sec = clock->sec;
+ min = clock->min;
+ hour = (clock->hr & 0x3f);
+ day = (clock->date & 0x3f);
+ mon = (clock->month & 0x1f);
+ year = clock->year;
+
+ /* Unfreeze clock. */
+ clock->cmd |= 0x80;
+
+ /* Frob the bits. */
+#define FROB1(x) (((x) & 0xf) + ((((x) & 0xf0) >> 4) * 10));
+#define FROB2(x) (((x) & 0xf) + (((((x) & 0xf0) >> 4) & 0x3) * 10));
+
+ /* XXX Should really check that secs register is the same
+ * XXX as when we first read it and if not go back and
+ * XXX read the regs above again.
+ */
+ sec = FROB1(sec); min = FROB1(min); day = FROB1(day);
+ mon = FROB1(mon); year = FROB1(year);
+ hour = FROB2(hour);
+
+#undef FROB1
+#undef FROB2
+
+ /* Wheee... */
+ if(year < 45)
+ year += 30;
+ if ((year += 1940) < 1970)
+ year += 100;
+
+ return mktime(year, mon, day, hour, min, sec);
+}
+
+#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
+
+void indy_timer_init(void)
+{
+ struct sgi_ioc_timers *p;
+ volatile unsigned char *tcwp, *tc2p;
+
+ /* 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. ;-)
+ */
+ p = ioc_timers;
+ tcwp = &p->tcword;
+ tc2p = &p->tcnt2;
+
+ printk("calculating r4koff... ");
+ r4k_offset = dosample(tcwp, tc2p); /* First sample. */
+ dosample(tcwp, tc2p); /* Eat one... */
+ r4k_offset += dosample(tcwp, tc2p); /* Second sample. */
+ r4k_offset = (r4k_offset >> 1); /* Get average. */
+ r4k_offset = HZ * r4k_offset; /* Multiply by HZ */
+
+ printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+
+ r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
+ write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
+ set_cp0_status(ST0_IM, ALLINTS);
+ sti();
+
+ /* Read time from the dallas chipset. */
+ xtime.tv_sec = get_indy_time();
+ xtime.tv_usec = 0;
+}
+
+void indy_8254timer_irq(void)
+{
+ kstat.interrupts[4]++;
+ printk("indy_8254timer_irq: Whoops, should not have gotten this IRQ\n");
+ prom_getchar();
+ prom_imode();
+}
+
+void do_gettimeofday(struct timeval *tv)
+{
+ unsigned long flags;
+
+ save_flags(flags); cli();
+ *tv = xtime;
+ restore_flags(flags);
+}
+
+void do_settimeofday(struct timeval *tv)
+{
+ cli();
+ xtime = *tv;
+ time_state = TIME_BAD;
+ time_maxerror = MAXPHASE;
+ time_esterror = MAXPHASE;
+ sti();
+}
+
diff --git a/arch/mips/sgi/kernel/reset.c b/arch/mips/sgi/kernel/reset.c
new file mode 100644
index 000000000..4e7455952
--- /dev/null
+++ b/arch/mips/sgi/kernel/reset.c
@@ -0,0 +1,14 @@
+/*
+ * linux/arch/mips/sgi/kernel/process.c
+ *
+ * Reset a SGI.
+ */
+#include <asm/io.h>
+#include <asm/system.h>
+
+void
+sgi_hard_reset_now(void)
+{
+ for(;;)
+ prom_imode();
+}
diff --git a/arch/mips/sgi/kernel/setup.c b/arch/mips/sgi/kernel/setup.c
new file mode 100644
index 000000000..61b67df75
--- /dev/null
+++ b/arch/mips/sgi/kernel/setup.c
@@ -0,0 +1,83 @@
+/* $Id: setup.c,v 1.11 1996/06/29 07:06:51 dm Exp $
+ * setup.c: SGI specific setup, including init of the feature struct.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+#ifndef __GOGOGO__
+#error "... about to fuckup your Indy?"
+#endif
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/vector.h>
+#include <asm/sgialib.h>
+#include <asm/sgi.h>
+#include <asm/sgimc.h>
+#include <asm/sgihpc.h>
+#include <asm/sgint23.h>
+
+extern int serial_console; /* in console.c, of course */
+
+struct feature sgi_feature = {
+};
+
+static void sgi_irq_setup(void)
+{
+ sgint_init();
+}
+
+#if 0
+extern void register_console(void (*proc)(const char *));
+
+static void sgi_print(const char *p)
+{
+ char c;
+
+ while((c = *p++) != 0) {
+ if(c == '\n')
+ prom_putchar('\r');
+ prom_putchar(c);
+ }
+}
+#endif
+
+void sgi_setup(void)
+{
+ char *ctype;
+
+ irq_setup = sgi_irq_setup;
+ feature = &sgi_feature;
+ hard_reset_now = acn_hard_reset_now;
+
+ /* register_console(sgi_print); */
+
+ sgi_sysinit();
+
+ /* 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();
+
+ /* 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 = prom_getenv("console");
+ serial_console = 0;
+ if(*ctype == 'd') {
+ if(*(ctype+1)=='2')
+ serial_console = 1;
+ else
+ serial_console = 2;
+ if(!serial_console) {
+ prom_printf("Weird console env setting %s\n", ctype);
+ prom_printf("Press a key to reboot.\n");
+ prom_getchar();
+ prom_imode();
+ }
+ }
+}
diff --git a/arch/mips/sgi/kernel/system.c b/arch/mips/sgi/kernel/system.c
new file mode 100644
index 000000000..051ad09c5
--- /dev/null
+++ b/arch/mips/sgi/kernel/system.c
@@ -0,0 +1,168 @@
+/* $Id: system.c,v 1.2 1996/06/10 16:38:32 dm Exp $
+ * system.c: Probe the system type using ARCS prom interface library.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/string.h>
+
+#include <asm/sgi.h>
+#include <asm/sgialib.h>
+#include <asm/bootinfo.h>
+
+#ifndef __GOGOGO__
+#error "... You're fearless, aren't you?"
+#endif
+
+enum sgi_mach sgimach;
+
+struct smatch {
+ char *name;
+ int type;
+};
+
+static struct smatch sgi_mtable[] = {
+ { "SGI-IP4", ip4 },
+ { "SGI-IP5", ip5 },
+ { "SGI-IP6", ip6 },
+ { "SGI-IP7", ip7 },
+ { "SGI-IP9", ip9 },
+ { "SGI-IP12", ip12 },
+ { "SGI-IP15", ip15 },
+ { "SGI-IP17", ip17 },
+ { "SGI-IP19", ip19 },
+ { "SGI-IP20", ip20 },
+ { "SGI-IP21", ip21 },
+ { "SGI-IP22", ip22 },
+ { "SGI-IP25", ip25 },
+ { "SGI-IP26", ip26 },
+ { "SGI-IP28", ip28 },
+ { "SGI-IP30", ip30 },
+ { "SGI-IP32", ip32 }
+};
+
+#define NUM_MACHS 17 /* for now */
+
+static struct smatch sgi_cputable[] = {
+ { "MIPS-R2000", CPU_R2000 },
+ { "MIPS-R3000", CPU_R3000 },
+ { "MIPS-R3000A", CPU_R3000A },
+ { "MIPS-R4000", CPU_R4000SC },
+ { "MIPS-R4400", CPU_R4400SC },
+ { "MIPS-R4600", CPU_R4600 },
+ { "MIPS-R8000", CPU_R8000 },
+ { "MIPS-R5000", CPU_R5000 },
+ { "MIPS-R5000A", CPU_R5000A }
+};
+
+#define NUM_CPUS 9 /* for now */
+
+static enum sgi_mach string_to_mach(char *s)
+{
+ int i;
+
+ for(i = 0; i < NUM_MACHS; i++) {
+ if(!strcmp(s, sgi_mtable[i].name))
+ return (enum sgi_mach) sgi_mtable[i].type;
+ }
+ prom_printf("\nYeee, could not determine SGI architecture type <%s>\n", s);
+ prom_printf("press a key to reboot\n");
+ prom_getchar();
+ romvec->imode();
+ return (enum sgi_mach) 0;
+}
+
+static int string_to_cpu(char *s)
+{
+ int i;
+
+ for(i = 0; i < NUM_CPUS; i++) {
+ if(!strcmp(s, sgi_cputable[i].name))
+ return sgi_mtable[i].type;
+ }
+ prom_printf("\nYeee, could not determine MIPS cpu type <%s>\n", s);
+ prom_printf("press a key to reboot\n");
+ prom_getchar();
+ romvec->imode();
+ return 0;
+}
+
+void sgi_sysinit(void)
+{
+ pcomponent *p, *toplev, *cpup = 0;
+ int cputype = -1;
+
+
+ /* The root component tells us what machine architecture we
+ * have here.
+ */
+ p = prom_getchild(PROM_NULL_COMPONENT);
+ printk("ARCH: %s\n", p->iname);
+ sgimach = string_to_mach(p->iname);
+
+ /* Now scan for cpu(s). */
+ toplev = p = prom_getchild(p);
+ while(p) {
+ int ncpus = 0;
+
+ if(p->type == Cpu) {
+ if(++ncpus > 1) {
+ prom_printf("\nYeee, SGI MP not ready yet\n");
+ prom_printf("press a key to reboot\n");
+ prom_getchar();
+ romvec->imode();
+ }
+ printk("CPU: %s ", p->iname);
+ cpup = p;
+ cputype = string_to_cpu(cpup->iname);
+ }
+ p = prom_getsibling(p);
+ }
+ if(cputype == -1) {
+ prom_printf("\nYeee, could not find cpu ARCS component\n");
+ prom_printf("press a key to reboot\n");
+ prom_getchar();
+ romvec->imode();
+ }
+ p = prom_getchild(cpup);
+ while(p) {
+ switch(p->class) {
+ case processor:
+ switch(p->type) {
+ case Fpu:
+ printk("FPU<%s> ", p->iname);
+ break;
+
+ default:
+ break;
+ };
+ break;
+
+ case cache:
+ switch(p->type) {
+ case picache:
+ printk("ICACHE ");
+ break;
+
+ case pdcache:
+ printk("DCACHE ");
+ break;
+
+ case sccache:
+ printk("SCACHE ");
+ break;
+
+ default:
+ break;
+
+ };
+ break;
+
+ default:
+ break;
+ };
+ p = prom_getsibling(p);
+ }
+ printk("\n");
+}
diff --git a/arch/mips/sgi/kernel/time.c b/arch/mips/sgi/kernel/time.c
new file mode 100644
index 000000000..1f5137c27
--- /dev/null
+++ b/arch/mips/sgi/kernel/time.c
@@ -0,0 +1,14 @@
+/* $Id: time.c,v 1.1 1996/06/08 12:07:08 dm Exp $
+ * time.c: Generic SGI time_init() code, this will dispatch to the
+ * appropriate per-architecture time/counter init code.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+extern void indy_timer_init(void);
+
+void time_init(void)
+{
+ /* XXX assume INDY for now XXX */
+ indy_timer_init();
+}