aboutsummaryrefslogtreecommitdiff
path: root/arch/mips/sgi-ip22
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-ip22
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-ip22')
-rw-r--r--arch/mips/sgi-ip22/Makefile11
-rw-r--r--arch/mips/sgi-ip22/ip22-berr.c114
-rw-r--r--arch/mips/sgi-ip22/ip22-eisa.c308
-rw-r--r--arch/mips/sgi-ip22/ip22-hpc.c63
-rw-r--r--arch/mips/sgi-ip22/ip22-int.c410
-rw-r--r--arch/mips/sgi-ip22/ip22-irq.S118
-rw-r--r--arch/mips/sgi-ip22/ip22-mc.c208
-rw-r--r--arch/mips/sgi-ip22/ip22-nvram.c119
-rw-r--r--arch/mips/sgi-ip22/ip22-reset.c247
-rw-r--r--arch/mips/sgi-ip22/ip22-setup.c144
-rw-r--r--arch/mips/sgi-ip22/ip22-time.c214
11 files changed, 1956 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
new file mode 100644
index 00000000000..eb0820fe50b
--- /dev/null
+++ b/arch/mips/sgi-ip22/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for the SGI specific kernel interface routines
+# under Linux.
+#
+
+obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-irq.o ip22-berr.o \
+ ip22-time.o ip22-nvram.o ip22-reset.o ip22-setup.o
+
+obj-$(CONFIG_EISA) += ip22-eisa.o
+
+EXTRA_AFLAGS := $(CFLAGS)
diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c
new file mode 100644
index 00000000000..a28dc780007
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-berr.c
@@ -0,0 +1,114 @@
+/*
+ * ip22-berr.c: Bus error handling.
+ *
+ * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/addrspace.h>
+#include <asm/system.h>
+#include <asm/traps.h>
+#include <asm/branch.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+
+static unsigned int cpu_err_stat; /* Status reg for CPU */
+static unsigned int gio_err_stat; /* Status reg for GIO */
+static unsigned int cpu_err_addr; /* Error address reg for CPU */
+static unsigned int gio_err_addr; /* Error address reg for GIO */
+static unsigned int extio_stat;
+static unsigned int hpc3_berr_stat; /* Bus error interrupt status */
+
+static void save_and_clear_buserr(void)
+{
+ /* save status registers */
+ cpu_err_addr = sgimc->cerr;
+ cpu_err_stat = sgimc->cstat;
+ gio_err_addr = sgimc->gerr;
+ gio_err_stat = sgimc->gstat;
+ extio_stat = ip22_is_fullhouse() ? sgioc->extio : (sgint->errstat << 4);
+ hpc3_berr_stat = hpc3c0->bestat;
+
+ sgimc->cstat = sgimc->gstat = 0;
+}
+
+#define GIO_ERRMASK 0xff00
+#define CPU_ERRMASK 0x3f00
+
+static void print_buserr(void)
+{
+ if (extio_stat & EXTIO_MC_BUSERR)
+ printk(KERN_ERR "MC Bus Error\n");
+ if (extio_stat & EXTIO_HPC3_BUSERR)
+ printk(KERN_ERR "HPC3 Bus Error 0x%x:<id=0x%x,%s,lane=0x%x>\n",
+ hpc3_berr_stat,
+ (hpc3_berr_stat & HPC3_BESTAT_PIDMASK) >>
+ HPC3_BESTAT_PIDSHIFT,
+ (hpc3_berr_stat & HPC3_BESTAT_CTYPE) ? "PIO" : "DMA",
+ hpc3_berr_stat & HPC3_BESTAT_BLMASK);
+ if (extio_stat & EXTIO_EISA_BUSERR)
+ printk(KERN_ERR "EISA Bus Error\n");
+ if (cpu_err_stat & CPU_ERRMASK)
+ printk(KERN_ERR "CPU error 0x%x<%s%s%s%s%s%s> @ 0x%08x\n",
+ cpu_err_stat,
+ cpu_err_stat & SGIMC_CSTAT_RD ? "RD " : "",
+ cpu_err_stat & SGIMC_CSTAT_PAR ? "PAR " : "",
+ cpu_err_stat & SGIMC_CSTAT_ADDR ? "ADDR " : "",
+ cpu_err_stat & SGIMC_CSTAT_SYSAD_PAR ? "SYSAD " : "",
+ cpu_err_stat & SGIMC_CSTAT_SYSCMD_PAR ? "SYSCMD " : "",
+ cpu_err_stat & SGIMC_CSTAT_BAD_DATA ? "BAD_DATA " : "",
+ cpu_err_addr);
+ if (gio_err_stat & GIO_ERRMASK)
+ printk(KERN_ERR "GIO error 0x%x:<%s%s%s%s%s%s%s%s> @ 0x%08x\n",
+ gio_err_stat,
+ gio_err_stat & SGIMC_GSTAT_RD ? "RD " : "",
+ gio_err_stat & SGIMC_GSTAT_WR ? "WR " : "",
+ gio_err_stat & SGIMC_GSTAT_TIME ? "TIME " : "",
+ gio_err_stat & SGIMC_GSTAT_PROM ? "PROM " : "",
+ gio_err_stat & SGIMC_GSTAT_ADDR ? "ADDR " : "",
+ gio_err_stat & SGIMC_GSTAT_BC ? "BC " : "",
+ gio_err_stat & SGIMC_GSTAT_PIO_RD ? "PIO_RD " : "",
+ gio_err_stat & SGIMC_GSTAT_PIO_WR ? "PIO_WR " : "",
+ gio_err_addr);
+}
+
+/*
+ * MC sends an interrupt whenever bus or parity errors occur. In addition,
+ * if the error happened during a CPU read, it also asserts the bus error
+ * pin on the R4K. Code in bus error handler save the MC bus error registers
+ * and then clear the interrupt when this happens.
+ */
+
+void ip22_be_interrupt(int irq, struct pt_regs *regs)
+{
+ const int field = 2 * sizeof(unsigned long);
+
+ save_and_clear_buserr();
+ print_buserr();
+ printk(KERN_ALERT "%s bus error, epc == %0*lx, ra == %0*lx\n",
+ (regs->cp0_cause & 4) ? "Data" : "Instruction",
+ field, regs->cp0_epc, field, regs->regs[31]);
+ /* Assume it would be too dangerous to continue ... */
+ die_if_kernel("Oops", regs);
+ force_sig(SIGBUS, current);
+}
+
+static int ip22_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ save_and_clear_buserr();
+ if (is_fixup)
+ return MIPS_BE_FIXUP;
+ print_buserr();
+ return MIPS_BE_FATAL;
+}
+
+void __init ip22_be_init(void)
+{
+ board_be_handler = ip22_be_handler;
+}
diff --git a/arch/mips/sgi-ip22/ip22-eisa.c b/arch/mips/sgi-ip22/ip22-eisa.c
new file mode 100644
index 00000000000..0ab4abf65d5
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-eisa.c
@@ -0,0 +1,308 @@
+/*
+ * Basic EISA bus support for the SGI Indigo-2.
+ *
+ * (C) 2002 Pascal Dameme <netinet@freesurf.fr>
+ * and Marc Zyngier <mzyngier@freesurf.fr>
+ *
+ * This code is released under both the GPL version 2 and BSD
+ * licenses. Either license may be used.
+ *
+ * This code offers a very basic support for this EISA bus present in
+ * the SGI Indigo-2. It currently only supports PIO (forget about DMA
+ * for the time being). This is enough for a low-end ethernet card,
+ * but forget about your favorite SCSI card...
+ *
+ * TODO :
+ * - Fix bugs...
+ * - Add ISA support
+ * - Add DMA (yeah, right...).
+ * - Fix more bugs.
+ */
+
+#include <linux/config.h>
+#include <linux/eisa.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/irq.h>
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+#include <asm/processor.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+#define EISA_MAX_SLOTS 4
+#define EISA_MAX_IRQ 16
+
+#define EISA_TO_PHYS(x) (0x00080000 | (x))
+#define EISA_TO_KSEG1(x) ((void *) KSEG1ADDR(EISA_TO_PHYS((x))))
+
+#define EIU_MODE_REG 0x0009ffc0
+#define EIU_STAT_REG 0x0009ffc4
+#define EIU_PREMPT_REG 0x0009ffc8
+#define EIU_QUIET_REG 0x0009ffcc
+#define EIU_INTRPT_ACK 0x00090004
+
+#define EISA_DMA1_STATUS 8
+#define EISA_INT1_CTRL 0x20
+#define EISA_INT1_MASK 0x21
+#define EISA_INT2_CTRL 0xA0
+#define EISA_INT2_MASK 0xA1
+#define EISA_DMA2_STATUS 0xD0
+#define EISA_DMA2_WRITE_SINGLE 0xD4
+#define EISA_EXT_NMI_RESET_CTRL 0x461
+#define EISA_INT1_EDGE_LEVEL 0x4D0
+#define EISA_INT2_EDGE_LEVEL 0x4D1
+#define EISA_VENDOR_ID_OFFSET 0xC80
+
+#define EIU_WRITE_32(x,y) { *((u32 *) KSEG1ADDR(x)) = (u32) (y); mb(); }
+#define EIU_READ_8(x) *((u8 *) KSEG1ADDR(x))
+#define EISA_WRITE_8(x,y) { *((u8 *) EISA_TO_KSEG1(x)) = (u8) (y); mb(); }
+#define EISA_READ_8(x) *((u8 *) EISA_TO_KSEG1(x))
+
+static char *decode_eisa_sig(u8 * sig)
+{
+ static char sig_str[8];
+ u16 rev;
+
+ if (sig[0] & 0x80)
+ return NULL;
+
+ sig_str[0] = ((sig[0] >> 2) & 0x1f) + ('A' - 1);
+ sig_str[1] = (((sig[0] & 3) << 3) | (sig[1] >> 5)) + ('A' - 1);
+ sig_str[2] = (sig[1] & 0x1f) + ('A' - 1);
+ rev = (sig[2] << 8) | sig[3];
+ sprintf(sig_str + 3, "%04X", rev);
+
+ return sig_str;
+}
+
+static void ip22_eisa_intr(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u8 eisa_irq;
+ u8 dma1, dma2;
+
+ eisa_irq = EIU_READ_8(EIU_INTRPT_ACK);
+ dma1 = EISA_READ_8(EISA_DMA1_STATUS);
+ dma2 = EISA_READ_8(EISA_DMA2_STATUS);
+
+ if (eisa_irq >= EISA_MAX_IRQ) {
+ /* Oops, Bad Stuff Happened... */
+ printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
+
+ EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
+ EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+ } else
+ do_IRQ(eisa_irq, regs);
+}
+
+static void enable_eisa1_irq(unsigned int irq)
+{
+ unsigned long flags;
+ u8 mask;
+
+ local_irq_save(flags);
+
+ mask = EISA_READ_8(EISA_INT1_MASK);
+ mask &= ~((u8) (1 << irq));
+ EISA_WRITE_8(EISA_INT1_MASK, mask);
+
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_eisa1_irq(unsigned int irq)
+{
+ u8 edge;
+
+ /* Only use edge interrupts for EISA */
+
+ edge = EISA_READ_8(EISA_INT1_EDGE_LEVEL);
+ edge &= ~((u8) (1 << irq));
+ EISA_WRITE_8(EISA_INT1_EDGE_LEVEL, edge);
+
+ enable_eisa1_irq(irq);
+ return 0;
+}
+
+static void disable_eisa1_irq(unsigned int irq)
+{
+ u8 mask;
+
+ mask = EISA_READ_8(EISA_INT1_MASK);
+ mask |= ((u8) (1 << irq));
+ EISA_WRITE_8(EISA_INT1_MASK, mask);
+}
+
+#define shutdown_eisa1_irq disable_eisa1_irq
+
+static void mask_and_ack_eisa1_irq(unsigned int irq)
+{
+ disable_eisa1_irq(irq);
+
+ EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+}
+
+static void end_eisa1_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_eisa1_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_eisa1_irq_type = {
+ .typename = "IP22 EISA",
+ .startup = startup_eisa1_irq,
+ .shutdown = shutdown_eisa1_irq,
+ .enable = enable_eisa1_irq,
+ .disable = disable_eisa1_irq,
+ .ack = mask_and_ack_eisa1_irq,
+ .end = end_eisa1_irq,
+};
+
+static void enable_eisa2_irq(unsigned int irq)
+{
+ unsigned long flags;
+ u8 mask;
+
+ local_irq_save(flags);
+
+ mask = EISA_READ_8(EISA_INT2_MASK);
+ mask &= ~((u8) (1 << (irq - 8)));
+ EISA_WRITE_8(EISA_INT2_MASK, mask);
+
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_eisa2_irq(unsigned int irq)
+{
+ u8 edge;
+
+ /* Only use edge interrupts for EISA */
+
+ edge = EISA_READ_8(EISA_INT2_EDGE_LEVEL);
+ edge &= ~((u8) (1 << (irq - 8)));
+ EISA_WRITE_8(EISA_INT2_EDGE_LEVEL, edge);
+
+ enable_eisa2_irq(irq);
+ return 0;
+}
+
+static void disable_eisa2_irq(unsigned int irq)
+{
+ u8 mask;
+
+ mask = EISA_READ_8(EISA_INT2_MASK);
+ mask |= ((u8) (1 << (irq - 8)));
+ EISA_WRITE_8(EISA_INT2_MASK, mask);
+}
+
+#define shutdown_eisa2_irq disable_eisa2_irq
+
+static void mask_and_ack_eisa2_irq(unsigned int irq)
+{
+ disable_eisa2_irq(irq);
+
+ EISA_WRITE_8(EISA_INT2_CTRL, 0x20);
+ EISA_WRITE_8(EISA_INT1_CTRL, 0x20);
+}
+
+static void end_eisa2_irq(unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED | IRQ_INPROGRESS)))
+ enable_eisa2_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_eisa2_irq_type = {
+ .typename = "IP22 EISA",
+ .startup = startup_eisa2_irq,
+ .shutdown = shutdown_eisa2_irq,
+ .enable = enable_eisa2_irq,
+ .disable = disable_eisa2_irq,
+ .ack = mask_and_ack_eisa2_irq,
+ .end = end_eisa2_irq,
+};
+
+static struct irqaction eisa_action = {
+ .handler = ip22_eisa_intr,
+ .name = "EISA",
+};
+
+static struct irqaction cascade_action = {
+ .handler = no_action,
+ .name = "EISA cascade",
+};
+
+int __init ip22_eisa_init(void)
+{
+ int i, c;
+ char *str;
+ u8 *slot_addr;
+
+ if (!(sgimc->systemid & SGIMC_SYSID_EPRESENT)) {
+ printk(KERN_INFO "EISA: bus not present.\n");
+ return 1;
+ }
+
+ printk(KERN_INFO "EISA: Probing bus...\n");
+ for (c = 0, i = 1; i <= EISA_MAX_SLOTS; i++) {
+ slot_addr =
+ (u8 *) EISA_TO_KSEG1((0x1000 * i) +
+ EISA_VENDOR_ID_OFFSET);
+ if ((str = decode_eisa_sig(slot_addr))) {
+ printk(KERN_INFO "EISA: slot %d : %s detected.\n",
+ i, str);
+ c++;
+ }
+ }
+ printk(KERN_INFO "EISA: Detected %d card%s.\n", c, c < 2 ? "" : "s");
+#ifdef CONFIG_ISA
+ printk(KERN_INFO "ISA support compiled in.\n");
+#endif
+
+ /* Warning : BlackMagicAhead(tm).
+ Please wave your favorite dead chicken over the busses */
+
+ /* First say hello to the EIU */
+ EIU_WRITE_32(EIU_PREMPT_REG, 0x0000FFFF);
+ EIU_WRITE_32(EIU_QUIET_REG, 1);
+ EIU_WRITE_32(EIU_MODE_REG, 0x40f3c07F);
+
+ /* Now be nice to the EISA chipset */
+ EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 1);
+ for (i = 0; i < 10000; i++); /* Wait long enough for the dust to settle */
+ EISA_WRITE_8(EISA_EXT_NMI_RESET_CTRL, 0);
+ EISA_WRITE_8(EISA_INT1_CTRL, 0x11);
+ EISA_WRITE_8(EISA_INT2_CTRL, 0x11);
+ EISA_WRITE_8(EISA_INT1_MASK, 0);
+ EISA_WRITE_8(EISA_INT2_MASK, 8);
+ EISA_WRITE_8(EISA_INT1_MASK, 4);
+ EISA_WRITE_8(EISA_INT2_MASK, 2);
+ EISA_WRITE_8(EISA_INT1_MASK, 1);
+ EISA_WRITE_8(EISA_INT2_MASK, 1);
+ EISA_WRITE_8(EISA_INT1_MASK, 0xfb);
+ EISA_WRITE_8(EISA_INT2_MASK, 0xff);
+ EISA_WRITE_8(EISA_DMA2_WRITE_SINGLE, 0);
+
+ for (i = SGINT_EISA; i < (SGINT_EISA + EISA_MAX_IRQ); i++) {
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = 0;
+ irq_desc[i].depth = 1;
+ if (i < (SGINT_EISA + 8))
+ irq_desc[i].handler = &ip22_eisa1_irq_type;
+ else
+ irq_desc[i].handler = &ip22_eisa2_irq_type;
+ }
+
+ /* Cannot use request_irq because of kmalloc not being ready at such
+ * an early stage. Yes, I've been bitten... */
+ setup_irq(SGI_EISA_IRQ, &eisa_action);
+ setup_irq(SGINT_EISA + 2, &cascade_action);
+
+ EISA_bus = 1;
+ return 0;
+}
diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c
new file mode 100644
index 00000000000..c0afeccb08c
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-hpc.c
@@ -0,0 +1,63 @@
+/*
+ * ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1998 Ralf Baechle
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+
+struct hpc3_regs *hpc3c0, *hpc3c1;
+
+EXPORT_SYMBOL(hpc3c0);
+EXPORT_SYMBOL(hpc3c1);
+
+struct sgioc_regs *sgioc;
+
+EXPORT_SYMBOL(sgioc);
+
+/* We need software copies of these because they are write only. */
+u8 sgi_ioc_reset, sgi_ioc_write;
+
+extern char *system_type;
+
+void __init sgihpc_init(void)
+{
+ /* ioremap can't fail */
+ hpc3c0 = (struct hpc3_regs *)
+ ioremap(HPC3_CHIP0_BASE, sizeof(struct hpc3_regs));
+ hpc3c1 = (struct hpc3_regs *)
+ ioremap(HPC3_CHIP1_BASE, sizeof(struct hpc3_regs));
+ /* IOC lives in PBUS PIO channel 6 */
+ sgioc = (struct sgioc_regs *)hpc3c0->pbus_extregs[6];
+
+ hpc3c0->pbus_piocfg[6][0] |= HPC3_PIOCFG_DS16;
+ if (ip22_is_fullhouse()) {
+ /* Full House comes with INT2 which lives in PBUS PIO
+ * channel 4 */
+ sgint = (struct sgint_regs *)hpc3c0->pbus_extregs[4];
+ system_type = "SGI Indigo2";
+ } else {
+ /* Guiness comes with INT3 which is part of IOC */
+ sgint = &sgioc->int3;
+ system_type = "SGI Indy";
+ }
+
+ sgi_ioc_reset = (SGIOC_RESET_PPORT | SGIOC_RESET_KBDMOUSE |
+ SGIOC_RESET_EISA | SGIOC_RESET_ISDN |
+ SGIOC_RESET_LC0OFF);
+
+ sgi_ioc_write = (SGIOC_WRITE_EASEL | SGIOC_WRITE_NTHRESH |
+ SGIOC_WRITE_TPSPEED | SGIOC_WRITE_EPSEL |
+ SGIOC_WRITE_U0AMODE | SGIOC_WRITE_U1AMODE);
+
+ sgioc->reset = sgi_ioc_reset;
+ sgioc->write = sgi_ioc_write;
+}
diff --git a/arch/mips/sgi-ip22/ip22-int.c b/arch/mips/sgi-ip22/ip22-int.c
new file mode 100644
index 00000000000..ea2844d29e6
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-int.c
@@ -0,0 +1,410 @@
+/*
+ * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
+ * found on INDY and Indigo2 workstations.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu)
+ * - Indigo2 changes
+ * - Interrupt handling fixes
+ * Copyright (C) 2001, 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+
+#include <asm/mipsregs.h>
+#include <asm/addrspace.h>
+
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/* #define DEBUG_SGINT */
+
+/* So far nothing hangs here */
+#undef USE_LIO3_IRQ
+
+struct sgint_regs *sgint;
+
+static char lc0msk_to_irqnr[256];
+static char lc1msk_to_irqnr[256];
+static char lc2msk_to_irqnr[256];
+static char lc3msk_to_irqnr[256];
+
+extern asmlinkage void indyIRQ(void);
+extern int ip22_eisa_init(void);
+
+static void enable_local0_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ /* don't allow mappable interrupt to be enabled from setup_irq,
+ * we have our own way to do so */
+ if (irq != SGI_MAP_0_IRQ)
+ sgint->imask0 |= (1 << (irq - SGINT_LOCAL0));
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_local0_irq(unsigned int irq)
+{
+ enable_local0_irq(irq);
+ return 0; /* Never anything pending */
+}
+
+static void disable_local0_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->imask0 &= ~(1 << (irq - SGINT_LOCAL0));
+ local_irq_restore(flags);
+}
+
+#define shutdown_local0_irq disable_local0_irq
+#define mask_and_ack_local0_irq disable_local0_irq
+
+static void end_local0_irq (unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_local0_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local0_irq_type = {
+ .typename = "IP22 local 0",
+ .startup = startup_local0_irq,
+ .shutdown = shutdown_local0_irq,
+ .enable = enable_local0_irq,
+ .disable = disable_local0_irq,
+ .ack = mask_and_ack_local0_irq,
+ .end = end_local0_irq,
+};
+
+static void enable_local1_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ /* don't allow mappable interrupt to be enabled from setup_irq,
+ * we have our own way to do so */
+ if (irq != SGI_MAP_1_IRQ)
+ sgint->imask1 |= (1 << (irq - SGINT_LOCAL1));
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_local1_irq(unsigned int irq)
+{
+ enable_local1_irq(irq);
+ return 0; /* Never anything pending */
+}
+
+void disable_local1_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->imask1 &= ~(1 << (irq - SGINT_LOCAL1));
+ local_irq_restore(flags);
+}
+
+#define shutdown_local1_irq disable_local1_irq
+#define mask_and_ack_local1_irq disable_local1_irq
+
+static void end_local1_irq (unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_local1_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local1_irq_type = {
+ .typename = "IP22 local 1",
+ .startup = startup_local1_irq,
+ .shutdown = shutdown_local1_irq,
+ .enable = enable_local1_irq,
+ .disable = disable_local1_irq,
+ .ack = mask_and_ack_local1_irq,
+ .end = end_local1_irq,
+};
+
+static void enable_local2_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+ sgint->cmeimask0 |= (1 << (irq - SGINT_LOCAL2));
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_local2_irq(unsigned int irq)
+{
+ enable_local2_irq(irq);
+ return 0; /* Never anything pending */
+}
+
+void disable_local2_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->cmeimask0 &= ~(1 << (irq - SGINT_LOCAL2));
+ if (!sgint->cmeimask0)
+ sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+ local_irq_restore(flags);
+}
+
+#define shutdown_local2_irq disable_local2_irq
+#define mask_and_ack_local2_irq disable_local2_irq
+
+static void end_local2_irq (unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_local2_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local2_irq_type = {
+ .typename = "IP22 local 2",
+ .startup = startup_local2_irq,
+ .shutdown = shutdown_local2_irq,
+ .enable = enable_local2_irq,
+ .disable = disable_local2_irq,
+ .ack = mask_and_ack_local2_irq,
+ .end = end_local2_irq,
+};
+
+static void enable_local3_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+ sgint->cmeimask1 |= (1 << (irq - SGINT_LOCAL3));
+ local_irq_restore(flags);
+}
+
+static unsigned int startup_local3_irq(unsigned int irq)
+{
+ enable_local3_irq(irq);
+ return 0; /* Never anything pending */
+}
+
+void disable_local3_irq(unsigned int irq)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+ sgint->cmeimask1 &= ~(1 << (irq - SGINT_LOCAL3));
+ if (!sgint->cmeimask1)
+ sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+ local_irq_restore(flags);
+}
+
+#define shutdown_local3_irq disable_local3_irq
+#define mask_and_ack_local3_irq disable_local3_irq
+
+static void end_local3_irq (unsigned int irq)
+{
+ if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)))
+ enable_local3_irq(irq);
+}
+
+static struct hw_interrupt_type ip22_local3_irq_type = {
+ .typename = "IP22 local 3",
+ .startup = startup_local3_irq,
+ .shutdown = shutdown_local3_irq,
+ .enable = enable_local3_irq,
+ .disable = disable_local3_irq,
+ .ack = mask_and_ack_local3_irq,
+ .end = end_local3_irq,
+};
+
+void indy_local0_irqdispatch(struct pt_regs *regs)
+{
+ u8 mask = sgint->istat0 & sgint->imask0;
+ u8 mask2;
+ int irq;
+
+ if (mask & SGINT_ISTAT0_LIO2) {
+ mask2 = sgint->vmeistat & sgint->cmeimask0;
+ irq = lc2msk_to_irqnr[mask2];
+ } else
+ irq = lc0msk_to_irqnr[mask];
+
+ /* if irq == 0, then the interrupt has already been cleared */
+ if (irq)
+ do_IRQ(irq, regs);
+ return;
+}
+
+void indy_local1_irqdispatch(struct pt_regs *regs)
+{
+ u8 mask = sgint->istat1 & sgint->imask1;
+ u8 mask2;
+ int irq;
+
+ if (mask & SGINT_ISTAT1_LIO3) {
+ mask2 = sgint->vmeistat & sgint->cmeimask1;
+ irq = lc3msk_to_irqnr[mask2];
+ } else
+ irq = lc1msk_to_irqnr[mask];
+
+ /* if irq == 0, then the interrupt has already been cleared */
+ if (irq)
+ do_IRQ(irq, regs);
+ return;
+}
+
+extern void ip22_be_interrupt(int irq, struct pt_regs *regs);
+
+void indy_buserror_irq(struct pt_regs *regs)
+{
+ int irq = SGI_BUSERR_IRQ;
+
+ irq_enter();
+ kstat_this_cpu.irqs[irq]++;
+ ip22_be_interrupt(irq, regs);
+ irq_exit();
+}
+
+static struct irqaction local0_cascade = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .name = "local0 cascade",
+};
+
+static struct irqaction local1_cascade = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .name = "local1 cascade",
+};
+
+static struct irqaction buserr = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .name = "Bus Error",
+};
+
+static struct irqaction map0_cascade = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .name = "mapable0 cascade",
+};
+
+#ifdef USE_LIO3_IRQ
+static struct irqaction map1_cascade = {
+ .handler = no_action,
+ .flags = SA_INTERRUPT,
+ .name = "mapable1 cascade",
+};
+#define SGI_INTERRUPTS SGINT_END
+#else
+#define SGI_INTERRUPTS SGINT_LOCAL3
+#endif
+
+extern void mips_cpu_irq_init(unsigned int irq_base);
+
+void __init arch_init_irq(void)
+{
+ int i;
+
+ /* Init local mask --> irq tables. */
+ for (i = 0; i < 256; i++) {
+ if (i & 0x80) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 7;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 7;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 7;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 7;
+ } else if (i & 0x40) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 6;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 6;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 6;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 6;
+ } else if (i & 0x20) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 5;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 5;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 5;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 5;
+ } else if (i & 0x10) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 4;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 4;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 4;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 4;
+ } else if (i & 0x08) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 3;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 3;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 3;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 3;
+ } else if (i & 0x04) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 2;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 2;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 2;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 2;
+ } else if (i & 0x02) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 1;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 1;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 1;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 1;
+ } else if (i & 0x01) {
+ lc0msk_to_irqnr[i] = SGINT_LOCAL0 + 0;
+ lc1msk_to_irqnr[i] = SGINT_LOCAL1 + 0;
+ lc2msk_to_irqnr[i] = SGINT_LOCAL2 + 0;
+ lc3msk_to_irqnr[i] = SGINT_LOCAL3 + 0;
+ } else {
+ lc0msk_to_irqnr[i] = 0;
+ lc1msk_to_irqnr[i] = 0;
+ lc2msk_to_irqnr[i] = 0;
+ lc3msk_to_irqnr[i] = 0;
+ }
+ }
+
+ /* Mask out all interrupts. */
+ sgint->imask0 = 0;
+ sgint->imask1 = 0;
+ sgint->cmeimask0 = 0;
+ sgint->cmeimask1 = 0;
+
+ set_except_vector(0, indyIRQ);
+
+ /* init CPU irqs */
+ mips_cpu_irq_init(SGINT_CPU);
+
+ for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
+ hw_irq_controller *handler;
+
+ if (i < SGINT_LOCAL1)
+ handler = &ip22_local0_irq_type;
+ else if (i < SGINT_LOCAL2)
+ handler = &ip22_local1_irq_type;
+ else if (i < SGINT_LOCAL3)
+ handler = &ip22_local2_irq_type;
+ else
+ handler = &ip22_local3_irq_type;
+
+ irq_desc[i].status = IRQ_DISABLED;
+ irq_desc[i].action = 0;
+ irq_desc[i].depth = 1;
+ irq_desc[i].handler = handler;
+ }
+
+ /* vector handler. this register the IRQ as non-sharable */
+ setup_irq(SGI_LOCAL_0_IRQ, &local0_cascade);
+ setup_irq(SGI_LOCAL_1_IRQ, &local1_cascade);
+ setup_irq(SGI_BUSERR_IRQ, &buserr);
+
+ /* cascade in cascade. i love Indy ;-) */
+ setup_irq(SGI_MAP_0_IRQ, &map0_cascade);
+#ifdef USE_LIO3_IRQ
+ setup_irq(SGI_MAP_1_IRQ, &map1_cascade);
+#endif
+
+#ifdef CONFIG_EISA
+ if (ip22_is_fullhouse()) /* Only Indigo-2 has EISA stuff */
+ ip22_eisa_init ();
+#endif
+}
diff --git a/arch/mips/sgi-ip22/ip22-irq.S b/arch/mips/sgi-ip22/ip22-irq.S
new file mode 100644
index 00000000000..6ccbd9e1d96
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-irq.S
@@ -0,0 +1,118 @@
+/*
+ * ip22-irq.S: Interrupt exception dispatch code for FullHouse and
+ * Guiness.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ */
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+/* A lot of complication here is taken away because:
+ *
+ * 1) We handle one interrupt and return, sitting in a loop and moving across
+ * all the pending IRQ bits in the cause register is _NOT_ the answer, the
+ * common case is one pending IRQ so optimize in that direction.
+ *
+ * 2) We need not check against bits in the status register IRQ mask, that
+ * would make this routine slow as hell.
+ *
+ * 3) Linux only thinks in terms of all IRQs on or all IRQs off, nothing in
+ * between like BSD spl() brain-damage.
+ *
+ * Furthermore, the IRQs on the INDY look basically (barring software IRQs
+ * which we don't use at all) like:
+ *
+ * MIPS IRQ Source
+ * -------- ------
+ * 0 Software (ignored)
+ * 1 Software (ignored)
+ * 2 Local IRQ level zero
+ * 3 Local IRQ level one
+ * 4 8254 Timer zero
+ * 5 8254 Timer one
+ * 6 Bus Error
+ * 7 R4k timer (what we use)
+ *
+ * We handle the IRQ according to _our_ priority which is:
+ *
+ * Highest ---- R4k Timer
+ * Local IRQ zero
+ * Local IRQ one
+ * Bus Error
+ * 8254 Timer zero
+ * Lowest ---- 8254 Timer one
+ *
+ * then we just return, if multiple IRQs are pending then we will just take
+ * another exception, big deal.
+ */
+
+ .text
+ .set noreorder
+ .set noat
+ .align 5
+ NESTED(indyIRQ, PT_SIZE, sp)
+ SAVE_ALL
+ CLI
+ .set at
+ mfc0 s0, CP0_CAUSE # get irq mask
+
+ /* First we check for r4k counter/timer IRQ. */
+ andi a0, s0, CAUSEF_IP7
+ beq a0, zero, 1f
+ andi a0, s0, CAUSEF_IP2 # delay slot, check local level zero
+
+ /* Wheee, a timer interrupt. */
+ jal indy_r4k_timer_interrupt
+ move a0, sp # delay slot
+ j ret_from_irq
+ nop # delay slot
+
+1:
+ beq a0, zero, 1f
+ andi a0, s0, CAUSEF_IP3 # delay slot, check local level one
+
+ /* Wheee, local level zero interrupt. */
+ jal indy_local0_irqdispatch
+ move a0, sp # delay slot
+
+ j ret_from_irq
+ nop # delay slot
+
+1:
+ beq a0, zero, 1f
+ andi a0, s0, CAUSEF_IP6 # delay slot, check bus error
+
+ /* Wheee, local level one interrupt. */
+ jal indy_local1_irqdispatch
+ move a0, sp # delay slot
+ j ret_from_irq
+ nop # delay slot
+
+1:
+ beq a0, zero, 1f
+ andi a0, s0, (CAUSEF_IP4 | CAUSEF_IP5) # delay slot
+
+ /* Wheee, an asynchronous bus error... */
+ jal indy_buserror_irq
+ move a0, sp # delay slot
+ j ret_from_irq
+ nop # delay slot
+
+1:
+ /* Here by mistake? It is possible, that by the time we take
+ * the exception the IRQ pin goes low, so just leave if this
+ * is the case.
+ */
+ beq a0, zero, 1f
+ nop # delay slot
+
+ /* Must be one of the 8254 timers... */
+ jal indy_8254timer_irq
+ move a0, sp # delay slot
+1:
+ j ret_from_irq
+ nop # delay slot
+ END(indyIRQ)
diff --git a/arch/mips/sgi-ip22/ip22-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
new file mode 100644
index 00000000000..b58bd522262
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -0,0 +1,208 @@
+/*
+ * ip22-mc.c: Routines for manipulating SGI Memory Controller.
+ *
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
+ * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+struct sgimc_regs *sgimc;