aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/sgi-ip27
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-ip27
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-ip27')
-rw-r--r--arch/mips/sgi-ip27/Makefile12
-rw-r--r--arch/mips/sgi-ip27/TODO23
-rw-r--r--arch/mips/sgi-ip27/ip27-berr.c94
-rw-r--r--arch/mips/sgi-ip27/ip27-console.c76
-rw-r--r--arch/mips/sgi-ip27/ip27-dbgio.c60
-rw-r--r--arch/mips/sgi-ip27/ip27-hubio.c186
-rw-r--r--arch/mips/sgi-ip27/ip27-init.c252
-rw-r--r--arch/mips/sgi-ip27/ip27-irq-glue.S45
-rw-r--r--arch/mips/sgi-ip27/ip27-irq.c457
-rw-r--r--arch/mips/sgi-ip27/ip27-klconfig.c135
-rw-r--r--arch/mips/sgi-ip27/ip27-klnuma.c135
-rw-r--r--arch/mips/sgi-ip27/ip27-memory.c586
-rw-r--r--arch/mips/sgi-ip27/ip27-nmi.c249
-rw-r--r--arch/mips/sgi-ip27/ip27-reset.c81
-rw-r--r--arch/mips/sgi-ip27/ip27-smp.c225
-rw-r--r--arch/mips/sgi-ip27/ip27-timer.c243
-rw-r--r--arch/mips/sgi-ip27/ip27-xtalk.c135
17 files changed, 2994 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip27/Makefile b/arch/mips/sgi-ip27/Makefile
new file mode 100644
index 00000000000..4ba340780c3
--- /dev/null
+++ b/arch/mips/sgi-ip27/Makefile
@@ -0,0 +1,12 @@
+#
+# Makefile for the IP27 specific kernel interface routines under Linux.
+#
+
+obj-y := ip27-berr.o ip27-console.o ip27-irq.o ip27-init.o ip27-irq-glue.o \
+ ip27-klconfig.o ip27-klnuma.o ip27-memory.o ip27-nmi.o ip27-reset.o \
+ ip27-timer.o ip27-hubio.o ip27-xtalk.o
+
+obj-$(CONFIG_KGDB) += ip27-dbgio.o
+obj-$(CONFIG_SMP) += ip27-smp.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sgi-ip27/TODO b/arch/mips/sgi-ip27/TODO
new file mode 100644
index 00000000000..32106131b0d
--- /dev/null
+++ b/arch/mips/sgi-ip27/TODO
@@ -0,0 +1,23 @@
+1. Need to figure out why PCI writes to the IOC3 hang, and if it is okay
+not to write to the IOC3 ever.
+2. Need to figure out RRB allocation in bridge_startup().
+3. Need to figure out why address swaizzling is needed in inw/outw for
+Qlogic scsi controllers.
+4. Need to integrate ip27-klconfig.c:find_lboard and
+ip27-init.c:find_lbaord_real. DONE
+5. Is it okay to set calias space on all nodes as 0, instead of 8k as
+in irix?
+6. Investigate why things do not work without the setup_test() call
+being invoked on all nodes in ip27-memory.c.
+7. Too many CLIs in the locore handlers :
+For the low level handlers set up by set_except_vector(),
+__tlb_refill_debug_tramp, __xtlb_refill_debug_tramp and cacheerror,
+investigate whether the code should do CLI, STI or KMODE.
+8. Too many do_page_faults invoked - investigate.
+9. start_thread must turn off UX64 ... and define tlb_refill_debug.
+10. Need a bad pmd table, bad pte table. __bad_pmd_table/__bad_pagetable
+does not agree with pgd_bad/pmd_bad.
+11. All intrs (ip27_do_irq handlers) are targetted at cpu A on the node.
+This might need to change later. Only the timer intr is set up to be
+received on both Cpu A and B. (ip27_do_irq()/bridge_startup())
+13. Cache flushing (specially the SMP version) has to be investigated.
diff --git a/arch/mips/sgi-ip27/ip27-berr.c b/arch/mips/sgi-ip27/ip27-berr.c
new file mode 100644
index 00000000000..e1829a5d3b1
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-berr.c
@@ -0,0 +1,94 @@
+/*
+ * 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/module.h>
+
+#include <asm/module.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/sn0/hub.h>
+#include <asm/tlbdebug.h>
+#include <asm/traps.h>
+#include <asm/uaccess.h>
+
+extern void dump_tlb_addr(unsigned long addr);
+extern void dump_tlb_all(void);
+
+static void dump_hub_information(unsigned long errst0, unsigned long errst1)
+{
+ static char *err_type[2][8] = {
+ { NULL, "Uncached Partial Read PRERR", "DERR", "Read Timeout",
+ NULL, NULL, NULL, NULL },
+ { "WERR", "Uncached Partial Write", "PWERR", "Write Timeout",
+ NULL, NULL, NULL, NULL }
+ };
+ int wrb = errst1 & PI_ERR_ST1_WRBRRB_MASK;
+
+ if (!(errst0 & PI_ERR_ST0_VALID_MASK)) {
+ printk("Hub does not contain valid error information\n");
+ return;
+ }
+
+
+ printk("Hub has valid error information:\n");
+ if (errst0 & PI_ERR_ST0_OVERRUN_MASK)
+ printk("Overrun is set. Error stack may contain additional "
+ "information.\n");
+ printk("Hub error address is %08lx\n",
+ (errst0 & PI_ERR_ST0_ADDR_MASK) >> (PI_ERR_ST0_ADDR_SHFT - 3));
+ printk("Incoming message command 0x%lx\n",
+ (errst0 & PI_ERR_ST0_CMD_MASK) >> PI_ERR_ST0_CMD_SHFT);
+ printk("Supplemental field of incoming message is 0x%lx\n",
+ (errst0 & PI_ERR_ST0_SUPPL_MASK) >> PI_ERR_ST0_SUPPL_SHFT);
+ printk("T5 Rn (for RRB only) is 0x%lx\n",
+ (errst0 & PI_ERR_ST0_REQNUM_MASK) >> PI_ERR_ST0_REQNUM_SHFT);
+ printk("Error type is %s\n", err_type[wrb]
+ [(errst0 & PI_ERR_ST0_TYPE_MASK) >> PI_ERR_ST0_TYPE_SHFT]
+ ? : "invalid");
+}
+
+int ip27_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ unsigned long errst0, errst1;
+ int data = regs->cp0_cause & 4;
+ int cpu = LOCAL_HUB_L(PI_CPU_NUM);
+
+ if (is_fixup)
+ return MIPS_BE_FIXUP;
+
+ printk("Slice %c got %cbe at 0x%lx\n", 'A' + cpu, data ? 'd' : 'i',
+ regs->cp0_epc);
+ printk("Hub information:\n");
+ printk("ERR_INT_PEND = 0x%06lx\n", LOCAL_HUB_L(PI_ERR_INT_PEND));
+ errst0 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS0_B : PI_ERR_STATUS0_A);
+ errst1 = LOCAL_HUB_L(cpu ? PI_ERR_STATUS1_B : PI_ERR_STATUS1_A);
+ dump_hub_information(errst0, errst1);
+ show_regs(regs);
+ dump_tlb_all();
+ while(1);
+ force_sig(SIGBUS, current);
+}
+
+void __init ip27_be_init(void)
+{
+ /* XXX Initialize all the Hub & Bridge error handling here. */
+ int cpu = LOCAL_HUB_L(PI_CPU_NUM);
+ int cpuoff = cpu << 8;
+
+ board_be_handler = ip27_be_handler;
+
+ LOCAL_HUB_S(PI_ERR_INT_PEND,
+ cpu ? PI_ERR_CLEAR_ALL_B : PI_ERR_CLEAR_ALL_A);
+ LOCAL_HUB_S(PI_ERR_INT_MASK_A + cpuoff, 0);
+ LOCAL_HUB_S(PI_ERR_STACK_ADDR_A + cpuoff, 0);
+ LOCAL_HUB_S(PI_ERR_STACK_SIZE, 0); /* Disable error stack */
+ LOCAL_HUB_S(PI_SYSAD_ERRCHK_EN, PI_SYSAD_CHECK_ALL);
+}
diff --git a/arch/mips/sgi-ip27/ip27-console.c b/arch/mips/sgi-ip27/ip27-console.c
new file mode 100644
index 00000000000..d97f5b5ef84
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-console.c
@@ -0,0 +1,76 @@
+/*
+ * 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, 2002 Ralf Baechle
+ */
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kdev_t.h>
+#include <linux/major.h>
+#include <linux/termios.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <asm/page.h>
+#include <asm/semaphore.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/sn0/hub.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/sn_private.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+
+#define IOC3_CLK (22000000 / 3)
+#define IOC3_FLAGS (0)
+
+static inline struct ioc3_uartregs *console_uart(void)
+{
+ struct ioc3 *ioc3;
+
+ ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(get_nasid())->memory_base;
+
+ return &ioc3->sregs.uarta;
+}
+
+void prom_putchar(char c)
+{
+ struct ioc3_uartregs *uart = console_uart();
+
+ while ((uart->iu_lsr & 0x20) == 0);
+ uart->iu_thr = c;
+}
+
+char __init prom_getchar(void)
+{
+ return 0;
+}
+
+static void inline ioc3_console_probe(void)
+{
+ struct uart_port up;
+
+ /*
+ * Register to interrupt zero because we share the interrupt with
+ * the serial driver which we don't properly support yet.
+ */
+ memset(&up, 0, sizeof(up));
+ up.membase = (unsigned char *) console_uart();
+ up.irq = 0;
+ up.uartclk = IOC3_CLK;
+ up.regshift = 0;
+ up.iotype = UPIO_MEM;
+ up.flags = IOC3_FLAGS;
+ up.line = 0;
+
+ if (early_serial_setup(&up))
+ printk(KERN_ERR "Early serial init of port 0 failed\n");
+}
+
+__init void ip27_setup_console(void)
+{
+ ioc3_console_probe();
+}
diff --git a/arch/mips/sgi-ip27/ip27-dbgio.c b/arch/mips/sgi-ip27/ip27-dbgio.c
new file mode 100644
index 00000000000..08fd88b36f8
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-dbgio.c
@@ -0,0 +1,60 @@
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Copyright 2004 Ralf Baechle <ralf@linux-mips.org>
+ */
+#include <asm/sn/addrs.h>
+#include <asm/sn/sn0/hub.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/ioc3.h>
+#include <asm/sn/sn_private.h>
+
+#include <linux/serial.h>
+#include <linux/serial_core.h>
+#include <linux/serial_reg.h>
+
+#define IOC3_CLK (22000000 / 3)
+#define IOC3_FLAGS (0)
+
+static inline struct ioc3_uartregs *console_uart(void)
+{
+ struct ioc3 *ioc3;
+
+ ioc3 = (struct ioc3 *)KL_CONFIG_CH_CONS_INFO(get_nasid())->memory_base;
+
+ return &ioc3->sregs.uarta;
+}
+
+unsigned char getDebugChar(void)
+{
+ struct ioc3_uartregs *uart = console_uart();
+
+ while ((uart->iu_lsr & UART_LSR_DR) == 0);
+ return uart->iu_rbr;
+}
+
+void putDebugChar(unsigned char c)
+{
+ struct ioc3_uartregs *uart = console_uart();
+
+ while ((uart->iu_lsr & UART_LSR_THRE) == 0);
+ uart->iu_thr = c;
+}
diff --git a/arch/mips/sgi-ip27/ip27-hubio.c b/arch/mips/sgi-ip27/ip27-hubio.c
new file mode 100644
index 00000000000..524b371f939
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-hubio.c
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 1992-1997, 2000-2003 Silicon Graphics, Inc.
+ * Copyright (C) 2004 Christoph Hellwig.
+ * Released under GPL v2.
+ *
+ * Support functions for the HUB ASIC - mostly PIO mapping related.
+ */
+
+#include <linux/bitops.h>
+#include <linux/string.h>
+#include <linux/mmzone.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/hub.h>
+
+
+static int force_fire_and_forget = 1;
+
+/**
+ * hub_pio_map - establish a HUB PIO mapping
+ *
+ * @hub: hub to perform PIO mapping on
+ * @widget: widget ID to perform PIO mapping for
+ * @xtalk_addr: xtalk_address that needs to be mapped
+ * @size: size of the PIO mapping
+ *
+ **/
+unsigned long hub_pio_map(cnodeid_t cnode, xwidgetnum_t widget,
+ unsigned long xtalk_addr, size_t size)
+{
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode);
+ volatile hubreg_t junk;
+ unsigned i;
+
+ /* use small-window mapping if possible */
+ if ((xtalk_addr % SWIN_SIZE) + size <= SWIN_SIZE)
+ return NODE_SWIN_BASE(nasid, widget) + (xtalk_addr % SWIN_SIZE);
+
+ if ((xtalk_addr % BWIN_SIZE) + size > BWIN_SIZE) {
+ printk(KERN_WARNING "PIO mapping at hub %d widget %d addr 0x%lx"
+ " too big (%ld)\n",
+ nasid, widget, xtalk_addr, size);
+ return 0;
+ }
+
+ xtalk_addr &= ~(BWIN_SIZE-1);
+ for (i = 0; i < HUB_NUM_BIG_WINDOW; i++) {
+ if (test_and_set_bit(i, hub_data(cnode)->h_bigwin_used))
+ continue;
+
+ /*
+ * The code below does a PIO write to setup an ITTE entry.
+ *
+ * We need to prevent other CPUs from seeing our updated
+ * memory shadow of the ITTE (in the piomap) until the ITTE
+ * entry is actually set up; otherwise, another CPU might
+ * attempt a PIO prematurely.
+ *
+ * Also, the only way we can know that an entry has been
+ * received by the hub and can be used by future PIO reads/
+ * writes is by reading back the ITTE entry after writing it.
+ *
+ * For these two reasons, we PIO read back the ITTE entry
+ * after we write it.
+ */
+ IIO_ITTE_PUT(nasid, i, HUB_PIO_MAP_TO_MEM, widget, xtalk_addr);
+ junk = HUB_L(IIO_ITTE_GET(nasid, i));
+
+ return NODE_BWIN_BASE(nasid, widget) + (xtalk_addr % BWIN_SIZE);
+ }
+
+ printk(KERN_WARNING "unable to establish PIO mapping for at"
+ " hub %d widget %d addr 0x%lx\n",
+ nasid, widget, xtalk_addr);
+ return 0;
+}
+
+
+/*
+ * hub_setup_prb(nasid, prbnum, credits, conveyor)
+ *
+ * Put a PRB into fire-and-forget mode if conveyor isn't set. Otherwise,
+ * put it into conveyor belt mode with the specified number of credits.
+ */
+static void hub_setup_prb(nasid_t nasid, int prbnum, int credits)
+{
+ iprb_t prb;
+ int prb_offset;
+
+ /*
+ * Get the current register value.
+ */
+ prb_offset = IIO_IOPRB(prbnum);
+ prb.iprb_regval = REMOTE_HUB_L(nasid, prb_offset);
+
+ /*
+ * Clear out some fields.
+ */
+ prb.iprb_ovflow = 1;
+ prb.iprb_bnakctr = 0;
+ prb.iprb_anakctr = 0;
+
+ /*
+ * Enable or disable fire-and-forget mode.
+ */
+ prb.iprb_ff = force_fire_and_forget ? 1 : 0;
+
+ /*
+ * Set the appropriate number of PIO cresits for the widget.
+ */
+ prb.iprb_xtalkctr = credits;
+
+ /*
+ * Store the new value to the register.
+ */
+ REMOTE_HUB_S(nasid, prb_offset, prb.iprb_regval);
+}
+
+/**
+ * hub_set_piomode - set pio mode for a given hub
+ *
+ * @nasid: physical node ID for the hub in question
+ *
+ * Put the hub into either "PIO conveyor belt" mode or "fire-and-forget" mode.
+ * To do this, we have to make absolutely sure that no PIOs are in progress
+ * so we turn off access to all widgets for the duration of the function.
+ *
+ * XXX - This code should really check what kind of widget we're talking
+ * to. Bridges can only handle three requests, but XG will do more.
+ * How many can crossbow handle to widget 0? We're assuming 1.
+ *
+ * XXX - There is a bug in the crossbow that link reset PIOs do not
+ * return write responses. The easiest solution to this problem is to
+ * leave widget 0 (xbow) in fire-and-forget mode at all times. This
+ * only affects pio's to xbow registers, which should be rare.
+ **/
+static void hub_set_piomode(nasid_t nasid)
+{
+ hubreg_t ii_iowa;
+ hubii_wcr_t ii_wcr;
+ unsigned i;
+
+ ii_iowa = REMOTE_HUB_L(nasid, IIO_OUTWIDGET_ACCESS);
+ REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, 0);
+
+ ii_wcr.wcr_reg_value = REMOTE_HUB_L(nasid, IIO_WCR);
+
+ if (ii_wcr.iwcr_dir_con) {
+ /*
+ * Assume a bridge here.
+ */
+ hub_setup_prb(nasid, 0, 3);
+ } else {
+ /*
+ * Assume a crossbow here.
+ */
+ hub_setup_prb(nasid, 0, 1);
+ }
+
+ /*
+ * XXX - Here's where we should take the widget type into
+ * when account assigning credits.
+ */
+ for (i = HUB_WIDGET_ID_MIN; i <= HUB_WIDGET_ID_MAX; i++)
+ hub_setup_prb(nasid, i, 3);
+
+ REMOTE_HUB_S(nasid, IIO_OUTWIDGET_ACCESS, ii_iowa);
+}
+
+/*
+ * hub_pio_init - PIO-related hub initalization
+ *
+ * @hub: hubinfo structure for our hub
+ */
+void hub_pio_init(cnodeid_t cnode)
+{
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode);
+ unsigned i;
+
+ /* initialize big window piomaps for this hub */
+ bitmap_zero(hub_data(cnode)->h_bigwin_used, HUB_NUM_BIG_WINDOW);
+ for (i = 0; i < HUB_NUM_BIG_WINDOW; i++)
+ IIO_ITTE_DISABLE(nasid, i);
+
+ hub_set_piomode(nasid);
+}
diff --git a/arch/mips/sgi-ip27/ip27-init.c b/arch/mips/sgi-ip27/ip27-init.c
new file mode 100644
index 00000000000..6dcee5c46c7
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-init.c
@@ -0,0 +1,252 @@
+/*
+ * 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 - 2001 by Kanoj Sarcar (kanoj@sgi.com)
+ * Copyright (C) 2000 - 2001 by Silicon Graphics, Inc.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/cpumask.h>
+#include <asm/cpu.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/time.h>
+#include <asm/sn/types.h>
+#include <asm/sn/sn0/addrs.h>
+#include <asm/sn/sn0/hubni.h>
+#include <asm/sn/sn0/hubio.h>
+#include <asm/sn/klconfig.h>
+#include <asm/sn/ioc3.h>
+#include <asm/mipsregs.h>
+#include <asm/sn/gda.h>
+#include <asm/sn/hub.h>
+#include <asm/sn/intr.h>
+#include <asm/current.h>
+#include <asm/smp.h>
+#include <asm/processor.h>
+#include <asm/mmu_context.h>
+#include <asm/thread_info.h>
+#include <asm/sn/launch.h>
+#include <asm/sn/sn_private.h>
+#include <asm/sn/sn0/ip27.h>
+#include <asm/sn/mapped_kernel.h>
+
+#define CPU_NONE (cpuid_t)-1
+
+static DECLARE_BITMAP(hub_init_mask, MAX_COMPACT_NODES);
+nasid_t master_nasid = INVALID_NASID;
+
+cnodeid_t nasid_to_compact_node[MAX_NASIDS];
+nasid_t compact_to_nasid_node[MAX_COMPACT_NODES];
+cnodeid_t cpuid_to_compact_node[MAXCPUS];
+
+EXPORT_SYMBOL(nasid_to_compact_node);
+
+extern void pcibr_setup(cnodeid_t);
+
+extern void xtalk_probe_node(cnodeid_t nid);
+
+static void __init per_hub_init(cnodeid_t cnode)
+{
+ struct hub_data *hub = hub_data(cnode);
+ nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode);
+
+ cpu_set(smp_processor_id(), hub->h_cpus);
+
+ if (test_and_set_bit(cnode, hub_init_mask))
+ return;
+
+ /*
+ * Set CRB timeout at 5ms, (< PI timeout of 10ms)
+ */
+ REMOTE_HUB_S(nasid, IIO_ICTP, 0x800);
+ REMOTE_HUB_S(nasid, IIO_ICTO, 0xff);
+
+ hub_rtc_init(cnode);
+ xtalk_probe_node(cnode);
+
+#ifdef CONFIG_REPLICATE_EXHANDLERS
+ /*
+ * If this is not a headless node initialization,
+ * copy over the caliased exception handlers.
+ */
+ if (get_compact_nodeid() == cnode) {
+ extern char except_vec2_generic, except_vec3_generic;
+ extern void build_tlb_refill_handler(void);
+
+ memcpy((void *)(CKSEG0 + 0x100), &except_vec2_generic, 0x80);
+ memcpy((void *)(CKSEG0 + 0x180), &except_vec3_generic, 0x80);
+ build_tlb_refill_handler();
+ memcpy((void *)(CKSEG0 + 0x100), (void *) CKSEG0, 0x80);
+ memcpy((void *)(CKSEG0 + 0x180), &except_vec3_generic, 0x100);
+ __flush_cache_all();
+ }
+#endif
+}
+
+void __init per_cpu_init(void)
+{
+ int cpu = smp_processor_id();
+ int slice = LOCAL_HUB_L(PI_CPU_NUM);
+ cnodeid_t cnode = get_compact_nodeid();
+ struct hub_data *hub = hub_data(cnode);
+ struct slice_data *si = hub->slice + slice;
+ int i;
+
+ if (test_and_set_bit(slice, &hub->slice_map))
+ return;
+
+ clear_c0_status(ST0_IM);
+
+ for (i = 0; i < LEVELS_PER_SLICE; i++)
+ si->level_to_irq[i] = -1;
+
+ /*
+ * Some interrupts are reserved by hardware or by software convention.
+ * Mark these as reserved right away so they won't be used accidently
+ * later.
+ */
+ for (i = 0; i <= BASE_PCI_IRQ; i++) {
+ __set_bit(i, si->irq_alloc_mask);
+ LOCAL_HUB_S(PI_INT_PEND_MOD, i);
+ }
+
+ __set_bit(IP_PEND0_6_63, si->irq_alloc_mask);
+ LOCAL_HUB_S(PI_INT_PEND_MOD, IP_PEND0_6_63);
+
+ for (i = NI_BRDCAST_ERR_A; i <= MSC_PANIC_INTR; i++) {
+ __set_bit(i, si->irq_alloc_mask + 1);
+ LOCAL_HUB_S(PI_INT_PEND_MOD, i);
+ }
+
+ LOCAL_HUB_L(PI_INT_PEND0);
+
+ /*
+ * We use this so we can find the local hub's data as fast as only
+ * possible.
+ */
+ cpu_data[cpu].data = si;
+
+ cpu_time_init();
+ install_ipi();
+
+ /* Install our NMI handler if symmon hasn't installed one. */
+ install_cpu_nmi_handler(cputoslice(cpu));
+
+ set_c0_status(SRB_DEV0 | SRB_DEV1);
+
+ per_hub_init(cnode);
+}
+
+/*
+ * get_nasid() returns the physical node id number of the caller.
+ */
+nasid_t
+get_nasid(void)
+{
+ return (nasid_t)((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_NODEID_MASK)
+ >> NSRI_NODEID_SHFT);
+}
+
+/*
+ * Map the physical node id to a virtual node id (virtual node ids are contiguous).
+ */
+cnodeid_t get_compact_nodeid(void)
+{
+ return NASID_TO_COMPACT_NODEID(get_nasid());
+}
+
+/* Extracted from the IOC3 meta driver. FIXME. */
+static inline void ioc3_sio_init(void)
+{
+ struct ioc3 *ioc3;
+ nasid_t nid;
+ long loops;
+
+ nid = get_nasid();
+ ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
+
+ ioc3->sscr_a = 0; /* PIO mode for uarta. */
+ ioc3->sscr_b = 0; /* PIO mode for uartb. */
+ ioc3->sio_iec = ~0;
+ ioc3->sio_ies = (SIO_IR_SA_INT | SIO_IR_SB_INT);
+
+ loops=1000000; while(loops--);
+ ioc3->sregs.uarta.iu_fcr = 0;
+ ioc3->sregs.uartb.iu_fcr = 0;
+ loops=1000000; while(loops--);
+}
+
+static inline void ioc3_eth_init(void)
+{
+ struct ioc3 *ioc3;
+ nasid_t nid;
+
+ nid = get_nasid();
+ ioc3 = (struct ioc3 *) KL_CONFIG_CH_CONS_INFO(nid)->memory_base;
+
+ ioc3->eier = 0;
+}
+
+extern void ip27_setup_console(void);
+extern void ip27_time_init(void);
+extern void ip27_reboot_setup(void);
+
+static int __init ip27_setup(void)
+{
+ hubreg_t p, e, n_mode;
+ nasid_t nid;
+
+ ip27_setup_console();
+ ip27_reboot_setup();
+
+ /*
+ * hub_rtc init and cpu clock intr enabled for later calibrate_delay.
+ */
+ nid = get_nasid();
+ printk("IP27: Running on node %d.\n", nid);
+
+ p = LOCAL_HUB_L(PI_CPU_PRESENT_A) & 1;
+ e = LOCAL_HUB_L(PI_CPU_ENABLE_A) & 1;
+ printk("Node %d has %s primary CPU%s.\n", nid,
+ p ? "a" : "no",
+ e ? ", CPU is running" : "");
+
+ p = LOCAL_HUB_L(PI_CPU_PRESENT_B) & 1;
+ e = LOCAL_HUB_L(PI_CPU_ENABLE_B) & 1;
+ printk("Node %d has %s secondary CPU%s.\n", nid,
+ p ? "a" : "no",
+ e ? ", CPU is running" : "");
+
+ /*
+ * Try to catch kernel missconfigurations and give user an
+ * indication what option to select.
+ */
+ n_mode = LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_MORENODES_MASK;
+ printk("Machine is in %c mode.\n", n_mode ? 'N' : 'M');
+#ifdef CONFIG_SGI_SN0_N_MODE
+ if (!n_mode)
+ panic("Kernel compiled for M mode.");
+#else
+ if (n_mode)
+ panic("Kernel compiled for N mode.");
+#endif
+
+ ioc3_sio_init();
+ ioc3_eth_init();
+ per_cpu_init();
+
+ set_io_port_base(IO_BASE);
+
+ board_time_init = ip27_time_init;
+
+ return 0;
+}
+
+early_initcall(ip27_setup);
diff --git a/arch/mips/sgi-ip27/ip27-irq-glue.S b/arch/mips/sgi-ip27/ip27-irq-glue.S
new file mode 100644
index 00000000000..c304df715e0
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-irq-glue.S
@@ -0,0 +1,45 @@
+/*
+ * 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) 1999 Ralf Baechle
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ */
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+ .text
+ .align 5
+NESTED(ip27_irq, PT_SIZE, sp)
+ SAVE_ALL
+ CLI
+
+ mfc0 s0, CP0_CAUSE
+ mfc0 t0, CP0_STATUS
+ and s0, t0
+ move a0, sp
+ PTR_LA ra, ret_from_irq
+
+ /* First check for RT interrupt. */
+ andi t0, s0, CAUSEF_IP4
+ bnez t0, ip4
+ andi t0, s0, CAUSEF_IP2
+ bnez t0, ip2
+ andi t0, s0, CAUSEF_IP3
+ bnez t0, ip3
+ andi t0, s0, CAUSEF_IP5
+ bnez t0, ip5
+ andi t0, s0, CAUSEF_IP6
+ bnez t0, ip6
+ j ra
+
+ip2: j ip27_do_irq_mask0 # PI_INT_PEND_0 or CC_PEND_{A|B}
+ip3: j ip27_do_irq_mask1 # PI_INT_PEND_1
+ip4: j ip27_rt_timer_interrupt
+ip5: j ip27_prof_timer
+ip6: j ip27_hub_error
+
+ END(ip27_irq)
diff --git a/arch/mips/sgi-ip27/ip27-irq.c b/arch/mips/sgi-ip27/ip27-irq.c
new file mode 100644
index 00000000000..61817a18aed
--- /dev/null
+++ b/arch/mips/sgi-ip27/ip27-irq.c
@@ -0,0 +1,457 @@
+/*
+ * ip27-irq.c: Highlevel interrupt handling for IP27 architecture.
+ *
+ * Copyright (C) 1999, 2000 Ralf Baechle (ralf@gnu.org)
+ * Copyright (C) 1999, 2000 Silicon Graphics, Inc.
+ * Copyright (C) 1999 - 2001 Kanoj Sarcar
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/smp_lock.h>
+#include <linux/kernel_stat.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+#include <asm/pci/bridge.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/agent.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/hub.h>
+#include <asm/sn/intr.h>
+
+#undef DEBUG_IRQ
+#ifdef DEBUG_IRQ
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+/*
+ * Linux has a controller-independent x86 interrupt architecture.
+ * every controller has a 'controller-template', that is used
+ * by the main code to do the right thing. Each driver-visible
+ * interrupt source is transparently wired to the apropriate
+ * controller. Thus drivers need not be aware of the
+ * interrupt-controller.
+ *
+ * Various interrupt controllers we handle: 8259 PIC, SMP IO-APIC,
+ * PIIX4's internal 8259 PIC and SGI's Visual Workstation Cobalt (IO-)APIC.
+ * (IO-APICs assumed to be messaging to Pentium local-APICs)
+ *
+ * the code is designed to be easily extended with new/different
+ * interrupt controllers, without having to do assembly magic.
+ */
+
+extern asmlinkage void ip27_irq(void);
+
+extern struct bridge_controller *irq_to_bridge[];
+extern int irq_to_slot[];
+
+/*
+ * use these macros to get the encoded nasid and widget id
+ * from the irq value
+ */
+#define IRQ_TO_BRIDGE(i) irq_to_bridge[(i)]
+#define SLOT_FROM_PCI_IRQ(i) irq_to_slot[i]
+
+static inline int alloc_level(int cpu, int irq)
+{
+ struct slice_data *si = cpu_data[cpu].data;
+ int level; /* pre-allocated entries */
+
+ level = find_first_zero_bit(si->irq_alloc_mask, LEVELS_PER_SLICE);
+ if (level >= LEVELS_PER_SLICE)
+ panic("Cpu %d flooded with devices\n", cpu);
+
+ __set_bit(level, si->irq_alloc_mask);
+ si->level_to_irq[level] = irq;
+
+ return level;
+}
+
+static inline int find_level(cpuid_t *cpunum, int irq)
+{
+ int cpu, i;
+
+ for (cpu = 0; cpu <= NR_CPUS; cpu++) {
+ struct slice_data *si = cpu_data[cpu].data;
+
+ if (!cpu_online(cpu))
+ continue;
+
+ for (i = BASE_PCI_IRQ; i < LEVELS_PER_SLICE; i++)
+ if (si->level_to_irq[i] == irq) {
+ *cpunum = cpu;
+
+ return i;
+ }
+ }
+
+ panic("Could not identify cpu/level for irq %d\n", irq);
+}
+
+/*
+ * Find first bit set
+ */
+static int ms1bit(unsigned long x)
+{
+ int b = 0, s;
+
+ s = 16; if (x >> 16 == 0) s = 0; b += s; x >>= s;
+ s = 8; if (x >> 8 == 0) s = 0; b += s; x >>= s;
+ s = 4; if (x >> 4 == 0) s = 0; b += s; x >>= s;
+ s = 2; if (x >> 2 == 0) s = 0; b += s; x >>= s;
+ s = 1; if (x >> 1 == 0) s = 0; b += s;
+
+ return b;
+}
+
+/*
+ * This code is unnecessarily complex, because we do SA_INTERRUPT
+ * intr enabling. Basically, once we grab the set of intrs we need
+ * to service, we must mask _all_ these interrupts; firstly, to make
+ * sure the same intr does not intr again, causing recursion that
+ * can lead to stack overflow. Secondly, we can not just mask the
+ * one intr we are do_IRQing, because the non-masked intrs in the
+ * first set might intr again, causing multiple servicings of the
+ * same intr. This effect is mostly seen for intercpu intrs.
+ * Kanoj 05.13.00
+ */
+
+void ip27_do_irq_mask0(struct pt_regs *regs)
+{
+ int irq, swlevel;
+ hubreg_t pend0, mask0;
+ cpuid_t cpu = smp_processor_id();
+ int pi_int_mask0 =
+ (cputoslice(cpu) == 0) ? PI_INT_MASK0_A : PI_INT_MASK0_B;
+
+ /* copied from Irix intpend0() */
+ pend0 = LOCAL_HUB_L(PI_INT_PEND0);
+ mask0 = LOCAL_HUB_L(pi_int_mask0);
+
+ pend0 &= mask0; /* Pick intrs we should look at */
+ if (!pend0)
+ return;
+
+ swlevel = ms1bit(pend0);
+#ifdef CONFIG_SMP
+ if (pend0 & (1UL << CPU_RESCHED_A_IRQ)) {
+ LOCAL_HUB_CLR_INTR(CPU_RESCHED_A_IRQ);
+ } else if (pend0 & (1UL << CPU_RESCHED_B_IRQ)) {
+ LOCAL_HUB_CLR_INTR(CPU_RESCHED_B_IRQ);
+ } else if (pend0 & (1UL << CPU_CALL_A_IRQ)) {
+ LOCAL_HUB_CLR_INTR(CPU_CALL_A_IRQ);
+ smp_call_function_interrupt();
+ } else if (pend0 & (1UL << CPU_CALL_B_IRQ)) {
+ LOCAL_HUB_CLR_INTR(CPU_CALL_B_IRQ);
+ smp_call_function_interrupt();
+ } else
+#endif
+ {
+ /* "map" swlevel to irq */
+ struct slice_data *si = cpu_data[cpu].data;
+
+ irq = si->level_to_irq[swlevel];
+ do_IRQ(irq, regs);
+ }
+
+ LOCAL_HUB_L(PI_INT_PEND0);
+}
+
+void ip27_do_irq_mask1(struct pt_regs *regs)
+{
+ int irq, swlevel;
+ hubreg_t pend1, mask1;
+ cpuid_t cpu = smp_processor_id();
+ int pi_int_mask1 = (cputoslice(cpu) == 0) ? PI_INT_MASK1_A : PI_INT_MASK1_B;
+ struct slice_data *si = cpu_data[cpu].data;
+
+ /* copied from Irix intpend0() */
+ pend1 = LOCAL_HUB_L(PI_INT_PEND1);
+ mask1 = LOCAL_HUB_L(pi_int_mask1);
+
+ pend1 &= mask1; /* Pick intrs we should look at */
+ if (!pend1)
+ return;
+
+ swlevel = ms1bit(pend1);
+ /* "map" swlevel to irq */
+ irq = si->level_to_irq[swlevel];
+ LOCAL_HUB_CLR_INTR(swlevel);<