summaryrefslogtreecommitdiff
path: root/arch/mips/sgi-ip32
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-04-16 15:20:36 -0700
commit1da177e4c3f41524e886b7f1b8a0c1fc7321cac2 (patch)
tree0bba044c4ce775e45a88a51686b5d9f90697ea9d /arch/mips/sgi-ip32
Linux-2.6.12-rc2v2.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!
Diffstat (limited to 'arch/mips/sgi-ip32')
-rw-r--r--arch/mips/sgi-ip32/Makefile9
-rw-r--r--arch/mips/sgi-ip32/crime.c103
-rw-r--r--arch/mips/sgi-ip32/ip32-berr.c36
-rw-r--r--arch/mips/sgi-ip32/ip32-irq-glue.S86
-rw-r--r--arch/mips/sgi-ip32/ip32-irq.c590
-rw-r--r--arch/mips/sgi-ip32/ip32-memory.c49
-rw-r--r--arch/mips/sgi-ip32/ip32-reset.c202
-rw-r--r--arch/mips/sgi-ip32/ip32-setup.c159
8 files changed, 1234 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip32/Makefile b/arch/mips/sgi-ip32/Makefile
new file mode 100644
index 000000000000..470898f4afe1
--- /dev/null
+++ b/arch/mips/sgi-ip32/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for the SGI specific kernel interface routines
+# under Linux.
+#
+
+obj-y += ip32-berr.o ip32-irq.o ip32-irq-glue.o ip32-setup.o ip32-reset.o \
+ crime.o ip32-memory.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sgi-ip32/crime.c b/arch/mips/sgi-ip32/crime.c
new file mode 100644
index 000000000000..eb3a16a04fee
--- /dev/null
+++ b/arch/mips/sgi-ip32/crime.c
@@ -0,0 +1,103 @@
+/*
+ * 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) 2001, 2003 Keith M Wesolowski
+ * Copyright (C) 2005 Ilya A. Volynets <ilya@total-knowledge.com>
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/ptrace.h>
+#include <asm/page.h>
+#include <asm/ip32/crime.h>
+#include <asm/ip32/mace.h>
+
+struct sgi_crime *crime;
+struct sgi_mace *mace;
+
+void __init crime_init(void)
+{
+ unsigned int id, rev;
+ const int field = 2 * sizeof(unsigned long);
+
+ set_io_port_base((unsigned long) ioremap(MACEPCI_LOW_IO, 0x2000000));
+ crime = ioremap(CRIME_BASE, sizeof(struct sgi_crime));
+ mace = ioremap(MACE_BASE, sizeof(struct sgi_mace));
+
+ id = crime->id;
+ rev = id & CRIME_ID_REV;
+ id = (id & CRIME_ID_IDBITS) >> 4;
+ printk (KERN_INFO "CRIME id %1x rev %d at 0x%0*lx\n",
+ id, rev, field, (unsigned long) CRIME_BASE);
+}
+
+irqreturn_t
+crime_memerr_intr (unsigned int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long stat, addr;
+ int fatal = 0;
+
+ stat = crime->mem_error_stat & CRIME_MEM_ERROR_STAT_MASK;
+ addr = crime->mem_error_addr & CRIME_MEM_ERROR_ADDR_MASK;
+
+ printk("CRIME memory error at 0x%08lx ST 0x%08lx<", addr, stat);
+
+ if (stat & CRIME_MEM_ERROR_INV)
+ printk("INV,");
+ if (stat & CRIME_MEM_ERROR_ECC) {
+ unsigned long ecc_syn =
+ crime->mem_ecc_syn & CRIME_MEM_ERROR_ECC_SYN_MASK;
+ unsigned long ecc_gen =
+ crime->mem_ecc_chk & CRIME_MEM_ERROR_ECC_CHK_MASK;
+ printk("ECC,SYN=0x%08lx,GEN=0x%08lx,", ecc_syn, ecc_gen);
+ }
+ if (stat & CRIME_MEM_ERROR_MULTIPLE) {
+ fatal = 1;
+ printk("MULTIPLE,");
+ }
+ if (stat & CRIME_MEM_ERROR_HARD_ERR) {
+ fatal = 1;
+ printk("HARD,");
+ }
+ if (stat & CRIME_MEM_ERROR_SOFT_ERR)
+ printk("SOFT,");
+ if (stat & CRIME_MEM_ERROR_CPU_ACCESS)
+ printk("CPU,");
+ if (stat & CRIME_MEM_ERROR_VICE_ACCESS)
+ printk("VICE,");
+ if (stat & CRIME_MEM_ERROR_GBE_ACCESS)
+ printk("GBE,");
+ if (stat & CRIME_MEM_ERROR_RE_ACCESS)
+ printk("RE,REID=0x%02lx,", (stat & CRIME_MEM_ERROR_RE_ID)>>8);
+ if (stat & CRIME_MEM_ERROR_MACE_ACCESS)
+ printk("MACE,MACEID=0x%02lx,", stat & CRIME_MEM_ERROR_MACE_ID);
+
+ crime->mem_error_stat = 0;
+
+ if (fatal) {
+ printk("FATAL>\n");
+ panic("Fatal memory error.");
+ } else
+ printk("NONFATAL>\n");
+
+ return IRQ_HANDLED;
+}
+
+irqreturn_t
+crime_cpuerr_intr (unsigned int irq, void *dev_id, struct pt_regs *regs)
+{
+ unsigned long stat = crime->cpu_error_stat & CRIME_CPU_ERROR_MASK;
+ unsigned long addr = crime->cpu_error_addr & CRIME_CPU_ERROR_ADDR_MASK;
+
+ addr <<= 2;
+ printk ("CRIME CPU error at 0x%09lx status 0x%08lx\n", addr, stat);
+ crime->cpu_error_stat = 0;
+
+ return IRQ_HANDLED;
+}
diff --git a/arch/mips/sgi-ip32/ip32-berr.c b/arch/mips/sgi-ip32/ip32-berr.c
new file mode 100644
index 000000000000..a278e918a019
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-berr.c
@@ -0,0 +1,36 @@
+/*
+ * 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) 1994, 1995, 1996, 1999, 2000 by Ralf Baechle
+ * Copyright (C) 1999, 2000 by Silicon Graphics
+ * Copyright (C) 2002 Maciej W. Rozycki
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+#include <asm/addrspace.h>
+#include <asm/ptrace.h>
+#include <asm/tlbdebug.h>
+
+int ip32_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ int data = regs->cp0_cause & 4;
+
+ if (is_fixup)
+ return MIPS_BE_FIXUP;
+
+ printk("Got %cbe at 0x%lx\n", data ? 'd' : 'i', regs->cp0_epc);
+ show_regs(regs);
+ dump_tlb_all();
+ while(1);
+ force_sig(SIGBUS, current);
+}
+
+void __init ip32_be_init(void)
+{
+ board_be_handler = ip32_be_handler;
+}
diff --git a/arch/mips/sgi-ip32/ip32-irq-glue.S b/arch/mips/sgi-ip32/ip32-irq-glue.S
new file mode 100644
index 000000000000..200924e1c4f5
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-irq-glue.S
@@ -0,0 +1,86 @@
+/*
+ * Low level interrupt handler for the SGI O2 aka IP32 aka Moosehead
+ *
+ * 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) 2000 Harald Koerfgen
+ * Copyright (C) 2001 Keith M Wesolowski
+ */
+#include <asm/asm.h>
+#include <asm/regdef.h>
+#include <asm/mipsregs.h>
+#include <asm/stackframe.h>
+#include <asm/addrspace.h>
+
+ .text
+ .set noreorder
+ .set noat
+ .align 5
+ NESTED(ip32_handle_int, PT_SIZE, ra)
+ .set noat
+ SAVE_ALL
+ CLI # TEST: interrupts should be off
+ .set at
+ .set noreorder
+
+ mfc0 s0,CP0_CAUSE
+
+ andi t1, s0, IE_IRQ0
+ bnez t1, handle_irq0
+ andi t1, s0, IE_IRQ1
+ bnez t1, handle_irq1
+ andi t1, s0, IE_IRQ2
+ bnez t1, handle_irq2
+ andi t1, s0, IE_IRQ3
+ bnez t1, handle_irq3
+ andi t1, s0, IE_IRQ4
+ bnez t1, handle_irq4
+ andi t1, s0, IE_IRQ5
+ bnez t1, handle_irq5
+ nop
+
+ /* Either someone has triggered the "software interrupts"
+ * or we lost an interrupt somehow. Ignore it.
+ */
+ j ret_from_irq
+ nop
+
+handle_irq0:
+ jal ip32_irq0
+ move a0, sp
+ j ret_from_irq
+ nop
+
+handle_irq1:
+ jal ip32_irq1
+ move a0, sp
+ j ret_from_irq
+ nop
+
+handle_irq2:
+ jal ip32_irq2
+ move a0, sp
+ j ret_from_irq
+ nop
+
+handle_irq3:
+ jal ip32_irq3
+ move a0, sp
+ j ret_from_irq
+ nop
+
+handle_irq4:
+ jal ip32_irq4
+ move a0, sp
+ j ret_from_irq
+ nop
+
+handle_irq5:
+ jal ip32_irq5
+ move a0, sp
+ j ret_from_irq
+ nop
+
+ END(ip32_handle_int)
diff --git a/arch/mips/sgi-ip32/ip32-irq.c b/arch/mips/sgi-ip32/ip32-irq.c
new file mode 100644
index 000000000000..fc3a8e90d763
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-irq.c
@@ -0,0 +1,590 @@
+/*
+ * Code to handle IP32 IRQs
+ *
+ * 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) 2000 Harald Koerfgen
+ * Copyright (C) 2001 Keith M Wesolowski
+ */
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/bitops.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+
+#include <asm/mipsregs.h>
+#include <asm/signal.h>
+#include <asm/system.h>
+#include <asm/time.h>
+#include <asm/ip32/crime.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/ip32_ints.h>
+
+/* issue a PIO read to make sure no PIO writes are pending */
+static void inline flush_crime_bus(void)
+{
+ volatile unsigned long junk = crime->control;
+}
+
+static void inline flush_mace_bus(void)
+{
+ volatile unsigned long junk = mace->perif.ctrl.misc;
+}
+
+#undef DEBUG_IRQ
+#ifdef DEBUG_IRQ
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/* O2 irq map
+ *
+ * IP0 -> software (ignored)
+ * IP1 -> software (ignored)
+ * IP2 -> (irq0) C crime 1.1 all interrupts; crime 1.5 ???
+ * IP3 -> (irq1) X unknown
+ * IP4 -> (irq2) X unknown
+ * IP5 -> (irq3) X unknown
+ * IP6 -> (irq4) X unknown
+ * IP7 -> (irq5) 0 CPU count/compare timer (system timer)
+ *
+ * crime: (C)
+ *
+ * CRIME_INT_STAT 31:0:
+ *
+ * 0 -> 1 Video in 1
+ * 1 -> 2 Video in 2
+ * 2 -> 3 Video out
+ * 3 -> 4 Mace ethernet
+ * 4 -> S SuperIO sub-interrupt
+ * 5 -> M Miscellaneous sub-interrupt
+ * 6 -> A Audio sub-interrupt
+ * 7 -> 8 PCI bridge errors
+ * 8 -> 9 PCI SCSI aic7xxx 0
+ * 9 -> 10 PCI SCSI aic7xxx 1
+ * 10 -> 11 PCI slot 0
+ * 11 -> 12 unused (PCI slot 1)
+ * 12 -> 13 unused (PCI slot 2)
+ * 13 -> 14 unused (PCI shared 0)
+ * 14 -> 15 unused (PCI shared 1)
+ * 15 -> 16 unused (PCI shared 2)
+ * 16 -> 17 GBE0 (E)
+ * 17 -> 18 GBE1 (E)
+ * 18 -> 19 GBE2 (E)
+ * 19 -> 20 GBE3 (E)
+ * 20 -> 21 CPU errors
+ * 21 -> 22 Memory errors
+ * 22 -> 23 RE empty edge (E)
+ * 23 -> 24 RE full edge (E)
+ * 24 -> 25 RE idle edge (E)
+ * 25 -> 26 RE empty level
+ * 26 -> 27 RE full level
+ * 27 -> 28 RE idle level
+ * 28 -> 29 unused (software 0) (E)
+ * 29 -> 30 unused (software 1) (E)
+ * 30 -> 31 unused (software 2) - crime 1.5 CPU SysCorError (E)
+ * 31 -> 32 VICE
+ *
+ * S, M, A: Use the MACE ISA interrupt register
+ * MACE_ISA_INT_STAT 31:0
+ *
+ * 0-7 -> 33-40 Audio
+ * 8 -> 41 RTC
+ * 9 -> 42 Keyboard
+ * 10 -> X Keyboard polled
+ * 11 -> 44 Mouse
+ * 12 -> X Mouse polled
+ * 13-15 -> 46-48 Count/compare timers
+ * 16-19 -> 49-52 Parallel (16 E)
+ * 20-25 -> 53-58 Serial 1 (22 E)
+ * 26-31 -> 59-64 Serial 2 (28 E)
+ *
+ * Note that this means IRQs 5-7, 43, and 45 do not exist. This is a
+ * different IRQ map than IRIX uses, but that's OK as Linux irq handling
+ * is quite different anyway.
+ */
+
+/*
+ * IRQ spinlock - Ralf says not to disable CPU interrupts,
+ * and I think he knows better.
+ */
+static DEFINE_SPINLOCK(ip32_irq_lock);
+
+/* Some initial interrupts to set up */
+extern irqreturn_t crime_memerr_intr (int irq, void *dev_id,
+ struct pt_regs *regs);
+extern irqreturn_t crime_cpuerr_intr (int irq, void *dev_id,
+ struct pt_regs *regs);
+
+struct irqaction memerr_irq = { crime_memerr_intr, SA_INTERRUPT,
+ CPU_MASK_NONE, "CRIME memory error", NULL, NULL };
+struct irqaction cpuerr_irq = { crime_cpuerr_intr, SA_INTERRUPT,
+ CPU_MASK_NONE, "CRIME CPU error", NULL, NULL };
+
+extern void ip32_handle_int(void);
+
+/*
+ * For interrupts wired from a single device to the CPU. Only the clock
+ * uses this it seems, which is IRQ 0 and IP7.
+ */
+
+static void enable_cpu_irq(unsigned int irq)
+{
+ set_c0_status(STATUSF_IP7);
+}
+
+static unsigned int startup_cpu_irq(unsigned int irq)
+{
+ enable_cpu_irq(irq);
+ return 0;
+}
+
+static void disable_cpu_irq(unsigned int irq)
+{
+ clear_c0_status(STATUSF_IP7);
+}
+
+static void end_cpu_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_cpu_irq (irq);
+}
+
+#define shutdown_cpu_irq disable_cpu_irq
+#define mask_and_ack_cpu_irq disable_cpu_irq
+
+static struct hw_interrupt_type ip32_cpu_interrupt = {
+ "IP32 CPU",
+ startup_cpu_irq,
+ shutdown_cpu_irq,
+ enable_cpu_irq,
+ disable_cpu_irq,
+ mask_and_ack_cpu_irq,
+ end_cpu_irq,
+ NULL
+};
+
+/*
+ * This is for pure CRIME interrupts - ie not MACE. The advantage?
+ * We get to split the register in half and do faster lookups.
+ */
+
+static uint64_t crime_mask;
+
+static void enable_crime_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask |= 1 << (irq - 1);
+ crime->imask = crime_mask;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static unsigned int startup_crime_irq(unsigned int irq)
+{
+ enable_crime_irq(irq);
+ return 0; /* This is probably not right; we could have pending irqs */
+}
+
+static void disable_crime_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask &= ~(1 << (irq - 1));
+ crime->imask = crime_mask;
+ flush_crime_bus();
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static void mask_and_ack_crime_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ /* Edge triggered interrupts must be cleared. */
+ if ((irq >= CRIME_GBE0_IRQ && irq <= CRIME_GBE3_IRQ)
+ || (irq >= CRIME_RE_EMPTY_E_IRQ && irq <= CRIME_RE_IDLE_E_IRQ)
+ || (irq >= CRIME_SOFT0_IRQ && irq <= CRIME_SOFT2_IRQ)) {
+ uint64_t crime_int;
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_int = crime->hard_int;
+ crime_int &= ~(1 << (irq - 1));
+ crime->hard_int = crime_int;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+ }
+ disable_crime_irq(irq);
+}
+
+static void end_crime_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_crime_irq(irq);
+}
+
+#define shutdown_crime_irq disable_crime_irq
+
+static struct hw_interrupt_type ip32_crime_interrupt = {
+ "IP32 CRIME",
+ startup_crime_irq,
+ shutdown_crime_irq,
+ enable_crime_irq,
+ disable_crime_irq,
+ mask_and_ack_crime_irq,
+ end_crime_irq,
+ NULL
+};
+
+/*
+ * This is for MACE PCI interrupts. We can decrease bus traffic by masking
+ * as close to the source as possible. This also means we can take the
+ * next chunk of the CRIME register in one piece.
+ */
+
+static unsigned long macepci_mask;
+
+static void enable_macepci_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ macepci_mask |= MACEPCI_CONTROL_INT(irq - 9);
+ mace->pci.control = macepci_mask;
+ crime_mask |= 1 << (irq - 1);
+ crime->imask = crime_mask;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static unsigned int startup_macepci_irq(unsigned int irq)
+{
+ enable_macepci_irq (irq);
+ return 0;
+}
+
+static void disable_macepci_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask &= ~(1 << (irq - 1));
+ crime->imask = crime_mask;
+ flush_crime_bus();
+ macepci_mask &= ~MACEPCI_CONTROL_INT(irq - 9);
+ mace->pci.control = macepci_mask;
+ flush_mace_bus();
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static void end_macepci_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_macepci_irq(irq);
+}
+
+#define shutdown_macepci_irq disable_macepci_irq
+#define mask_and_ack_macepci_irq disable_macepci_irq
+
+static struct hw_interrupt_type ip32_macepci_interrupt = {
+ "IP32 MACE PCI",
+ startup_macepci_irq,
+ shutdown_macepci_irq,
+ enable_macepci_irq,
+ disable_macepci_irq,
+ mask_and_ack_macepci_irq,
+ end_macepci_irq,
+ NULL
+};
+
+/* This is used for MACE ISA interrupts. That means bits 4-6 in the
+ * CRIME register.
+ */
+
+#define MACEISA_AUDIO_INT (MACEISA_AUDIO_SW_INT | \
+ MACEISA_AUDIO_SC_INT | \
+ MACEISA_AUDIO1_DMAT_INT | \
+ MACEISA_AUDIO1_OF_INT | \
+ MACEISA_AUDIO2_DMAT_INT | \
+ MACEISA_AUDIO2_MERR_INT | \
+ MACEISA_AUDIO3_DMAT_INT | \
+ MACEISA_AUDIO3_MERR_INT)
+#define MACEISA_MISC_INT (MACEISA_RTC_INT | \
+ MACEISA_KEYB_INT | \
+ MACEISA_KEYB_POLL_INT | \
+ MACEISA_MOUSE_INT | \
+ MACEISA_MOUSE_POLL_INT | \
+ MACEISA_TIMER0_INT | \
+ MACEISA_TIMER1_INT | \
+ MACEISA_TIMER2_INT)
+#define MACEISA_SUPERIO_INT (MACEISA_PARALLEL_INT | \
+ MACEISA_PAR_CTXA_INT | \
+ MACEISA_PAR_CTXB_INT | \
+ MACEISA_PAR_MERR_INT | \
+ MACEISA_SERIAL1_INT | \
+ MACEISA_SERIAL1_TDMAT_INT | \
+ MACEISA_SERIAL1_TDMAPR_INT | \
+ MACEISA_SERIAL1_TDMAME_INT | \
+ MACEISA_SERIAL1_RDMAT_INT | \
+ MACEISA_SERIAL1_RDMAOR_INT | \
+ MACEISA_SERIAL2_INT | \
+ MACEISA_SERIAL2_TDMAT_INT | \
+ MACEISA_SERIAL2_TDMAPR_INT | \
+ MACEISA_SERIAL2_TDMAME_INT | \
+ MACEISA_SERIAL2_RDMAT_INT | \
+ MACEISA_SERIAL2_RDMAOR_INT)
+
+static unsigned long maceisa_mask;
+
+static void enable_maceisa_irq (unsigned int irq)
+{
+ unsigned int crime_int = 0;
+ unsigned long flags;
+
+ DBG ("maceisa enable: %u\n", irq);
+
+ switch (irq) {
+ case MACEISA_AUDIO_SW_IRQ ... MACEISA_AUDIO3_MERR_IRQ:
+ crime_int = MACE_AUDIO_INT;
+ break;
+ case MACEISA_RTC_IRQ ... MACEISA_TIMER2_IRQ:
+ crime_int = MACE_MISC_INT;
+ break;
+ case MACEISA_PARALLEL_IRQ ... MACEISA_SERIAL2_RDMAOR_IRQ:
+ crime_int = MACE_SUPERIO_INT;
+ break;
+ }
+ DBG ("crime_int %08x enabled\n", crime_int);
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask |= crime_int;
+ crime->imask = crime_mask;
+ maceisa_mask |= 1 << (irq - 33);
+ mace->perif.ctrl.imask = maceisa_mask;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static unsigned int startup_maceisa_irq(unsigned int irq)
+{
+ enable_maceisa_irq(irq);
+ return 0;
+}
+
+static void disable_maceisa_irq(unsigned int irq)
+{
+ unsigned int crime_int = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ maceisa_mask &= ~(1 << (irq - 33));
+ if(!(maceisa_mask & MACEISA_AUDIO_INT))
+ crime_int |= MACE_AUDIO_INT;
+ if(!(maceisa_mask & MACEISA_MISC_INT))
+ crime_int |= MACE_MISC_INT;
+ if(!(maceisa_mask & MACEISA_SUPERIO_INT))
+ crime_int |= MACE_SUPERIO_INT;
+ crime_mask &= ~crime_int;
+ crime->imask = crime_mask;
+ flush_crime_bus();
+ mace->perif.ctrl.imask = maceisa_mask;
+ flush_mace_bus();
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static void mask_and_ack_maceisa_irq(unsigned int irq)
+{
+ unsigned long mace_int, flags;
+
+ switch (irq) {
+ case MACEISA_PARALLEL_IRQ:
+ case MACEISA_SERIAL1_TDMAPR_IRQ:
+ case MACEISA_SERIAL2_TDMAPR_IRQ:
+ /* edge triggered */
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ mace_int = mace->perif.ctrl.istat;
+ mace_int &= ~(1 << (irq - 33));
+ mace->perif.ctrl.istat = mace_int;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+ break;
+ }
+ disable_maceisa_irq(irq);
+}
+
+static void end_maceisa_irq(unsigned irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_maceisa_irq(irq);
+}
+
+#define shutdown_maceisa_irq disable_maceisa_irq
+
+static struct hw_interrupt_type ip32_maceisa_interrupt = {
+ "IP32 MACE ISA",
+ startup_maceisa_irq,
+ shutdown_maceisa_irq,
+ enable_maceisa_irq,
+ disable_maceisa_irq,
+ mask_and_ack_maceisa_irq,
+ end_maceisa_irq,
+ NULL
+};
+
+/* This is used for regular non-ISA, non-PCI MACE interrupts. That means
+ * bits 0-3 and 7 in the CRIME register.
+ */
+
+static void enable_mace_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask |= 1 << (irq - 1);
+ crime->imask = crime_mask;
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static unsigned int startup_mace_irq(unsigned int irq)
+{
+ enable_mace_irq(irq);
+ return 0;
+}
+
+static void disable_mace_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&ip32_irq_lock, flags);
+ crime_mask &= ~(1 << (irq - 1));
+ crime->imask = crime_mask;
+ flush_crime_bus();
+ spin_unlock_irqrestore(&ip32_irq_lock, flags);
+}
+
+static void end_mace_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_mace_irq(irq);
+}
+
+#define shutdown_mace_irq disable_mace_irq
+#define mask_and_ack_mace_irq disable_mace_irq
+
+static struct hw_interrupt_type ip32_mace_interrupt = {
+ "IP32 MACE",
+ startup_mace_irq,
+ shutdown_mace_irq,
+ enable_mace_irq,
+ disable_mace_irq,
+ mask_and_ack_mace_irq,
+ end_mace_irq,
+ NULL
+};
+
+static void ip32_unknown_interrupt(struct pt_regs *regs)
+{
+ printk ("Unknown interrupt occurred!\n");
+ printk ("cp0_status: %08x\n", read_c0_status());
+ printk ("cp0_cause: %08x\n", read_c0_cause());
+ printk ("CRIME intr mask: %016lx\n", crime->imask);
+ printk ("CRIME intr status: %016lx\n", crime->istat);
+ printk ("CRIME hardware intr register: %016lx\n", crime->hard_int);
+ printk ("MACE ISA intr mask: %08lx\n", mace->perif.ctrl.imask);
+ printk ("MACE ISA intr status: %08lx\n", mace->perif.ctrl.istat);
+ printk ("MACE PCI control register: %08x\n", mace->pci.control);
+
+ printk("Register dump:\n");
+ show_regs(regs);
+
+ printk("Please mail this report to linux-mips@linux-mips.org\n");
+ printk("Spinning...");
+ while(1) ;
+}
+
+/* CRIME 1.1 appears to deliver all interrupts to this one pin. */
+/* change this to loop over all edge-triggered irqs, exception masked out ones */
+void ip32_irq0(struct pt_regs *regs)
+{
+ uint64_t crime_int;
+ int irq = 0;
+
+ crime_int = crime->istat & crime_mask;
+ irq = ffs(crime_int);
+ crime_int = 1 << (irq - 1);
+
+ if (crime_int & CRIME_MACEISA_INT_MASK) {
+ unsigned long mace_int = mace->perif.ctrl.istat;
+ irq = ffs(mace_int & maceisa_mask) + 32;
+ }
+ DBG("*irq %u*\n", irq);
+ do_IRQ(irq, regs);
+}
+
+void ip32_irq1(struct pt_regs *regs)
+{
+ ip32_unknown_interrupt(regs);
+}
+
+void ip32_irq2(struct pt_regs *regs)
+{
+ ip32_unknown_interrupt(regs);
+}
+
+void ip32_irq3(struct pt_regs *regs)
+{
+ ip32_unknown_interrupt(regs);
+}
+
+void ip32_irq4(struct pt_regs *regs)
+{
+ ip32_unknown_interrupt(regs);
+}
+
+void ip32_irq5(struct pt_regs *regs)
+{
+ ll_timer_interrupt(IP32_R4K_TIMER_IRQ, regs);
+}
+
+void __init arch_init_irq(void)
+{
+ unsigned int irq;
+
+ /* Install our interrupt handler, then clear and disable all
+ * CRIME and MACE interrupts. */
+ crime->imask = 0;
+ crime->hard_int = 0;
+ crime->soft_int = 0;
+ mace->perif.ctrl.istat = 0;
+ mace->perif.ctrl.imask = 0;
+ set_except_vector(0, ip32_handle_int);
+
+ for (irq = 0; irq <= IP32_IRQ_MAX; irq++) {
+ hw_irq_controller *controller;
+
+ if (irq == IP32_R4K_TIMER_IRQ)
+ controller = &ip32_cpu_interrupt;
+ else if (irq <= MACE_PCI_BRIDGE_IRQ && irq >= MACE_VID_IN1_IRQ)
+ controller = &ip32_mace_interrupt;
+ else if (irq <= MACEPCI_SHARED2_IRQ && irq >= MACEPCI_SCSI0_IRQ)
+ controller = &ip32_macepci_interrupt;
+ else if (irq <= CRIME_VICE_IRQ && irq >= CRIME_GBE0_IRQ)
+ controller = &ip32_crime_interrupt;
+ else
+ controller = &ip32_maceisa_interrupt;
+
+ irq_desc[irq].status = IRQ_DISABLED;
+ irq_desc[irq].action = 0;
+ irq_desc[irq].depth = 0;
+ irq_desc[irq].handler = controller;
+ }
+ setup_irq(CRIME_MEMERR_IRQ, &memerr_irq);
+ setup_irq(CRIME_CPUERR_IRQ, &cpuerr_irq);
+
+#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
+ change_c0_status(ST0_IM, ALLINTS);
+}
diff --git a/arch/mips/sgi-ip32/ip32-memory.c b/arch/mips/sgi-ip32/ip32-memory.c
new file mode 100644
index 000000000000..fc76ca92bab9
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-memory.c
@@ -0,0 +1,49 @@
+/*
+ * 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) 2003 Keith M Wesolowski
+ * Copyright (C) 2005 Ilya A. Volynets (Total Knowledge)
+ */
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/ip32/crime.h>
+#include <asm/bootinfo.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+
+extern void crime_init(void);
+
+void __init prom_meminit (void)
+{
+ u64 base, size;
+ int bank;
+
+ crime_init();
+
+ for (bank=0; bank < CRIME_MAXBANKS; bank++) {
+ u64 bankctl = crime->bank_ctrl[bank];
+ base = (bankctl & CRIME_MEM_BANK_CONTROL_ADDR) << 25;
+ if (bank != 0 && base == 0)
+ continue;
+ size = (bankctl & CRIME_MEM_BANK_CONTROL_SDRAM_SIZE) ? 128 : 32;
+ size <<= 20;
+ if (base + size > (256 << 20))
+ base += CRIME_HI_MEM_BASE;
+
+ printk("CRIME MC: bank %u base 0x%016lx size %luMB\n",
+ bank, base, size);
+ add_memory_region (base, size, BOOT_MEM_RAM);
+ }
+}
+
+
+unsigned long __init prom_free_prom_memory (void)
+{
+ return 0;
+}
diff --git a/arch/mips/sgi-ip32/ip32-reset.c b/arch/mips/sgi-ip32/ip32-reset.c
new file mode 100644
index 000000000000..281f090e48a4
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-reset.c
@@ -0,0 +1,202 @@
+/*
+ * 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) 2001 Keith M Wesolowski
+ * Copyright (C) 2001 Paul Mundt
+ * Copyright (C) 2003 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/notifier.h>
+#include <linux/delay.h>
+#include <linux/ds17287rtc.h>
+#include <linux/interrupt.h>
+
+#include <asm/addrspace.h>
+#include <asm/irq.h>
+#include <asm/reboot.h>
+#include <asm/system.h>
+#include <asm/wbflush.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/crime.h>
+#include <asm/ip32/ip32_ints.h>
+
+#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;
+static int has_paniced, shuting_down;
+
+static void ip32_machine_restart(char *command) __attribute__((noreturn));
+static void ip32_machine_halt(void) __attribute__((noreturn));
+static void ip32_machine_power_off(void) __attribute__((noreturn));
+
+static void ip32_machine_restart(char *cmd)
+{
+ crime->control = CRIME_CONTROL_HARD_RESET;
+ while (1);
+}
+
+static inline void ip32_machine_halt(void)
+{
+ ip32_machine_power_off();
+}
+
+static void ip32_machine_power_off(void)
+{
+ volatile unsigned char reg_a, xctrl_a, xctrl_b;
+
+ disable_irq(MACEISA_RTC_IRQ);
+ reg_a = CMOS_READ(RTC_REG_A);
+
+ /* setup for kickstart & wake-up (DS12287 Ref. Man. p. 19) */
+ reg_a &= ~DS_REGA_DV2;
+ reg_a |= DS_REGA_DV1;
+
+ CMOS_WRITE(reg_a | DS_REGA_DV0, RTC_REG_A);
+ wbflush();
+ xctrl_b = CMOS_READ(DS_B1_XCTRL4B)
+ | DS_XCTRL4B_ABE | DS_XCTRL4B_KFE;
+ CMOS_WRITE(xctrl_b, DS_B1_XCTRL4B);
+ xctrl_a = CMOS_READ(DS_B1_XCTRL4A) & ~DS_XCTRL4A_IFS;
+ CMOS_WRITE(xctrl_a, DS_B1_XCTRL4A);
+ wbflush();
+ /* adios amigos... */
+ CMOS_WRITE(xctrl_a | DS_XCTRL4A_PAB, DS_B1_XCTRL4A);
+ CMOS_WRITE(reg_a, RTC_REG_A);
+ wbflush();
+ while (1);
+}
+
+static void power_timeout(unsigned long data)
+{
+ ip32_machine_power_off();
+}
+
+static void blink_timeout(unsigned long data)
+{
+ unsigned long led = mace->perif.ctrl.misc ^ MACEISA_LED_RED;
+ mace->perif.ctrl.misc = led;
+ mod_timer(&blink_timer, jiffies + data);
+}
+
+static void debounce(unsigned long data)
+{
+ volatile unsigned char reg_a, reg_c, xctrl_a;
+
+ reg_c = CMOS_READ(RTC_INTR_FLAGS);
+ CMOS_WRITE(reg_a | DS_REGA_DV0, RTC_REG_A);
+ wbflush();
+ xctrl_a = CMOS_READ(DS_B1_XCTRL4A);
+ if ((xctrl_a & DS_XCTRL4A_IFS) || (reg_c & RTC_IRQF )) {
+ /* Interrupt still being sent. */
+ debounce_timer.expires = jiffies + 50;
+ add_timer(&debounce_timer);
+
+ /* clear interrupt source */
+ CMOS_WRITE(xctrl_a & ~DS_XCTRL4A_IFS, DS_B1_XCTRL4A);
+ CMOS_WRITE(reg_a & ~DS_REGA_DV0, RTC_REG_A);
+ return;
+ }
+ CMOS_WRITE(reg_a & ~DS_REGA_DV0, RTC_REG_A);
+
+ if (has_paniced)
+ ip32_machine_restart(NULL);
+
+ enable_irq(MACEISA_RTC_IRQ);
+}
+
+static inline void ip32_power_button(void)
+{
+ if (has_paniced)
+ return;
+
+ if (shuting_down || kill_proc(1, SIGINT, 1)) {
+ /* No init process or button pressed twice. */
+ ip32_machine_power_off();
+ }
+
+ shuting_down = 1;
+ 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);
+}
+
+static irqreturn_t ip32_rtc_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+ volatile unsigned char reg_c;
+
+ reg_c = CMOS_READ(RTC_INTR_FLAGS);
+ if (!(reg_c & RTC_IRQF)) {
+ printk(KERN_WARNING
+ "%s: RTC IRQ without RTC_IRQF\n", __FUNCTION__);
+ }
+ /* Wait until interrupt goes away */
+ disable_irq(MACEISA_RTC_IRQ);
+ init_timer(&debounce_timer);
+ debounce_timer.function = debounce;
+ debounce_timer.expires = jiffies + 50;
+ add_timer(&debounce_timer);
+
+ printk(KERN_DEBUG "Power button pressed\n");
+ ip32_power_button();
+ return IRQ_HANDLED;
+}
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ unsigned long led;
+
+ if (has_paniced)
+ return NOTIFY_DONE;
+ has_paniced = 1;
+
+ /* turn off the green LED */
+ led = mace->perif.ctrl.misc | MACEISA_LED_GREEN;
+ mace->perif.ctrl.misc = led;
+
+ blink_timer.data = PANIC_FREQ;
+ blink_timeout(PANIC_FREQ);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+ .notifier_call = panic_event,
+};
+
+static __init int ip32_reboot_setup(void)
+{
+ /* turn on the green led only */
+ unsigned long led = mace->perif.ctrl.misc;
+ led |= MACEISA_LED_RED;
+ led &= ~MACEISA_LED_GREEN;
+ mace->perif.ctrl.misc = led;
+
+ _machine_restart = ip32_machine_restart;
+ _machine_halt = ip32_machine_halt;
+ _machine_power_off = ip32_machine_power_off;
+
+ init_timer(&blink_timer);
+ blink_timer.function = blink_timeout;
+ notifier_chain_register(&panic_notifier_list, &panic_block);
+
+ request_irq(MACEISA_RTC_IRQ, ip32_rtc_int, 0, "rtc", NULL);
+
+ return 0;
+}
+
+subsys_initcall(ip32_reboot_setup);
diff --git a/arch/mips/sgi-ip32/ip32-setup.c b/arch/mips/sgi-ip32/ip32-setup.c
new file mode 100644
index 000000000000..8d270be58224
--- /dev/null
+++ b/arch/mips/sgi-ip32/ip32-setup.c
@@ -0,0 +1,159 @@
+/*
+ * IP32 basic setup
+ *
+ * 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) 2000 Harald Koerfgen
+ * Copyright (C) 2002, 2003, 2005 Ilya A. Volynets
+ */
+#include <linux/config.h>
+#include <linux/console.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/mc146818rtc.h>
+#include <linux/param.h>
+#include <linux/sched.h>
+
+#include <asm/bootinfo.h>
+#include <asm/mc146818-time.h>
+#include <asm/mipsregs.h>
+#include <asm/mmu_context.h>
+#include <asm/sgialib.h>
+#include <asm/time.h>
+#include <asm/traps.h>
+#include <asm/io.h>
+#include <asm/ip32/crime.h>
+#include <asm/ip32/mace.h>
+#include <asm/ip32/ip32_ints.h>
+
+extern void ip32_be_init(void);
+extern void crime_init(void);
+
+#ifdef CONFIG_SGI_O2MACE_ETH
+/*
+ * This is taken care of in here 'cause they say using Arc later on is
+ * problematic
+ */
+extern char o2meth_eaddr[8];
+static inline unsigned char str2hexnum(unsigned char c)
+{
+ if (c >= '0' && c <= '9')
+ return c - '0';
+ if (c >= 'a' && c <= 'f')
+ return c - 'a' + 10;
+ return 0; /* foo */
+}
+
+static inline void str2eaddr(unsigned char *ea, unsigned char *str)
+{
+ int i;
+
+ for (i = 0; i < 6; i++) {
+ unsigned char num;
+
+ if(*str == ':')
+ str++;
+ num = str2hexnum(*str++) << 4;
+ num |= (str2hexnum(*str++));
+ ea[i] = num;
+ }
+}
+#endif
+
+#ifdef CONFIG_SERIAL_8250
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+extern int early_serial_setup(struct uart_port *port);
+
+#define STD_COM_FLAGS (ASYNC_SKIP_TEST)
+#define BASE_BAUD (1843200 / 16)
+
+#endif /* CONFIG_SERIAL_8250 */
+
+/* An arbitrary time; this can be decreased if reliability looks good */
+#define WAIT_MS 10
+
+void __init ip32_time_init(void)
+{
+ printk(KERN_INFO "Calibrating system timer... ");
+ write_c0_count(0);
+ crime->timer = 0;
+ while (crime->timer < CRIME_MASTER_FREQ * WAIT_MS / 1000) ;
+ mips_hpt_frequency = read_c0_count() * 1000 / WAIT_MS;
+ printk("%d MHz CPU detected\n", mips_hpt_frequency * 2 / 1000000);
+}
+
+void __init ip32_timer_setup(struct irqaction *irq)
+{
+ irq->handler = no_action;
+ setup_irq(IP32_R4K_TIMER_IRQ, irq);
+}
+
+static int __init ip32_setup(void)
+{
+ board_be_init = ip32_be_init;
+
+ rtc_get_time = mc146818_get_cmos_time;
+ rtc_set_mmss = mc146818_set_rtc_mmss;
+
+ board_time_init = ip32_time_init;
+ board_timer_setup = ip32_timer_setup;
+
+#ifdef CONFIG_SERIAL_8250
+ {
+ static struct uart_port o2_serial[2];
+
+ memset(o2_serial, 0, sizeof(o2_serial));
+ o2_serial[0].type = PORT_16550A;
+ o2_serial[0].line = 0;
+ o2_serial[0].irq = MACEISA_SERIAL1_IRQ;
+ o2_serial[0].flags = STD_COM_FLAGS;
+ o2_serial[0].uartclk = BASE_BAUD * 16;
+ o2_serial[0].iotype = UPIO_MEM;
+ o2_serial[0].membase = (char *)&mace->isa.serial1;
+ o2_serial[0].fifosize = 14;
+ /* How much to shift register offset by. Each UART register
+ * is replicated over 256 byte space */
+ o2_serial[0].regshift = 8;
+ o2_serial[1].type = PORT_16550A;
+ o2_serial[1].line = 1;
+ o2_serial[1].irq = MACEISA_SERIAL2_IRQ;
+ o2_serial[1].flags = STD_COM_FLAGS;
+ o2_serial[1].uartclk = BASE_BAUD * 16;
+ o2_serial[1].iotype = UPIO_MEM;
+ o2_serial[1].membase = (char *)&mace->isa.serial2;
+ o2_serial[1].fifosize = 14;
+ o2_serial[1].regshift = 8;
+
+ early_serial_setup(&o2_serial[0]);
+ early_serial_setup(&o2_serial[1]);
+ }
+#endif
+#ifdef CONFIG_SGI_O2MACE_ETH
+ {
+ char *mac = ArcGetEnvironmentVariable("eaddr");
+ str2eaddr(o2meth_eaddr, mac);
+ }
+#endif
+
+#if defined(CONFIG_SERIAL_CORE_CONSOLE)
+ {
+ char* con = ArcGetEnvironmentVariable("console");
+ if (con && *con == 'd') {
+ static char options[8];
+ char *baud = ArcGetEnvironmentVariable("dbaud");
+ if (baud)
+ strcpy(options, baud);
+ add_preferred_console("ttyS", *(con + 1) == '2' ? 1 : 0,
+ baud ? options : NULL);
+ }
+ }
+#endif
+
+ return 0;
+}
+
+early_initcall(ip32_setup);