summaryrefslogtreecommitdiffstats
path: root/arch/mips/sgi-ip22
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 10:05:51 +0000
commit5d1646d90e1f2cceb9f0828f4b28318cd0ec7744 (patch)
treea94efe259b9009378be6d90eb30d2b019d95c194 /arch/mips/sgi-ip22
parentInitial commit. (diff)
downloadlinux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.tar.xz
linux-5d1646d90e1f2cceb9f0828f4b28318cd0ec7744.zip
Adding upstream version 5.10.209.upstream/5.10.209upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'arch/mips/sgi-ip22')
-rw-r--r--arch/mips/sgi-ip22/Makefile12
-rw-r--r--arch/mips/sgi-ip22/Platform32
-rw-r--r--arch/mips/sgi-ip22/ip22-berr.c116
-rw-r--r--arch/mips/sgi-ip22/ip22-eisa.c139
-rw-r--r--arch/mips/sgi-ip22/ip22-gio.c431
-rw-r--r--arch/mips/sgi-ip22/ip22-hpc.c64
-rw-r--r--arch/mips/sgi-ip22/ip22-int.c320
-rw-r--r--arch/mips/sgi-ip22/ip22-mc.c203
-rw-r--r--arch/mips/sgi-ip22/ip22-nvram.c122
-rw-r--r--arch/mips/sgi-ip22/ip22-platform.c223
-rw-r--r--arch/mips/sgi-ip22/ip22-reset.c203
-rw-r--r--arch/mips/sgi-ip22/ip22-setup.c78
-rw-r--r--arch/mips/sgi-ip22/ip22-time.c131
-rw-r--r--arch/mips/sgi-ip22/ip28-berr.c488
14 files changed, 2562 insertions, 0 deletions
diff --git a/arch/mips/sgi-ip22/Makefile b/arch/mips/sgi-ip22/Makefile
new file mode 100644
index 000000000..45f42fa08
--- /dev/null
+++ b/arch/mips/sgi-ip22/Makefile
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the SGI specific kernel interface routines
+# under Linux.
+#
+
+obj-y += ip22-mc.o ip22-hpc.o ip22-int.o ip22-time.o ip22-nvram.o \
+ ip22-platform.o ip22-reset.o ip22-setup.o ip22-gio.o
+
+obj-$(CONFIG_SGI_IP22) += ip22-berr.o
+obj-$(CONFIG_SGI_IP28) += ip28-berr.o
+obj-$(CONFIG_EISA) += ip22-eisa.o
diff --git a/arch/mips/sgi-ip22/Platform b/arch/mips/sgi-ip22/Platform
new file mode 100644
index 000000000..62fa30bb9
--- /dev/null
+++ b/arch/mips/sgi-ip22/Platform
@@ -0,0 +1,32 @@
+#
+# SGI IP22 (Indy/Indigo2)
+#
+# Set the load address to >= 0xffffffff88069000 if you want to leave space for
+# symmon, 0xffffffff80002000 for production kernels. Note that the value must
+# be aligned to a multiple of the kernel stack size or the handling of the
+# current variable will break so for 64-bit kernels we have to raise the start
+# address by 8kb.
+#
+cflags-$(CONFIG_SGI_IP22) += -I$(srctree)/arch/mips/include/asm/mach-ip22
+ifdef CONFIG_32BIT
+load-$(CONFIG_SGI_IP22) += 0xffffffff88002000
+endif
+ifdef CONFIG_64BIT
+load-$(CONFIG_SGI_IP22) += 0xffffffff88004000
+endif
+
+#
+# SGI IP28 (Indigo2 R10k)
+#
+# Set the load address to >= 0xa800000020080000 if you want to leave space for
+# symmon, 0xa800000020004000 for production kernels ? Note that the value must
+# be 16kb aligned or the handling of the current variable will break.
+# Simplified: what IP22 does at 128MB+ in ksegN, IP28 does at 512MB+ in xkphys
+#
+ifdef CONFIG_SGI_IP28
+ ifeq ($(call cc-option-yn,-march=r10000 -mr10k-cache-barrier=store), n)
+ $(error gcc doesn't support needed option -mr10k-cache-barrier=store)
+ endif
+endif
+cflags-$(CONFIG_SGI_IP28) += -mr10k-cache-barrier=store -I$(srctree)/arch/mips/include/asm/mach-ip28
+load-$(CONFIG_SGI_IP28) += 0xa800000020004000
diff --git a/arch/mips/sgi-ip22/ip22-berr.c b/arch/mips/sgi-ip22/ip22-berr.c
new file mode 100644
index 000000000..dc0110a60
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-berr.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * 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/signal.h>
+
+#include <asm/addrspace.h>
+#include <asm/traps.h>
+#include <asm/branch.h>
+#include <asm/irq_regs.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)
+{
+ const int field = 2 * sizeof(unsigned long);
+ struct pt_regs *regs = get_irq_regs();
+
+ 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);
+}
+
+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 000000000..f3b0e90e0
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-eisa.c
@@ -0,0 +1,139 @@
+/*
+ * 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/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/io.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>
+#include <asm/i8259.h>
+
+/* I2 has four EISA slots. */
+#define IP22_EISA_MAX_SLOTS 4
+#define EISA_MAX_IRQ 16
+
+#define EIU_MODE_REG 0x0001ffc0
+#define EIU_STAT_REG 0x0001ffc4
+#define EIU_PREMPT_REG 0x0001ffc8
+#define EIU_QUIET_REG 0x0001ffcc
+#define EIU_INTRPT_ACK 0x00010004
+
+static char __init *decode_eisa_sig(unsigned long addr)
+{
+ static char sig_str[EISA_SIG_LEN] __initdata;
+ u8 sig[4];
+ u16 rev;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ sig[i] = inb(addr + i);
+
+ if (!i && (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 irqreturn_t ip22_eisa_intr(int irq, void *dev_id)
+{
+ u8 eisa_irq = inb(EIU_INTRPT_ACK);
+
+ inb(EISA_DMA1_STATUS);
+ inb(EISA_DMA2_STATUS);
+
+ if (eisa_irq < EISA_MAX_IRQ) {
+ do_IRQ(eisa_irq);
+ return IRQ_HANDLED;
+ }
+
+ /* Oops, Bad Stuff Happened... */
+ printk(KERN_ERR "eisa_irq %d out of bound\n", eisa_irq);
+
+ outb(0x20, EISA_INT2_CTRL);
+ outb(0x20, EISA_INT1_CTRL);
+
+ return IRQ_NONE;
+}
+
+int __init ip22_eisa_init(void)
+{
+ int i, c;
+ char *str;
+
+ 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 <= IP22_EISA_MAX_SLOTS; i++) {
+ if ((str = decode_eisa_sig(0x1000 * i + EISA_VENDOR_ID_OFFSET))) {
+ 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 */
+ outl(0x0000FFFF, EIU_PREMPT_REG);
+ outl(1, EIU_QUIET_REG);
+ outl(0x40f3c07F, EIU_MODE_REG);
+
+ /* Now be nice to the EISA chipset */
+ outb(1, EISA_EXT_NMI_RESET_CTRL);
+ udelay(50); /* Wait long enough for the dust to settle */
+ outb(0, EISA_EXT_NMI_RESET_CTRL);
+ outb(0, EISA_DMA2_WRITE_SINGLE);
+
+ init_i8259_irqs();
+
+ if (request_irq(SGI_EISA_IRQ, ip22_eisa_intr, 0, "EISA", NULL))
+ pr_err("Failed to request irq %d (EISA)\n", SGI_EISA_IRQ);
+
+ EISA_bus = 1;
+ return 0;
+}
diff --git a/arch/mips/sgi-ip22/ip22-gio.c b/arch/mips/sgi-ip22/ip22-gio.c
new file mode 100644
index 000000000..de0768a49
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-gio.c
@@ -0,0 +1,431 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+
+#include <asm/addrspace.h>
+#include <asm/paccess.h>
+#include <asm/gio_device.h>
+#include <asm/sgi/gio.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+static struct bus_type gio_bus_type;
+
+static struct {
+ const char *name;
+ __u8 id;
+} gio_name_table[] = {
+ { .name = "SGI Impact", .id = 0x10 },
+ { .name = "Phobos G160", .id = 0x35 },
+ { .name = "Phobos G130", .id = 0x36 },
+ { .name = "Phobos G100", .id = 0x37 },
+ { .name = "Set Engineering GFE", .id = 0x38 },
+ /* fake IDs */
+ { .name = "SGI Newport", .id = 0x7e },
+ { .name = "SGI GR2/GR3", .id = 0x7f },
+};
+
+static void gio_bus_release(struct device *dev)
+{
+ kfree(dev);
+}
+
+static struct device gio_bus = {
+ .init_name = "gio",
+ .release = &gio_bus_release,
+};
+
+/**
+ * gio_match_device - Tell if an of_device structure has a matching
+ * gio_match structure
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an of_device present in the
+ * system is in its list of supported devices.
+ */
+static const struct gio_device_id *
+gio_match_device(const struct gio_device_id *match,
+ const struct gio_device *dev)
+{
+ const struct gio_device_id *ids;
+
+ for (ids = match; ids->id != 0xff; ids++)
+ if (ids->id == dev->id.id)
+ return ids;
+
+ return NULL;
+}
+
+struct gio_device *gio_dev_get(struct gio_device *dev)
+{
+ struct device *tmp;
+
+ if (!dev)
+ return NULL;
+ tmp = get_device(&dev->dev);
+ if (tmp)
+ return to_gio_device(tmp);
+ else
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(gio_dev_get);
+
+void gio_dev_put(struct gio_device *dev)
+{
+ if (dev)
+ put_device(&dev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_dev_put);
+
+/**
+ * gio_release_dev - free an gio device structure when all users of it are finished.
+ * @dev: device that's been disconnected
+ *
+ * Will be called only by the device core when all users of this gio device are
+ * done.
+ */
+void gio_release_dev(struct device *dev)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ kfree(giodev);
+}
+EXPORT_SYMBOL_GPL(gio_release_dev);
+
+int gio_device_register(struct gio_device *giodev)
+{
+ giodev->dev.bus = &gio_bus_type;
+ giodev->dev.parent = &gio_bus;
+ return device_register(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_register);
+
+void gio_device_unregister(struct gio_device *giodev)
+{
+ device_unregister(&giodev->dev);
+}
+EXPORT_SYMBOL_GPL(gio_device_unregister);
+
+static int gio_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *gio_drv = to_gio_driver(drv);
+
+ return gio_match_device(gio_drv->id_table, gio_dev) != NULL;
+}
+
+static int gio_device_probe(struct device *dev)
+{
+ int error = -ENODEV;
+ struct gio_driver *drv;
+ struct gio_device *gio_dev;
+ const struct gio_device_id *match;
+
+ drv = to_gio_driver(dev->driver);
+ gio_dev = to_gio_device(dev);
+
+ if (!drv->probe)
+ return error;
+
+ gio_dev_get(gio_dev);
+
+ match = gio_match_device(drv->id_table, gio_dev);
+ if (match)
+ error = drv->probe(gio_dev, match);
+ if (error)
+ gio_dev_put(gio_dev);
+
+ return error;
+}
+
+static int gio_device_remove(struct device *dev)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+
+ if (dev->driver && drv->remove)
+ drv->remove(gio_dev);
+ return 0;
+}
+
+static void gio_device_shutdown(struct device *dev)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ struct gio_driver *drv = to_gio_driver(dev->driver);
+
+ if (dev->driver && drv->shutdown)
+ drv->shutdown(gio_dev);
+}
+
+static ssize_t modalias_show(struct device *dev, struct device_attribute *a,
+ char *buf)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+ int len = snprintf(buf, PAGE_SIZE, "gio:%x\n", gio_dev->id.id);
+
+ return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;
+}
+static DEVICE_ATTR_RO(modalias);
+
+static ssize_t name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ return sprintf(buf, "%s", giodev->name);
+}
+static DEVICE_ATTR_RO(name);
+
+static ssize_t id_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct gio_device *giodev;
+
+ giodev = to_gio_device(dev);
+ return sprintf(buf, "%x", giodev->id.id);
+}
+static DEVICE_ATTR_RO(id);
+
+static struct attribute *gio_dev_attrs[] = {
+ &dev_attr_modalias.attr,
+ &dev_attr_name.attr,
+ &dev_attr_id.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(gio_dev);
+
+static int gio_device_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct gio_device *gio_dev = to_gio_device(dev);
+
+ add_uevent_var(env, "MODALIAS=gio:%x", gio_dev->id.id);
+ return 0;
+}
+
+int gio_register_driver(struct gio_driver *drv)
+{
+ /* initialize common driver fields */
+ if (!drv->driver.name)
+ drv->driver.name = drv->name;
+ if (!drv->driver.owner)
+ drv->driver.owner = drv->owner;
+ drv->driver.bus = &gio_bus_type;
+
+ /* register with core */
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_register_driver);
+
+void gio_unregister_driver(struct gio_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(gio_unregister_driver);
+
+void gio_set_master(struct gio_device *dev)
+{
+ u32 tmp = sgimc->giopar;
+
+ switch (dev->slotno) {
+ case 0:
+ tmp |= SGIMC_GIOPAR_MASTERGFX;
+ break;
+ case 1:
+ tmp |= SGIMC_GIOPAR_MASTEREXP0;
+ break;
+ case 2:
+ tmp |= SGIMC_GIOPAR_MASTEREXP1;
+ break;
+ }
+ sgimc->giopar = tmp;
+}
+EXPORT_SYMBOL_GPL(gio_set_master);
+
+void ip22_gio_set_64bit(int slotno)
+{
+ u32 tmp = sgimc->giopar;
+
+ switch (slotno) {
+ case 0:
+ tmp |= SGIMC_GIOPAR_GFX64;
+ break;
+ case 1:
+ tmp |= SGIMC_GIOPAR_EXP064;
+ break;
+ case 2:
+ tmp |= SGIMC_GIOPAR_EXP164;
+ break;
+ }
+ sgimc->giopar = tmp;
+}
+
+static int ip22_gio_id(unsigned long addr, u32 *res)
+{
+ u8 tmp8;
+ u8 tmp16;
+ u32 tmp32;
+ u8 *ptr8;
+ u16 *ptr16;
+ u32 *ptr32;
+
+ ptr32 = (void *)CKSEG1ADDR(addr);
+ if (!get_dbe(tmp32, ptr32)) {
+ /*
+ * We got no DBE, but this doesn't mean anything.
+ * If GIO is pipelined (which can't be disabled
+ * for GFX slot) we don't get a DBE, but we see
+ * the transfer size as data. So we do an 8bit
+ * and a 16bit access and check whether the common
+ * data matches
+ */
+ ptr8 = (void *)CKSEG1ADDR(addr + 3);
+ if (get_dbe(tmp8, ptr8)) {
+ /*
+ * 32bit access worked, but 8bit doesn't
+ * so we don't see phantom reads on
+ * a pipelined bus, but a real card which
+ * doesn't support 8 bit reads
+ */
+ *res = tmp32;
+ return 1;
+ }
+ ptr16 = (void *)CKSEG1ADDR(addr + 2);
+ get_dbe(tmp16, ptr16);
+ if (tmp8 == (tmp16 & 0xff) &&
+ tmp8 == (tmp32 & 0xff) &&
+ tmp16 == (tmp32 & 0xffff)) {
+ *res = tmp32;
+ return 1;
+ }
+ }
+ return 0; /* nothing here */
+}
+
+#define HQ2_MYSTERY_OFFS 0x6A07C
+#define NEWPORT_USTATUS_OFFS 0xF133C
+
+static int ip22_is_gr2(unsigned long addr)
+{
+ u32 tmp;
+ u32 *ptr;
+
+ /* HQ2 only allows 32bit accesses */
+ ptr = (void *)CKSEG1ADDR(addr + HQ2_MYSTERY_OFFS);
+ if (!get_dbe(tmp, ptr)) {
+ if (tmp == 0xdeadbeef)
+ return 1;
+ }
+ return 0;
+}
+
+
+static void ip22_check_gio(int slotno, unsigned long addr, int irq)
+{
+ const char *name = "Unknown";
+ struct gio_device *gio_dev;
+ u32 tmp;
+ __u8 id;
+ int i;
+
+ /* first look for GR2/GR3 by checking mystery register */
+ if (ip22_is_gr2(addr))
+ tmp = 0x7f;
+ else {
+ if (!ip22_gio_id(addr, &tmp)) {
+ /*
+ * no GIO signature at start address of slot
+ * since Newport doesn't have one, we check if
+ * user status register is readable
+ */
+ if (ip22_gio_id(addr + NEWPORT_USTATUS_OFFS, &tmp))
+ tmp = 0x7e;
+ else
+ tmp = 0;
+ }
+ }
+ if (tmp) {
+ id = GIO_ID(tmp);
+ if (tmp & GIO_32BIT_ID) {
+ if (tmp & GIO_64BIT_IFACE)
+ ip22_gio_set_64bit(slotno);
+ }
+ for (i = 0; i < ARRAY_SIZE(gio_name_table); i++) {
+ if (id == gio_name_table[i].id) {
+ name = gio_name_table[i].name;
+ break;
+ }
+ }
+ printk(KERN_INFO "GIO: slot %d : %s (id %x)\n",
+ slotno, name, id);
+ gio_dev = kzalloc(sizeof *gio_dev, GFP_KERNEL);
+ gio_dev->name = name;
+ gio_dev->slotno = slotno;
+ gio_dev->id.id = id;
+ gio_dev->resource.start = addr;
+ gio_dev->resource.end = addr + 0x3fffff;
+ gio_dev->resource.flags = IORESOURCE_MEM;
+ gio_dev->irq = irq;
+ dev_set_name(&gio_dev->dev, "%d", slotno);
+ gio_device_register(gio_dev);
+ } else
+ printk(KERN_INFO "GIO: slot %d : Empty\n", slotno);
+}
+
+static struct bus_type gio_bus_type = {
+ .name = "gio",
+ .dev_groups = gio_dev_groups,
+ .match = gio_bus_match,
+ .probe = gio_device_probe,
+ .remove = gio_device_remove,
+ .shutdown = gio_device_shutdown,
+ .uevent = gio_device_uevent,
+};
+
+static struct resource gio_bus_resource = {
+ .start = GIO_SLOT_GFX_BASE,
+ .end = GIO_SLOT_GFX_BASE + 0x9fffff,
+ .name = "GIO Bus",
+ .flags = IORESOURCE_MEM,
+};
+
+int __init ip22_gio_init(void)
+{
+ unsigned int pbdma __maybe_unused;
+ int ret;
+
+ ret = device_register(&gio_bus);
+ if (ret) {
+ put_device(&gio_bus);
+ return ret;
+ }
+
+ ret = bus_register(&gio_bus_type);
+ if (!ret) {
+ request_resource(&iomem_resource, &gio_bus_resource);
+ printk(KERN_INFO "GIO: Probing bus...\n");
+
+ if (ip22_is_fullhouse()) {
+ /* Indigo2 */
+ ip22_check_gio(0, GIO_SLOT_GFX_BASE, SGI_GIO_1_IRQ);
+ ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIO_1_IRQ);
+ } else {
+ /* Indy/Challenge S */
+ if (get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1]))
+ ip22_check_gio(0, GIO_SLOT_GFX_BASE,
+ SGI_GIO_0_IRQ);
+ ip22_check_gio(1, GIO_SLOT_EXP0_BASE, SGI_GIOEXP0_IRQ);
+ ip22_check_gio(2, GIO_SLOT_EXP1_BASE, SGI_GIOEXP1_IRQ);
+ }
+ } else
+ device_unregister(&gio_bus);
+
+ return ret;
+}
+
+subsys_initcall(ip22_gio_init);
diff --git a/arch/mips/sgi-ip22/ip22-hpc.c b/arch/mips/sgi-ip22/ip22-hpc.c
new file mode 100644
index 000000000..49922e86c
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-hpc.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip22-hpc.c: Routines for generic manipulation of the HPC controllers.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1998 Ralf Baechle
+ */
+
+#include <linux/export.h>
+#include <linux/init.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 000000000..96798a4ab
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-int.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip22-int.c: Routines for generic manipulation of the INT[23] ASIC
+ * found on INDY and Indigo2 workstations.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
+ * 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/types.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/interrupt.h>
+#include <linux/ftrace.h>
+
+#include <asm/irq_cpu.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/* 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 int ip22_eisa_init(void);
+
+static void enable_local0_irq(struct irq_data *d)
+{
+ /* don't allow mappable interrupt to be enabled from setup_irq,
+ * we have our own way to do so */
+ if (d->irq != SGI_MAP_0_IRQ)
+ sgint->imask0 |= (1 << (d->irq - SGINT_LOCAL0));
+}
+
+static void disable_local0_irq(struct irq_data *d)
+{
+ sgint->imask0 &= ~(1 << (d->irq - SGINT_LOCAL0));
+}
+
+static struct irq_chip ip22_local0_irq_type = {
+ .name = "IP22 local 0",
+ .irq_mask = disable_local0_irq,
+ .irq_unmask = enable_local0_irq,
+};
+
+static void enable_local1_irq(struct irq_data *d)
+{
+ /* don't allow mappable interrupt to be enabled from setup_irq,
+ * we have our own way to do so */
+ if (d->irq != SGI_MAP_1_IRQ)
+ sgint->imask1 |= (1 << (d->irq - SGINT_LOCAL1));
+}
+
+static void disable_local1_irq(struct irq_data *d)
+{
+ sgint->imask1 &= ~(1 << (d->irq - SGINT_LOCAL1));
+}
+
+static struct irq_chip ip22_local1_irq_type = {
+ .name = "IP22 local 1",
+ .irq_mask = disable_local1_irq,
+ .irq_unmask = enable_local1_irq,
+};
+
+static void enable_local2_irq(struct irq_data *d)
+{
+ sgint->imask0 |= (1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+ sgint->cmeimask0 |= (1 << (d->irq - SGINT_LOCAL2));
+}
+
+static void disable_local2_irq(struct irq_data *d)
+{
+ sgint->cmeimask0 &= ~(1 << (d->irq - SGINT_LOCAL2));
+ if (!sgint->cmeimask0)
+ sgint->imask0 &= ~(1 << (SGI_MAP_0_IRQ - SGINT_LOCAL0));
+}
+
+static struct irq_chip ip22_local2_irq_type = {
+ .name = "IP22 local 2",
+ .irq_mask = disable_local2_irq,
+ .irq_unmask = enable_local2_irq,
+};
+
+static void enable_local3_irq(struct irq_data *d)
+{
+ sgint->imask1 |= (1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+ sgint->cmeimask1 |= (1 << (d->irq - SGINT_LOCAL3));
+}
+
+static void disable_local3_irq(struct irq_data *d)
+{
+ sgint->cmeimask1 &= ~(1 << (d->irq - SGINT_LOCAL3));
+ if (!sgint->cmeimask1)
+ sgint->imask1 &= ~(1 << (SGI_MAP_1_IRQ - SGINT_LOCAL1));
+}
+
+static struct irq_chip ip22_local3_irq_type = {
+ .name = "IP22 local 3",
+ .irq_mask = disable_local3_irq,
+ .irq_unmask = enable_local3_irq,
+};
+
+static void indy_local0_irqdispatch(void)
+{
+ 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];
+
+ /*
+ * workaround for INT2 bug; if irq == 0, INT2 has seen a fifo full
+ * irq, but failed to latch it into status register
+ */
+ if (irq)
+ do_IRQ(irq);
+ else
+ do_IRQ(SGINT_LOCAL0 + 0);
+}
+
+static void indy_local1_irqdispatch(void)
+{
+ 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);
+}
+
+extern void ip22_be_interrupt(int irq);
+
+static void __irq_entry indy_buserror_irq(void)
+{
+ int irq = SGI_BUSERR_IRQ;
+
+ irq_enter();
+ kstat_incr_irq_this_cpu(irq);
+ ip22_be_interrupt(irq);
+ irq_exit();
+}
+
+#ifdef USE_LIO3_IRQ
+#define SGI_INTERRUPTS SGINT_END
+#else
+#define SGI_INTERRUPTS SGINT_LOCAL3
+#endif
+
+extern void indy_8254timer_irq(void);
+
+/*
+ * 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.
+ */
+
+asmlinkage void plat_irq_dispatch(void)
+{
+ unsigned int pending = read_c0_status() & read_c0_cause();
+
+ /*
+ * First we check for r4k counter/timer IRQ.
+ */
+ if (pending & CAUSEF_IP7)
+ do_IRQ(SGI_TIMER_IRQ);
+ else if (pending & CAUSEF_IP2)
+ indy_local0_irqdispatch();
+ else if (pending & CAUSEF_IP3)
+ indy_local1_irqdispatch();
+ else if (pending & CAUSEF_IP6)
+ indy_buserror_irq();
+ else if (pending & (CAUSEF_IP4 | CAUSEF_IP5))
+ indy_8254timer_irq();
+}
+
+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;
+
+ /* init CPU irqs */
+ mips_cpu_irq_init();
+
+ for (i = SGINT_LOCAL0; i < SGI_INTERRUPTS; i++) {
+ struct irq_chip *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_set_chip_and_handler(i, handler, handle_level_irq);
+ }
+
+ /* vector handler. this register the IRQ as non-sharable */
+ if (request_irq(SGI_LOCAL_0_IRQ, no_action, IRQF_NO_THREAD,
+ "local0 cascade", NULL))
+ pr_err("Failed to register local0 cascade interrupt\n");
+ if (request_irq(SGI_LOCAL_1_IRQ, no_action, IRQF_NO_THREAD,
+ "local1 cascade", NULL))
+ pr_err("Failed to register local1 cascade interrupt\n");
+ if (request_irq(SGI_BUSERR_IRQ, no_action, IRQF_NO_THREAD,
+ "Bus Error", NULL))
+ pr_err("Failed to register Bus Error interrupt\n");
+
+ /* cascade in cascade. i love Indy ;-) */
+ if (request_irq(SGI_MAP_0_IRQ, no_action, IRQF_NO_THREAD,
+ "mapable0 cascade", NULL))
+ pr_err("Failed to register mapable0 cascade interrupt\n");
+#ifdef USE_LIO3_IRQ
+ if (request_irq(SGI_MAP_1_IRQ, no_action, IRQF_NO_THREAD,
+ "mapable1 cascade", NULL))
+ pr_err("Failed to register mapable1 cascade interrupt\n");
+#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-mc.c b/arch/mips/sgi-ip22/ip22-mc.c
new file mode 100644
index 000000000..74e5b9e27
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-mc.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip22-mc.c: Routines for manipulating SGI Memory Controller.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1999 Andrew R. Baker (andrewb@uab.edu) - Indigo2 changes
+ * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
+ * Copyright (C) 2004 Peter Fuerst (pf@net.alphadv.de) - IP28
+ */
+
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/kernel.h>
+#include <linux/memblock.h>
+#include <linux/spinlock.h>
+
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+struct sgimc_regs *sgimc;
+
+EXPORT_SYMBOL(sgimc);
+
+static inline unsigned long get_bank_addr(unsigned int memconfig)
+{
+ return (memconfig & SGIMC_MCONFIG_BASEADDR) << ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 24 : 22);
+}
+
+static inline unsigned long get_bank_size(unsigned int memconfig)
+{
+ return ((memconfig & SGIMC_MCONFIG_RMASK) + 0x0100) << ((sgimc->systemid & SGIMC_SYSID_MASKREV) >= 5 ? 16 : 14);
+}
+
+static inline unsigned int get_bank_config(int bank)
+{
+ unsigned int res = bank > 1 ? sgimc->mconfig1 : sgimc->mconfig0;
+ return bank % 2 ? res & 0xffff : res >> 16;
+}
+
+#if defined(CONFIG_SGI_IP28) || defined(CONFIG_32BIT)
+static void __init probe_memory(void)
+{
+ /* prom detects all usable memory */
+}
+#else
+/*
+ * Detect installed memory, which PROM misses
+ */
+static void __init probe_memory(void)
+{
+ unsigned long addr, size;
+ int i;
+
+ printk(KERN_INFO "MC: Probing memory configuration:\n");
+ for (i = 0; i < 4; i++) {
+ unsigned int tmp = get_bank_config(i);
+ if (!(tmp & SGIMC_MCONFIG_BVALID))
+ continue;
+
+ size = get_bank_size(tmp);
+ addr = get_bank_addr(tmp);
+ printk(KERN_INFO " bank%d: %3ldM @ %08lx\n",
+ i, size / 1024 / 1024, addr);
+
+ if (addr >= SGIMC_SEG1_BADDR)
+ memblock_add(addr, size);
+ }
+}
+#endif
+
+void __init sgimc_init(void)
+{
+ u32 tmp;
+
+ /* ioremap can't fail */
+ sgimc = (struct sgimc_regs *)
+ ioremap(SGIMC_BASE, sizeof(struct sgimc_regs));
+
+ printk(KERN_INFO "MC: SGI memory controller Revision %d\n",
+ (int) sgimc->systemid & SGIMC_SYSID_MASKREV);
+
+ /* Place the MC into a known state. This must be done before
+ * interrupts are first enabled etc.
+ */
+
+ /* Step 0: Make sure we turn off the watchdog in case it's
+ * still running (which might be the case after a
+ * soft reboot).
+ */
+ tmp = sgimc->cpuctrl0;
+ tmp &= ~SGIMC_CCTRL0_WDOG;
+ sgimc->cpuctrl0 = tmp;
+
+ /* Step 1: The CPU/GIO error status registers will not latch
+ * up a new error status until the register has been
+ * cleared by the cpu. These status registers are
+ * cleared by writing any value to them.
+ */
+ sgimc->cstat = sgimc->gstat = 0;
+
+ /* Step 2: Enable all parity checking in cpu control register
+ * zero.
+ */
+ /* don't touch parity settings for IP28 */
+ tmp = sgimc->cpuctrl0;
+#ifndef CONFIG_SGI_IP28
+ tmp |= SGIMC_CCTRL0_EPERRGIO | SGIMC_CCTRL0_EPERRMEM;
+#endif
+ tmp |= SGIMC_CCTRL0_R4KNOCHKPARR;
+ sgimc->cpuctrl0 = tmp;
+
+ /* Step 3: Setup the MC write buffer depth, this is controlled
+ * in cpu control register 1 in the lower 4 bits.
+ */
+ tmp = sgimc->cpuctrl1;
+ tmp &= ~0xf;
+ tmp |= 0xd;
+ sgimc->cpuctrl1 = tmp;
+
+ /* Step 4: Initialize the RPSS divider register to run as fast
+ * as it can correctly operate. The register is laid
+ * out as follows:
+ *
+ * ----------------------------------------
+ * | RESERVED | INCREMENT | DIVIDER |
+ * ----------------------------------------
+ * 31 16 15 8 7 0
+ *
+ * DIVIDER determines how often a 'tick' happens,
+ * INCREMENT determines by how the RPSS increment
+ * registers value increases at each 'tick'. Thus,
+ * for IP22 we get INCREMENT=1, DIVIDER=1 == 0x101
+ */
+ sgimc->divider = 0x101;
+
+ /* Step 5: Initialize GIO64 arbitrator configuration register.
+ *
+ * NOTE: HPC init code in sgihpc_init() must run before us because
+ * we need to know Guiness vs. FullHouse and the board
+ * revision on this machine. You have been warned.
+ */
+
+ /* First the basic invariants across all GIO64 implementations. */
+ tmp = sgimc->giopar & SGIMC_GIOPAR_GFX64; /* keep gfx 64bit settings */
+ tmp |= SGIMC_GIOPAR_HPC64; /* All 1st HPC's interface at 64bits */
+ tmp |= SGIMC_GIOPAR_ONEBUS; /* Only one physical GIO bus exists */
+
+ if (ip22_is_fullhouse()) {
+ /* Fullhouse specific settings. */
+ if (SGIOC_SYSID_BOARDREV(sgioc->sysid) < 2) {
+ tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC at 64bits */
+ tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp0 pipelines */
+ tmp |= SGIMC_GIOPAR_MASTEREXP1; /* exp1 masters */
+ tmp |= SGIMC_GIOPAR_RTIMEEXP0; /* exp0 is realtime */
+ } else {
+ tmp |= SGIMC_GIOPAR_HPC264; /* 2nd HPC 64bits */
+ tmp |= SGIMC_GIOPAR_PLINEEXP0; /* exp[01] pipelined */
+ tmp |= SGIMC_GIOPAR_PLINEEXP1;
+ tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA masters */
+ }
+ } else {
+ /* Guiness specific settings. */
+ tmp |= SGIMC_GIOPAR_EISA64; /* MC talks to EISA at 64bits */
+ tmp |= SGIMC_GIOPAR_MASTEREISA; /* EISA bus can act as master */
+ }
+ sgimc->giopar = tmp; /* poof */
+
+ probe_memory();
+}
+
+#ifdef CONFIG_SGI_IP28
+void __init prom_cleanup(void)
+{
+ u32 mconfig1;
+ unsigned long flags;
+ spinlock_t lock;
+
+ /*
+ * because ARCS accesses memory uncached we wait until ARCS
+ * isn't needed any longer, before we switch from slow to
+ * normal mode
+ */
+ spin_lock_irqsave(&lock, flags);
+ mconfig1 = sgimc->mconfig1;
+ /* map ECC register */
+ sgimc->mconfig1 = (mconfig1 & 0xffff0000) | 0x2060;
+ iob();
+ /* switch to normal mode */
+ *(unsigned long *)PHYS_TO_XKSEG_UNCACHED(0x60000000) = 0;
+ iob();
+ /* reduce WR_COL */
+ sgimc->cmacc = (sgimc->cmacc & ~0xf) | 4;
+ iob();
+ /* restore old config */
+ sgimc->mconfig1 = mconfig1;
+ iob();
+ spin_unlock_irqrestore(&lock, flags);
+}
+#endif
diff --git a/arch/mips/sgi-ip22/ip22-nvram.c b/arch/mips/sgi-ip22/ip22-nvram.c
new file mode 100644
index 000000000..e727ef519
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-nvram.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip22-nvram.c: NVRAM and serial EEPROM handling.
+ *
+ * Copyright (C) 2003 Ladislav Michl (ladis@linux-mips.org)
+ */
+#include <linux/export.h>
+
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+/* Control opcode for serial eeprom */
+#define EEPROM_READ 0xc000 /* serial memory read */
+#define EEPROM_WEN 0x9800 /* write enable before prog modes */
+#define EEPROM_WRITE 0xa000 /* serial memory write */
+#define EEPROM_WRALL 0x8800 /* write all registers */
+#define EEPROM_WDS 0x8000 /* disable all programming */
+#define EEPROM_PRREAD 0xc000 /* read protect register */
+#define EEPROM_PREN 0x9800 /* enable protect register mode */
+#define EEPROM_PRCLEAR 0xffff /* clear protect register */
+#define EEPROM_PRWRITE 0xa000 /* write protect register */
+#define EEPROM_PRDS 0x8000 /* disable protect register, forever */
+
+#define EEPROM_EPROT 0x01 /* Protect register enable */
+#define EEPROM_CSEL 0x02 /* Chip select */
+#define EEPROM_ECLK 0x04 /* EEPROM clock */
+#define EEPROM_DATO 0x08 /* Data out */
+#define EEPROM_DATI 0x10 /* Data in */
+
+/* We need to use these functions early... */
+#define delay() ({ \
+ int x; \
+ for (x=0; x<100000; x++) __asm__ __volatile__(""); })
+
+#define eeprom_cs_on(ptr) ({ \
+ __raw_writel(__raw_readl(ptr) & ~EEPROM_DATO, ptr); \
+ __raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \
+ __raw_writel(__raw_readl(ptr) & ~EEPROM_EPROT, ptr); \
+ delay(); \
+ __raw_writel(__raw_readl(ptr) | EEPROM_CSEL, ptr); \
+ __raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); })
+
+
+#define eeprom_cs_off(ptr) ({ \
+ __raw_writel(__raw_readl(ptr) & ~EEPROM_ECLK, ptr); \
+ __raw_writel(__raw_readl(ptr) & ~EEPROM_CSEL, ptr); \
+ __raw_writel(__raw_readl(ptr) | EEPROM_EPROT, ptr); \
+ __raw_writel(__raw_readl(ptr) | EEPROM_ECLK, ptr); })
+
+#define BITS_IN_COMMAND 11
+/*
+ * clock in the nvram command and the register number. For the
+ * national semiconductor nv ram chip the op code is 3 bits and
+ * the address is 6/8 bits.
+ */
+static inline void eeprom_cmd(unsigned int *ctrl, unsigned cmd, unsigned reg)
+{
+ unsigned short ser_cmd;
+ int i;
+
+ ser_cmd = cmd | (reg << (16 - BITS_IN_COMMAND));
+ for (i = 0; i < BITS_IN_COMMAND; i++) {
+ if (ser_cmd & (1<<15)) /* if high order bit set */
+ __raw_writel(__raw_readl(ctrl) | EEPROM_DATO, ctrl);
+ else
+ __raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl);
+ __raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl);
+ delay();
+ __raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl);
+ delay();
+ ser_cmd <<= 1;
+ }
+ /* see data sheet timing diagram */
+ __raw_writel(__raw_readl(ctrl) & ~EEPROM_DATO, ctrl);
+}
+
+unsigned short ip22_eeprom_read(unsigned int *ctrl, int reg)
+{
+ unsigned short res = 0;
+ int i;
+
+ __raw_writel(__raw_readl(ctrl) & ~EEPROM_EPROT, ctrl);
+ eeprom_cs_on(ctrl);
+ eeprom_cmd(ctrl, EEPROM_READ, reg);
+
+ /* clock the data ouf of serial mem */
+ for (i = 0; i < 16; i++) {
+ __raw_writel(__raw_readl(ctrl) & ~EEPROM_ECLK, ctrl);
+ delay();
+ __raw_writel(__raw_readl(ctrl) | EEPROM_ECLK, ctrl);
+ delay();
+ res <<= 1;
+ if (__raw_readl(ctrl) & EEPROM_DATI)
+ res |= 1;
+ }
+
+ eeprom_cs_off(ctrl);
+
+ return res;
+}
+
+EXPORT_SYMBOL(ip22_eeprom_read);
+
+/*
+ * Read specified register from main NVRAM
+ */
+unsigned short ip22_nvram_read(int reg)
+{
+ if (ip22_is_fullhouse())
+ /* IP22 (Indigo2 aka FullHouse) stores env variables into
+ * 93CS56 Microwire Bus EEPROM 2048 Bit (128x16) */
+ return ip22_eeprom_read(&hpc3c0->eeprom, reg);
+ else {
+ unsigned short tmp;
+ /* IP24 (Indy aka Guiness) uses DS1386 8K version */
+ reg <<= 1;
+ tmp = hpc3c0->bbram[reg++] & 0xff;
+ return (tmp << 8) | (hpc3c0->bbram[reg] & 0xff);
+ }
+}
+
+EXPORT_SYMBOL(ip22_nvram_read);
diff --git a/arch/mips/sgi-ip22/ip22-platform.c b/arch/mips/sgi-ip22/ip22-platform.c
new file mode 100644
index 000000000..0b2002e02
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-platform.c
@@ -0,0 +1,223 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/init.h>
+#include <linux/if_ether.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+
+#include <asm/paccess.h>
+#include <asm/sgi/ip22.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/seeq.h>
+#include <asm/sgi/wd.h>
+
+static struct resource sgiwd93_0_resources[] = {
+ {
+ .name = "eth0 irq",
+ .start = SGI_WD93_0_IRQ,
+ .end = SGI_WD93_0_IRQ,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct sgiwd93_platform_data sgiwd93_0_pd = {
+ .unit = 0,
+ .irq = SGI_WD93_0_IRQ,
+};
+
+static u64 sgiwd93_0_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device sgiwd93_0_device = {
+ .name = "sgiwd93",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(sgiwd93_0_resources),
+ .resource = sgiwd93_0_resources,
+ .dev = {
+ .platform_data = &sgiwd93_0_pd,
+ .dma_mask = &sgiwd93_0_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource sgiwd93_1_resources[] = {
+ {
+ .name = "eth0 irq",
+ .start = SGI_WD93_1_IRQ,
+ .end = SGI_WD93_1_IRQ,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct sgiwd93_platform_data sgiwd93_1_pd = {
+ .unit = 1,
+ .irq = SGI_WD93_1_IRQ,
+};
+
+static u64 sgiwd93_1_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device sgiwd93_1_device = {
+ .name = "sgiwd93",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(sgiwd93_1_resources),
+ .resource = sgiwd93_1_resources,
+ .dev = {
+ .platform_data = &sgiwd93_1_pd,
+ .dma_mask = &sgiwd93_1_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+/*
+ * Create a platform device for the GPI port that receives the
+ * image data from the embedded camera.
+ */
+static int __init sgiwd93_devinit(void)
+{
+ int res;
+
+ sgiwd93_0_pd.hregs = &hpc3c0->scsi_chan0;
+ sgiwd93_0_pd.wdregs = (unsigned char *) hpc3c0->scsi0_ext;
+
+ res = platform_device_register(&sgiwd93_0_device);
+ if (res)
+ return res;
+
+ if (!ip22_is_fullhouse())
+ return 0;
+
+ sgiwd93_1_pd.hregs = &hpc3c0->scsi_chan1;
+ sgiwd93_1_pd.wdregs = (unsigned char *) hpc3c0->scsi1_ext;
+
+ return platform_device_register(&sgiwd93_1_device);
+}
+
+device_initcall(sgiwd93_devinit);
+
+static struct resource sgiseeq_0_resources[] = {
+ {
+ .name = "eth0 irq",
+ .start = SGI_ENET_IRQ,
+ .end = SGI_ENET_IRQ,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct sgiseeq_platform_data eth0_pd;
+
+static u64 sgiseeq_dma_mask = DMA_BIT_MASK(32);
+
+static struct platform_device eth0_device = {
+ .name = "sgiseeq",
+ .id = 0,
+ .num_resources = ARRAY_SIZE(sgiseeq_0_resources),
+ .resource = sgiseeq_0_resources,
+ .dev = {
+ .platform_data = &eth0_pd,
+ .dma_mask = &sgiseeq_dma_mask,
+ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+};
+
+static struct resource sgiseeq_1_resources[] = {
+ {
+ .name = "eth1 irq",
+ .start = SGI_GIO_0_IRQ,
+ .end = SGI_GIO_0_IRQ,
+ .flags = IORESOURCE_IRQ
+ }
+};
+
+static struct sgiseeq_platform_data eth1_pd;
+
+static struct platform_device eth1_device = {
+ .name = "sgiseeq",
+ .id = 1,
+ .num_resources = ARRAY_SIZE(sgiseeq_1_resources),
+ .resource = sgiseeq_1_resources,
+ .dev = {
+ .platform_data = &eth1_pd,
+ },
+};
+
+/*
+ * Create a platform device for the GPI port that receives the
+ * image data from the embedded camera.
+ */
+static int __init sgiseeq_devinit(void)
+{
+ unsigned int pbdma __maybe_unused;
+ int res, i;
+
+ eth0_pd.hpc = hpc3c0;
+ eth0_pd.irq = SGI_ENET_IRQ;
+#define EADDR_NVOFS 250
+ for (i = 0; i < 3; i++) {
+ unsigned short tmp = ip22_nvram_read(EADDR_NVOFS / 2 + i);
+
+ eth0_pd.mac[2 * i] = tmp >> 8;
+ eth0_pd.mac[2 * i + 1] = tmp & 0xff;
+ }
+
+ res = platform_device_register(&eth0_device);
+ if (res)
+ return res;
+
+ /* Second HPC is missing? */
+ if (ip22_is_fullhouse() ||
+ get_dbe(pbdma, (unsigned int *)&hpc3c1->pbdma[1]))
+ return 0;
+
+ sgimc->giopar |= SGIMC_GIOPAR_MASTEREXP1 | SGIMC_GIOPAR_EXP164 |
+ SGIMC_GIOPAR_HPC264;
+ hpc3c1->pbus_piocfg[0][0] = 0x3ffff;
+ /* interrupt/config register on Challenge S Mezz board */
+ hpc3c1->pbus_extregs[0][0] = 0x30;
+
+ eth1_pd.hpc = hpc3c1;
+ eth1_pd.irq = SGI_GIO_0_IRQ;
+#define EADDR_NVOFS 250
+ for (i = 0; i < 3; i++) {
+ unsigned short tmp = ip22_eeprom_read(&hpc3c1->eeprom,
+ EADDR_NVOFS / 2 + i);
+
+ eth1_pd.mac[2 * i] = tmp >> 8;
+ eth1_pd.mac[2 * i + 1] = tmp & 0xff;
+ }
+
+ return platform_device_register(&eth1_device);
+}
+
+device_initcall(sgiseeq_devinit);
+
+static int __init sgi_hal2_devinit(void)
+{
+ return IS_ERR(platform_device_register_simple("sgihal2", 0, NULL, 0));
+}
+
+device_initcall(sgi_hal2_devinit);
+
+static int __init sgi_button_devinit(void)
+{
+ if (ip22_is_fullhouse())
+ return 0; /* full house has no volume buttons */
+
+ return IS_ERR(platform_device_register_simple("sgibtns", -1, NULL, 0));
+}
+
+device_initcall(sgi_button_devinit);
+
+static int __init sgi_ds1286_devinit(void)
+{
+ struct resource res;
+
+ memset(&res, 0, sizeof(res));
+ res.start = HPC3_CHIP0_BASE + offsetof(struct hpc3_regs, rtcregs);
+ res.end = res.start + sizeof(hpc3c0->rtcregs) - 1;
+ res.flags = IORESOURCE_MEM;
+
+ return IS_ERR(platform_device_register_simple("rtc-ds1286", -1,
+ &res, 1));
+}
+
+device_initcall(sgi_ds1286_devinit);
diff --git a/arch/mips/sgi-ip22/ip22-reset.c b/arch/mips/sgi-ip22/ip22-reset.c
new file mode 100644
index 000000000..c374f3cee
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-reset.c
@@ -0,0 +1,203 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1997, 1998, 2001, 03, 05, 06 by Ralf Baechle
+ */
+#include <linux/linkage.h>
+#include <linux/init.h>
+#include <linux/rtc/ds1286.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/sched/signal.h>
+#include <linux/notifier.h>
+#include <linux/pm.h>
+#include <linux/timer.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/reboot.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/ip22.h>
+
+/*
+ * Just powerdown if init hasn't done after POWERDOWN_TIMEOUT seconds.
+ * I'm not sure if this feature is a good idea, for now it's here just to
+ * make the power button make behave just like under IRIX.
+ */
+#define POWERDOWN_TIMEOUT 120
+
+/*
+ * Blink frequency during reboot grace period and when panicked.
+ */
+#define POWERDOWN_FREQ (HZ / 4)
+#define PANIC_FREQ (HZ / 8)
+
+static struct timer_list power_timer, blink_timer, debounce_timer;
+static unsigned long blink_timer_timeout;
+
+#define MACHINE_PANICED 1
+#define MACHINE_SHUTTING_DOWN 2
+
+static int machine_state;
+
+static void __noreturn sgi_machine_power_off(void)
+{
+ unsigned int tmp;
+
+ local_irq_disable();
+
+ /* Disable watchdog */
+ tmp = hpc3c0->rtcregs[RTC_CMD] & 0xff;
+ hpc3c0->rtcregs[RTC_CMD] = tmp | RTC_WAM;
+ hpc3c0->rtcregs[RTC_WSEC] = 0;
+ hpc3c0->rtcregs[RTC_WHSEC] = 0;
+
+ while (1) {
+ sgioc->panel = ~SGIOC_PANEL_POWERON;
+ /* Good bye cruel world ... */
+
+ /* If we're still running, we probably got sent an alarm
+ interrupt. Read the flag to clear it. */
+ tmp = hpc3c0->rtcregs[RTC_HOURS_ALARM];
+ }
+}
+
+static void __noreturn sgi_machine_restart(char *command)
+{
+ if (machine_state & MACHINE_SHUTTING_DOWN)
+ sgi_machine_power_off();
+ sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
+ while (1);
+}
+
+static void __noreturn sgi_machine_halt(void)
+{
+ if (machine_state & MACHINE_SHUTTING_DOWN)
+ sgi_machine_power_off();
+ ArcEnterInteractiveMode();
+}
+
+static void power_timeout(struct timer_list *unused)
+{
+ sgi_machine_power_off();
+}
+
+static void blink_timeout(struct timer_list *unused)
+{
+ /* XXX fix this for fullhouse */
+ sgi_ioc_reset ^= (SGIOC_RESET_LC0OFF|SGIOC_RESET_LC1OFF);
+ sgioc->reset = sgi_ioc_reset;
+
+ mod_timer(&blink_timer, jiffies + blink_timer_timeout);
+}
+
+static void debounce(struct timer_list *unused)
+{
+ del_timer(&debounce_timer);
+ if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+ /* Interrupt still being sent. */
+ debounce_timer.expires = jiffies + (HZ / 20); /* 0.05s */
+ add_timer(&debounce_timer);
+
+ sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR |
+ SGIOC_PANEL_VOLDNINTR | SGIOC_PANEL_VOLDNHOLD |
+ SGIOC_PANEL_VOLUPINTR | SGIOC_PANEL_VOLUPHOLD;
+
+ return;
+ }
+
+ if (machine_state & MACHINE_PANICED)
+ sgimc->cpuctrl0 |= SGIMC_CCTRL0_SYSINIT;
+
+ enable_irq(SGI_PANEL_IRQ);
+}
+
+static inline void power_button(void)
+{
+ if (machine_state & MACHINE_PANICED)
+ return;
+
+ if ((machine_state & MACHINE_SHUTTING_DOWN) ||
+ kill_cad_pid(SIGINT, 1)) {
+ /* No init process or button pressed twice. */
+ sgi_machine_power_off();
+ }
+
+ machine_state |= MACHINE_SHUTTING_DOWN;
+ blink_timer_timeout = POWERDOWN_FREQ;
+ blink_timeout(&blink_timer);
+
+ timer_setup(&power_timer, power_timeout, 0);
+ power_timer.expires = jiffies + POWERDOWN_TIMEOUT * HZ;
+ add_timer(&power_timer);
+}
+
+static irqreturn_t panel_int(int irq, void *dev_id)
+{
+ unsigned int buttons;
+
+ buttons = sgioc->panel;
+ sgioc->panel = SGIOC_PANEL_POWERON | SGIOC_PANEL_POWERINTR;
+
+ if (sgint->istat1 & SGINT_ISTAT1_PWR) {
+ /* Wait until interrupt goes away */
+ disable_irq_nosync(SGI_PANEL_IRQ);
+ timer_setup(&debounce_timer, debounce, 0);
+ debounce_timer.expires = jiffies + 5;
+ add_timer(&debounce_timer);
+ }
+
+ /* Power button was pressed
+ * ioc.ps page 22: "The Panel Register is called Power Control by Full
+ * House. Only lowest 2 bits are used. Guiness uses upper four bits
+ * for volume control". This is not true, all bits are pulled high
+ * on fullhouse */
+ if (!(buttons & SGIOC_PANEL_POWERINTR))
+ power_button();
+
+ return IRQ_HANDLED;
+}
+
+static int panic_event(struct notifier_block *this, unsigned long event,
+ void *ptr)
+{
+ if (machine_state & MACHINE_PANICED)
+ return NOTIFY_DONE;
+ machine_state |= MACHINE_PANICED;
+
+ blink_timer_timeout = PANIC_FREQ;
+ blink_timeout(&blink_timer);
+
+ return NOTIFY_DONE;
+}
+
+static struct notifier_block panic_block = {
+ .notifier_call = panic_event,
+};
+
+static int __init reboot_setup(void)
+{
+ int res;
+
+ _machine_restart = sgi_machine_restart;
+ _machine_halt = sgi_machine_halt;
+ pm_power_off = sgi_machine_power_off;
+
+ res = request_irq(SGI_PANEL_IRQ, panel_int, 0, "Front Panel", NULL);
+ if (res) {
+ printk(KERN_ERR "Allocation of front panel IRQ failed\n");
+ return res;
+ }
+
+ timer_setup(&blink_timer, blink_timeout, 0);
+ atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
+
+ return 0;
+}
+
+subsys_initcall(reboot_setup);
diff --git a/arch/mips/sgi-ip22/ip22-setup.c b/arch/mips/sgi-ip22/ip22-setup.c
new file mode 100644
index 000000000..b69daa024
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-setup.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip22-setup.c: SGI specific setup, including init of the feature struct.
+ *
+ * Copyright (C) 1996 David S. Miller (davem@davemloft.net)
+ * Copyright (C) 1997, 1998 Ralf Baechle (ralf@gnu.org)
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kdev_t.h>
+#include <linux/types.h>
+#include <linux/console.h>
+#include <linux/sched.h>
+#include <linux/tty.h>
+
+#include <asm/addrspace.h>
+#include <asm/bcache.h>
+#include <asm/bootinfo.h>
+#include <asm/irq.h>
+#include <asm/reboot.h>
+#include <asm/time.h>
+#include <asm/io.h>
+#include <asm/traps.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+extern void ip22_be_init(void) __init;
+
+void __init plat_mem_setup(void)
+{
+ char *ctype;
+ char *cserial;
+
+ board_be_init = ip22_be_init;
+
+ /* Init the INDY HPC I/O controller. Need to call this before
+ * fucking with the memory controller because it needs to know the
+ * boardID and whether this is a Guiness or a FullHouse machine.
+ */
+ sgihpc_init();
+
+ /* Init INDY memory controller. */
+ sgimc_init();
+
+#ifdef CONFIG_BOARD_SCACHE
+ /* Now enable boardcaches, if any. */
+ indy_sc_init();
+#endif
+
+ /* Set EISA IO port base for Indigo2
+ * ioremap cannot fail */
+ set_io_port_base((unsigned long)ioremap(0x00080000,
+ 0x1fffffff - 0x00080000));
+ /* ARCS console environment variable is set to "g?" for
+ * graphics console, it is set to "d" for the first serial
+ * line and "d2" for the second serial line.
+ *
+ * Need to check if the case is 'g' but no keyboard:
+ * (ConsoleIn/Out = serial)
+ */
+ ctype = ArcGetEnvironmentVariable("console");
+ cserial = ArcGetEnvironmentVariable("ConsoleOut");
+
+ if ((ctype && *ctype == 'd') || (cserial && *cserial == 's')) {
+ static char options[8] __initdata;
+ char *baud = ArcGetEnvironmentVariable("dbaud");
+ if (baud)
+ strcpy(options, baud);
+ add_preferred_console("ttyS", *(ctype + 1) == '2' ? 1 : 0,
+ baud ? options : NULL);
+ } else if (!ctype || *ctype != 'g') {
+ /* Use ARC if we don't want serial ('d') or graphics ('g'). */
+ prom_flags |= PROM_FLAG_USE_AS_CONSOLE;
+ add_preferred_console("arc", 0, NULL);
+ }
+}
diff --git a/arch/mips/sgi-ip22/ip22-time.c b/arch/mips/sgi-ip22/ip22-time.c
new file mode 100644
index 000000000..045aa89f2
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip22-time.c
@@ -0,0 +1,131 @@
+/*
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Time operations for IP22 machines. Original code may come from
+ * Ralf Baechle or David S. Miller (sorry guys, i'm really not sure)
+ *
+ * Copyright (C) 2001 by Ladislav Michl
+ * Copyright (C) 2003, 06 Ralf Baechle (ralf@linux-mips.org)
+ */
+#include <linux/bcd.h>
+#include <linux/i8253.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/time.h>
+#include <linux/ftrace.h>
+
+#include <asm/cpu.h>
+#include <asm/mipsregs.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/time.h>
+#include <asm/sgialib.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ip22.h>
+
+static unsigned long dosample(void)
+{
+ u32 ct0, ct1;
+ u8 msb;
+
+ /* Start the counter. */
+ sgint->tcword = (SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL |
+ SGINT_TCWORD_MRGEN);
+ sgint->tcnt2 = SGINT_TCSAMP_COUNTER & 0xff;
+ sgint->tcnt2 = SGINT_TCSAMP_COUNTER >> 8;
+
+ /* Get initial counter invariant */
+ ct0 = read_c0_count();
+
+ /* Latch and spin until top byte of counter2 is zero */
+ do {
+ writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CLAT, &sgint->tcword);
+ (void) readb(&sgint->tcnt2);
+ msb = readb(&sgint->tcnt2);
+ ct1 = read_c0_count();
+ } while (msb);
+
+ /* Stop the counter. */
+ writeb(SGINT_TCWORD_CNT2 | SGINT_TCWORD_CALL | SGINT_TCWORD_MSWST,
+ &sgint->tcword);
+ /*
+ * Return the difference, this is how far the r4k counter increments
+ * for every 1/HZ seconds. We round off the nearest 1 MHz of master
+ * clock (= 1000000 / HZ / 2).
+ */
+
+ return (ct1 - ct0) / (500000/HZ) * (500000/HZ);
+}
+
+/*
+ * Here we need to calibrate the cycle counter to at least be close.
+ */
+__init void plat_time_init(void)
+{
+ unsigned long r4k_ticks[3];
+ unsigned long r4k_tick;
+
+ /*
+ * Figure out the r4k offset, the algorithm is very simple and works in
+ * _all_ cases as long as the 8254 counter register itself works ok (as
+ * an interrupt driving timer it does not because of bug, this is why
+ * we are using the onchip r4k counter/compare register to serve this
+ * purpose, but for r4k_offset calculation it will work ok for us).
+ * There are other very complicated ways of performing this calculation
+ * but this one works just fine so I am not going to futz around. ;-)
+ */
+ printk(KERN_INFO "Calibrating system timer... ");
+ dosample(); /* Prime cache. */
+ dosample(); /* Prime cache. */
+ /* Zero is NOT an option. */
+ do {
+ r4k_ticks[0] = dosample();
+ } while (!r4k_ticks[0]);
+ do {
+ r4k_ticks[1] = dosample();
+ } while (!r4k_ticks[1]);
+
+ if (r4k_ticks[0] != r4k_ticks[1]) {
+ printk("warning: timer counts differ, retrying... ");
+ r4k_ticks[2] = dosample();
+ if (r4k_ticks[2] == r4k_ticks[0]
+ || r4k_ticks[2] == r4k_ticks[1])
+ r4k_tick = r4k_ticks[2];
+ else {
+ printk("disagreement, using average... ");
+ r4k_tick = (r4k_ticks[0] + r4k_ticks[1]
+ + r4k_ticks[2]) / 3;
+ }
+ } else
+ r4k_tick = r4k_ticks[0];
+
+ printk("%d [%d.%04d MHz CPU]\n", (int) r4k_tick,
+ (int) (r4k_tick / (500000 / HZ)),
+ (int) (r4k_tick % (500000 / HZ)));
+
+ mips_hpt_frequency = r4k_tick * HZ;
+
+ if (ip22_is_fullhouse())
+ setup_pit_timer();
+}
+
+/* Generic SGI handler for (spurious) 8254 interrupts */
+void __irq_entry indy_8254timer_irq(void)
+{
+ int irq = SGI_8254_0_IRQ;
+ ULONG cnt;
+ char c;
+
+ irq_enter();
+ kstat_incr_irq_this_cpu(irq);
+ printk(KERN_ALERT "Oops, got 8254 interrupt.\n");
+ ArcRead(0, &c, 1, &cnt);
+ ArcEnterInteractiveMode();
+ irq_exit();
+}
diff --git a/arch/mips/sgi-ip22/ip28-berr.c b/arch/mips/sgi-ip22/ip28-berr.c
new file mode 100644
index 000000000..c61362d9e
--- /dev/null
+++ b/arch/mips/sgi-ip22/ip28-berr.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ip28-berr.c: Bus error handling.
+ *
+ * Copyright (C) 2002, 2003 Ladislav Michl (ladis@linux-mips.org)
+ * Copyright (C) 2005 Peter Fuerst (pf@net.alphadv.de) - IP28
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/sched/debug.h>
+#include <linux/sched/signal.h>
+#include <linux/seq_file.h>
+
+#include <asm/addrspace.h>
+#include <asm/traps.h>
+#include <asm/branch.h>
+#include <asm/irq_regs.h>
+#include <asm/sgi/mc.h>
+#include <asm/sgi/hpc3.h>
+#include <asm/sgi/ioc.h>
+#include <asm/sgi/ip22.h>
+#include <asm/r4kcache.h>
+#include <linux/uaccess.h>
+#include <asm/bootinfo.h>
+
+static unsigned int count_be_is_fixup;
+static unsigned int count_be_handler;
+static unsigned int count_be_interrupt;
+static int debug_be_interrupt;
+
+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 */
+
+struct hpc3_stat {
+ unsigned long addr;
+ unsigned int ctrl;
+ unsigned int cbp;
+ unsigned int ndptr;
+};
+
+static struct {
+ struct hpc3_stat pbdma[8];
+ struct hpc3_stat scsi[2];
+ struct hpc3_stat ethrx, ethtx;
+} hpc3;
+
+static struct {
+ unsigned long err_addr;
+ struct {
+ u32 lo;
+ u32 hi;
+ } tags[1][2], tagd[4][2], tagi[4][2]; /* Way 0/1 */
+} cache_tags;
+
+static inline void save_cache_tags(unsigned busaddr)
+{
+ unsigned long addr = CAC_BASE | busaddr;
+ int i;
+ cache_tags.err_addr = addr;
+
+ /*
+ * Starting with a bus-address, save secondary cache (indexed by
+ * PA[23..18:7..6]) tags first.
+ */
+ addr &= ~1L;
+#define tag cache_tags.tags[0]
+ cache_op(Index_Load_Tag_S, addr);
+ tag[0].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */
+ tag[0].hi = read_c0_taghi(); /* PA[39:36] */
+ cache_op(Index_Load_Tag_S, addr | 1L);
+ tag[1].lo = read_c0_taglo(); /* PA[35:18], VA[13:12] */
+ tag[1].hi = read_c0_taghi(); /* PA[39:36] */
+#undef tag
+
+ /*
+ * Save all primary data cache (indexed by VA[13:5]) tags which
+ * might fit to this bus-address, knowing that VA[11:0] == PA[11:0].
+ * Saving all tags and evaluating them later is easier and safer
+ * than relying on VA[13:12] from the secondary cache tags to pick
+ * matching primary tags here already.
+ */
+ addr &= (0xffL << 56) | ((1 << 12) - 1);
+#define tag cache_tags.tagd[i]
+ for (i = 0; i < 4; ++i, addr += (1 << 12)) {
+ cache_op(Index_Load_Tag_D, addr);
+ tag[0].lo = read_c0_taglo(); /* PA[35:12] */
+ tag[0].hi = read_c0_taghi(); /* PA[39:36] */
+ cache_op(Index_Load_Tag_D, addr | 1L);
+ tag[1].lo = read_c0_taglo(); /* PA[35:12] */
+ tag[1].hi = read_c0_taghi(); /* PA[39:36] */
+ }
+#undef tag
+
+ /*
+ * Save primary instruction cache (indexed by VA[13:6]) tags
+ * the same way.
+ */
+ addr &= (0xffL << 56) | ((1 << 12) - 1);
+#define tag cache_tags.tagi[i]
+ for (i = 0; i < 4; ++i, addr += (1 << 12)) {
+ cache_op(Index_Load_Tag_I, addr);
+ tag[0].lo = read_c0_taglo(); /* PA[35:12] */
+ tag[0].hi = read_c0_taghi(); /* PA[39:36] */
+ cache_op(Index_Load_Tag_I, addr | 1L);
+ tag[1].lo = read_c0_taglo(); /* PA[35:12] */
+ tag[1].hi = read_c0_taghi(); /* PA[39:36] */
+ }
+#undef tag
+}
+
+#define GIO_ERRMASK 0xff00
+#define CPU_ERRMASK 0x3f00
+
+static void save_and_clear_buserr(void)
+{
+ int i;
+
+ /* 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 = sgioc->extio;
+ hpc3_berr_stat = hpc3c0->bestat;
+
+ hpc3.scsi[0].addr = (unsigned long)&hpc3c0->scsi_chan0;
+ hpc3.scsi[0].ctrl = hpc3c0->scsi_chan0.ctrl; /* HPC3_SCTRL_ACTIVE ? */
+ hpc3.scsi[0].cbp = hpc3c0->scsi_chan0.cbptr;
+ hpc3.scsi[0].ndptr = hpc3c0->scsi_chan0.ndptr;
+
+ hpc3.scsi[1].addr = (unsigned long)&hpc3c0->scsi_chan1;
+ hpc3.scsi[1].ctrl = hpc3c0->scsi_chan1.ctrl; /* HPC3_SCTRL_ACTIVE ? */
+ hpc3.scsi[1].cbp = hpc3c0->scsi_chan1.cbptr;
+ hpc3.scsi[1].ndptr = hpc3c0->scsi_chan1.ndptr;
+
+ hpc3.ethrx.addr = (unsigned long)&hpc3c0->ethregs.rx_cbptr;
+ hpc3.ethrx.ctrl = hpc3c0->ethregs.rx_ctrl; /* HPC3_ERXCTRL_ACTIVE ? */
+ hpc3.ethrx.cbp = hpc3c0->ethregs.rx_cbptr;
+ hpc3.ethrx.ndptr = hpc3c0->ethregs.rx_ndptr;
+
+ hpc3.ethtx.addr = (unsigned long)&hpc3c0->ethregs.tx_cbptr;
+ hpc3.ethtx.ctrl = hpc3c0->ethregs.tx_ctrl; /* HPC3_ETXCTRL_ACTIVE ? */
+ hpc3.ethtx.cbp = hpc3c0->ethregs.tx_cbptr;
+ hpc3.ethtx.ndptr = hpc3c0->ethregs.tx_ndptr;
+
+ for (i = 0; i < 8; ++i) {
+ /* HPC3_PDMACTRL_ISACT ? */
+ hpc3.pbdma[i].addr = (unsigned long)&hpc3c0->pbdma[i];
+ hpc3.pbdma[i].ctrl = hpc3c0->pbdma[i].pbdma_ctrl;
+ hpc3.pbdma[i].cbp = hpc3c0->pbdma[i].pbdma_bptr;
+ hpc3.pbdma[i].ndptr = hpc3c0->pbdma[i].pbdma_dptr;
+ }
+ i = 0;
+ if (gio_err_stat & CPU_ERRMASK)
+ i = gio_err_addr;
+ if (cpu_err_stat & CPU_ERRMASK)
+ i = cpu_err_addr;
+ save_cache_tags(i);
+
+ sgimc->cstat = sgimc->gstat = 0;
+}
+
+static void print_cache_tags(void)
+{
+ u32 scb, scw;
+ int i;
+
+ printk(KERN_ERR "Cache tags @ %08x:\n", (unsigned)cache_tags.err_addr);
+
+ /* PA[31:12] shifted to PTag0 (PA[35:12]) format */
+ scw = (cache_tags.err_addr >> 4) & 0x0fffff00;
+
+ scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 5) - 1);
+ for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */
+ if ((cache_tags.tagd[i][0].lo & 0x0fffff00) != scw &&
+ (cache_tags.tagd[i][1].lo & 0x0fffff00) != scw)
+ continue;
+ printk(KERN_ERR
+ "D: 0: %08x %08x, 1: %08x %08x (VA[13:5] %04x)\n",
+ cache_tags.tagd[i][0].hi, cache_tags.tagd[i][0].lo,
+ cache_tags.tagd[i][1].hi, cache_tags.tagd[i][1].lo,
+ scb | (1 << 12)*i);
+ }
+ scb = cache_tags.err_addr & ((1 << 12) - 1) & ~((1 << 6) - 1);
+ for (i = 0; i < 4; ++i) { /* for each possible VA[13:12] value */
+ if ((cache_tags.tagi[i][0].lo & 0x0fffff00) != scw &&
+ (cache_tags.tagi[i][1].lo & 0x0fffff00) != scw)
+ continue;
+ printk(KERN_ERR
+ "I: 0: %08x %08x, 1: %08x %08x (VA[13:6] %04x)\n",
+ cache_tags.tagi[i][0].hi, cache_tags.tagi[i][0].lo,
+ cache_tags.tagi[i][1].hi, cache_tags.tagi[i][1].lo,
+ scb | (1 << 12)*i);
+ }
+ i = read_c0_config();
+ scb = i & (1 << 13) ? 7:6; /* scblksize = 2^[7..6] */
+ scw = ((i >> 16) & 7) + 19 - 1; /* scwaysize = 2^[24..19] / 2 */
+
+ i = ((1 << scw) - 1) & ~((1 << scb) - 1);
+ printk(KERN_ERR "S: 0: %08x %08x, 1: %08x %08x (PA[%u:%u] %05x)\n",
+ cache_tags.tags[0][0].hi, cache_tags.tags[0][0].lo,
+ cache_tags.tags[0][1].hi, cache_tags.tags[0][1].lo,
+ scw-1, scb, i & (unsigned)cache_tags.err_addr);
+}
+
+static inline const char *cause_excode_text(int cause)
+{
+ static const char *txt[32] =
+ { "Interrupt",
+ "TLB modification",
+ "TLB (load or instruction fetch)",
+ "TLB (store)",
+ "Address error (load or instruction fetch)",
+ "Address error (store)",
+ "Bus error (instruction fetch)",
+ "Bus error (data: load or store)",
+ "Syscall",
+ "Breakpoint",
+ "Reserved instruction",
+ "Coprocessor unusable",
+ "Arithmetic Overflow",
+ "Trap",
+ "14",
+ "Floating-Point",
+ "16", "17", "18", "19", "20", "21", "22",
+ "Watch Hi/Lo",
+ "24", "25", "26", "27", "28", "29", "30", "31",
+ };
+ return txt[(cause & 0x7c) >> 2];
+}
+
+static void print_buserr(const struct pt_regs *regs)
+{
+ const int field = 2 * sizeof(unsigned long);
+ int error = 0;
+
+ if (extio_stat & EXTIO_MC_BUSERR) {
+ printk(KERN_ERR "MC Bus Error\n");
+ error |= 1;
+ }
+ 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);
+ error |= 2;
+ }
+ if (extio_stat & EXTIO_EISA_BUSERR) {
+ printk(KERN_ERR "EISA Bus Error\n");
+ error |= 4;
+ }
+ 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);
+ error |= 8;
+ }
+ 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);
+ error |= 16;
+ }
+ if (!error)
+ printk(KERN_ERR "MC: Hmm, didn't find any error condition.\n");
+ else {
+ printk(KERN_ERR "CP0: config %08x, "
+ "MC: cpuctrl0/1: %08x/%05x, giopar: %04x\n"
+ "MC: cpu/gio_memacc: %08x/%05x, memcfg0/1: %08x/%08x\n",
+ read_c0_config(),
+ sgimc->cpuctrl0, sgimc->cpuctrl0, sgimc->giopar,
+ sgimc->cmacc, sgimc->gmacc,
+ sgimc->mconfig0, sgimc->mconfig1);
+ print_cache_tags();
+ }
+ printk(KERN_ALERT "%s, epc == %0*lx, ra == %0*lx\n",
+ cause_excode_text(regs->cp0_cause),
+ field, regs->cp0_epc, field, regs->regs[31]);
+}
+
+static int check_microtlb(u32 hi, u32 lo, unsigned long vaddr)
+{
+ /* This is likely rather similar to correct code ;-) */
+
+ vaddr &= 0x7fffffff; /* Doc. states that top bit is ignored */
+
+ /* If tlb-entry is valid and VPN-high (bits [30:21] ?) matches... */
+ if ((lo & 2) && (vaddr >> 21) == ((hi<<1) >> 22)) {
+ u32 ctl = sgimc->dma_ctrl;
+ if (ctl & 1) {
+ unsigned int pgsz = (ctl & 2) ? 14:12; /* 16k:4k */
+ /* PTEIndex is VPN-low (bits [22:14]/[20:12] ?) */
+ unsigned long pte = (lo >> 6) << 12; /* PTEBase */
+ pte += 8*((vaddr >> pgsz) & 0x1ff);
+ if (page_is_ram(PFN_DOWN(pte))) {
+ /*
+ * Note: Since DMA hardware does look up
+ * translation on its own, this PTE *must*
+ * match the TLB/EntryLo-register format !
+ */
+ unsigned long a = *(unsigned long *)
+ PHYS_TO_XKSEG_UNCACHED(pte);
+ a = (a & 0x3f) << 6; /* PFN */
+ a += vaddr & ((1 << pgsz) - 1);
+ return cpu_err_addr == a;
+ }
+ }
+ }
+ return 0;
+}
+
+static int check_vdma_memaddr(void)
+{
+ if (cpu_err_stat & CPU_ERRMASK) {
+ u32 a = sgimc->maddronly;
+
+ if (!(sgimc->dma_ctrl & 0x100)) /* Xlate-bit clear ? */
+ return cpu_err_addr == a;
+
+ if (check_microtlb(sgimc->dtlb_hi0, sgimc->dtlb_lo0, a) ||
+ check_microtlb(sgimc->dtlb_hi1, sgimc->dtlb_lo1, a) ||
+ check_microtlb(sgimc->dtlb_hi2, sgimc->dtlb_lo2, a) ||
+ check_microtlb(sgimc->dtlb_hi3, sgimc->dtlb_lo3, a))
+ return 1;
+ }
+ return 0;
+}
+
+static int check_vdma_gioaddr(void)
+{
+ if (gio_err_stat & GIO_ERRMASK) {
+ u32 a = sgimc->gio_dma_trans;
+ a = (sgimc->gmaddronly & ~a) | (sgimc->gio_dma_sbits & a);
+ return gio_err_addr == a;
+ }
+ return 0;
+}
+
+/*
+ * 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.
+ */
+
+static int ip28_be_interrupt(const struct pt_regs *regs)
+{
+ int i;
+
+ save_and_clear_buserr();
+ /*
+ * Try to find out, whether we got here by a mispredicted speculative
+ * load/store operation. If so, it's not fatal, we can go on.
+ */
+ /* Any cause other than "Interrupt" (ExcCode 0) is fatal. */
+ if (regs->cp0_cause & CAUSEF_EXCCODE)
+ goto mips_be_fatal;
+
+ /* Any cause other than "Bus error interrupt" (IP6) is weird. */
+ if ((regs->cp0_cause & CAUSEF_IP6) != CAUSEF_IP6)
+ goto mips_be_fatal;
+
+ if (extio_stat & (EXTIO_HPC3_BUSERR | EXTIO_EISA_BUSERR))
+ goto mips_be_fatal;
+
+ /* Any state other than "Memory bus error" is fatal. */
+ if (cpu_err_stat & CPU_ERRMASK & ~SGIMC_CSTAT_ADDR)
+ goto mips_be_fatal;
+
+ /* GIO errors other than timeouts are fatal */
+ if (gio_err_stat & GIO_ERRMASK & ~SGIMC_GSTAT_TIME)
+ goto mips_be_fatal;
+
+ /*
+ * Now we have an asynchronous bus error, speculatively or DMA caused.
+ * Need to search all DMA descriptors for the error address.
+ */
+ for (i = 0; i < sizeof(hpc3)/sizeof(struct hpc3_stat); ++i) {
+ struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i;
+ if ((cpu_err_stat & CPU_ERRMASK) &&
+ (cpu_err_addr == hp->ndptr || cpu_err_addr == hp->cbp))
+ break;
+ if ((gio_err_stat & GIO_ERRMASK) &&
+ (gio_err_addr == hp->ndptr || gio_err_addr == hp->cbp))
+ break;
+ }
+ if (i < sizeof(hpc3)/sizeof(struct hpc3_stat)) {
+ struct hpc3_stat *hp = (struct hpc3_stat *)&hpc3 + i;
+ printk(KERN_ERR "at DMA addresses: HPC3 @ %08lx:"
+ " ctl %08x, ndp %08x, cbp %08x\n",
+ CPHYSADDR(hp->addr), hp->ctrl, hp->ndptr, hp->cbp);
+ goto mips_be_fatal;
+ }
+ /* Check MC's virtual DMA stuff. */
+ if (check_vdma_memaddr()) {
+ printk(KERN_ERR "at GIO DMA: mem address 0x%08x.\n",
+ sgimc->maddronly);
+ goto mips_be_fatal;
+ }
+ if (check_vdma_gioaddr()) {
+ printk(KERN_ERR "at GIO DMA: gio address 0x%08x.\n",
+ sgimc->gmaddronly);
+ goto mips_be_fatal;
+ }
+ /* A speculative bus error... */
+ if (debug_be_interrupt) {
+ print_buserr(regs);
+ printk(KERN_ERR "discarded!\n");
+ }
+ return MIPS_BE_DISCARD;
+
+mips_be_fatal:
+ print_buserr(regs);
+ return MIPS_BE_FATAL;
+}
+
+void ip22_be_interrupt(int irq)
+{
+ struct pt_regs *regs = get_irq_regs();
+
+ count_be_interrupt++;
+
+ if (ip28_be_interrupt(regs) != MIPS_BE_DISCARD) {
+ /* Assume it would be too dangerous to continue ... */
+ die_if_kernel("Oops", regs);
+ force_sig(SIGBUS);
+ } else if (debug_be_interrupt)
+ show_regs(regs);
+}
+
+static int ip28_be_handler(struct pt_regs *regs, int is_fixup)
+{
+ /*
+ * We arrive here only in the unusual case of do_be() invocation,
+ * i.e. by a bus error exception without a bus error interrupt.
+ */
+ if (is_fixup) {
+ count_be_is_fixup++;
+ save_and_clear_buserr();
+ return MIPS_BE_FIXUP;
+ }
+ count_be_handler++;
+ return ip28_be_interrupt(regs);
+}
+
+void __init ip22_be_init(void)
+{
+ board_be_handler = ip28_be_handler;
+}
+
+int ip28_show_be_info(struct seq_file *m)
+{
+ seq_printf(m, "IP28 be fixups\t\t: %u\n", count_be_is_fixup);
+ seq_printf(m, "IP28 be interrupts\t: %u\n", count_be_interrupt);
+ seq_printf(m, "IP28 be handler\t\t: %u\n", count_be_handler);
+
+ return 0;
+}
+
+static int __init debug_be_setup(char *str)
+{
+ debug_be_interrupt++;
+ return 1;
+}
+__setup("ip28_debug_be", debug_be_setup);