summaryrefslogtreecommitdiff
path: root/sys/arch/sgi/localbus
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2012-03-28 20:44:24 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2012-03-28 20:44:24 +0000
commit6ea0ed8392999d052b79785548a577d7e55b7184 (patch)
treeb4c5fb05563150bb09cc3d203971a18a2b5277bc /sys/arch/sgi/localbus
parentb71835f84a40d614f56816274c2400a719e82600 (diff)
Work in progress support for the SGI Indigo, Indigo 2 and Indy systems
(IP20, IP22, IP24) in 64-bit mode, adapated from NetBSD. Currently limited to headless operation, input and video drivers will get ported soon. Should work on all R4000, R4440 and R5000 based systems. L2 cache on R5000SC Indy not supported yet (coming soon), R4600 not supported yet either (coming soon as well). Tested to boot multiuser on: Indigo2 R4000SC, Indy R4000PC, Indy R4000SC, Indy R5000SC, Indigo2 R4400SC. There are still glitches in the Ethernet driver which are being looked at. Expansion support is limited to the GIO E++ board; GIO boards with PCI-GIO bridges not ported yet due to the lack of hardware, and this kind of driver does not port blindly. Most of this work comes from NetBSD, polishing and integration work, as well as putting as many ``R4x00 in 64-bit mode'' erratas as necessary, by yours truly. More work is coming, as well as trying to get some easy way to boot install kernels (as older PROM can only boot ECOFF binaries, which won't do for the kernel).
Diffstat (limited to 'sys/arch/sgi/localbus')
-rw-r--r--sys/arch/sgi/localbus/imc.c826
-rw-r--r--sys/arch/sgi/localbus/imcreg.h143
-rw-r--r--sys/arch/sgi/localbus/imcvar.h42
-rw-r--r--sys/arch/sgi/localbus/int.c369
-rw-r--r--sys/arch/sgi/localbus/intreg.h51
-rw-r--r--sys/arch/sgi/localbus/intvar.h34
6 files changed, 1465 insertions, 0 deletions
diff --git a/sys/arch/sgi/localbus/imc.c b/sys/arch/sgi/localbus/imc.c
new file mode 100644
index 00000000000..c8544aa8788
--- /dev/null
+++ b/sys/arch/sgi/localbus/imc.c
@@ -0,0 +1,826 @@
+/* $OpenBSD: imc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: imc.c,v 1.32 2011/07/01 18:53:46 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2012 Miodrag Vallat.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Copyright (c) 2001 Rafal K. Boni
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/device.h>
+#include <sys/systm.h>
+
+#include <mips64/archtype.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+
+#include <sgi/sgi/ip22.h>
+#include <sgi/localbus/imcreg.h>
+#include <sgi/localbus/imcvar.h>
+
+#include <sgi/hpc/hpcreg.h>
+#include <sgi/gio/giovar.h>
+
+#include "eisa.h"
+
+#if NEISA > 0
+#include <dev/eisa/eisavar.h>
+#endif
+
+int imc_match(struct device *, void *, void *);
+void imc_attach(struct device *, struct device *, void *);
+int imc_print(void *, const char *);
+
+const struct cfattach imc_ca = {
+ sizeof(struct device), imc_match, imc_attach
+};
+
+struct cfdriver imc_cd = {
+ NULL, "imc", DV_DULL
+};
+
+uint32_t imc_bus_error(uint32_t, struct trap_frame *);
+void imc_bus_reset(void);
+int imc_watchdog_cb(void *, int);
+
+uint8_t imc_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint16_t imc_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void imc_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void imc_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
+void imc_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t);
+void imc_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+uint32_t imc_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint64_t imc_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void imc_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t);
+void imc_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint64_t);
+void imc_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void imc_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+void imc_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ uint8_t *, bus_size_t);
+void imc_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t,
+ const uint8_t *, bus_size_t);
+int imc_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+void imc_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+int imc_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+void *imc_space_vaddr(bus_space_tag_t, bus_space_handle_t);
+void imc_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, int);
+
+bus_space_t imcbus_tag = {/* not static for gio_cnattch() */
+ PHYS_TO_XKPHYS(0, CCA_NC),
+ NULL,
+ imc_read_1, imc_write_1,
+ imc_read_2, imc_write_2,
+ imc_read_4, imc_write_4,
+ imc_read_8, imc_write_8,
+ imc_read_raw_2, imc_write_raw_2,
+ imc_read_raw_4, imc_write_raw_4,
+ imc_read_raw_8, imc_write_raw_8,
+ imc_space_map, imc_space_unmap, imc_space_region,
+ imc_space_vaddr, imc_space_barrier
+};
+
+#if NEISA > 0
+int imc_eisa_io_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+int imc_eisa_io_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+int imc_eisa_mem_map(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+int imc_eisa_mem_region(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+
+static bus_space_t imcbus_eisa_io_tag = {
+ PHYS_TO_XKPHYS(EISA_IO_BASE, CCA_NC),
+ NULL,
+ imc_read_1, imc_write_1,
+ imc_read_2, imc_write_2,
+ imc_read_4, imc_write_4,
+ imc_read_8, imc_write_8,
+ imc_read_raw_2, imc_write_raw_2,
+ imc_read_raw_4, imc_write_raw_4,
+ imc_read_raw_8, imc_write_raw_8,
+ imc_eisa_io_map, imc_space_unmap, imc_eisa_io_region,
+ imc_space_vaddr, imc_space_barrier
+};
+static bus_space_t imcbus_eisa_mem_tag = {
+ PHYS_TO_XKPHYS(0, CCA_NC),
+ NULL,
+ imc_read_1, imc_write_1,
+ imc_read_2, imc_write_2,
+ imc_read_4, imc_write_4,
+ imc_read_8, imc_write_8,
+ imc_read_raw_2, imc_write_raw_2,
+ imc_read_raw_4, imc_write_raw_4,
+ imc_read_raw_8, imc_write_raw_8,
+ imc_eisa_mem_map, imc_space_unmap, imc_eisa_mem_region,
+ imc_space_vaddr, imc_space_barrier
+};
+#endif
+
+bus_addr_t imc_pa_to_device(paddr_t);
+paddr_t imc_device_to_pa(bus_addr_t);
+
+struct machine_bus_dma_tag imc_bus_dma_tag = {/* not static for gio_cnattch() */
+ NULL, /* _cookie */
+ _dmamap_create,
+ _dmamap_destroy,
+ _dmamap_load,
+ _dmamap_load_mbuf,
+ _dmamap_load_uio,
+ _dmamap_load_raw,
+ _dmamap_load_buffer,
+ _dmamap_unload,
+ _dmamap_sync,
+ _dmamem_alloc,
+ _dmamem_free,
+ _dmamem_map,
+ _dmamem_unmap,
+ _dmamem_mmap,
+ imc_pa_to_device,
+ imc_device_to_pa,
+ 0
+};
+
+/*
+ * Bus access primitives.
+ */
+
+uint8_t
+imc_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint8_t *)(h + o);
+}
+
+uint16_t
+imc_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint16_t *)(h + o);
+}
+
+uint32_t
+imc_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint32_t *)(h + o);
+}
+
+uint64_t
+imc_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint64_t *)(h + o);
+}
+
+void
+imc_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint8_t v)
+{
+ *(volatile uint8_t *)(h + o) = v;
+}
+
+void
+imc_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint16_t v)
+{
+ *(volatile uint16_t *)(h + o) = v;
+}
+
+void
+imc_write_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint32_t v)
+{
+ *(volatile uint32_t *)(h + o) = v;
+}
+
+void
+imc_write_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o, uint64_t v)
+{
+ *(volatile uint64_t *)(h + o) = v;
+}
+
+void
+imc_read_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ uint8_t *buf, bus_size_t len)
+{
+ volatile uint16_t *addr = (volatile uint16_t *)(h + o);
+ len >>= 1;
+ while (len-- != 0) {
+ *(uint16_t *)buf = *addr;
+ buf += 2;
+ }
+}
+
+void
+imc_write_raw_2(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ const uint8_t *buf, bus_size_t len)
+{
+ volatile uint16_t *addr = (volatile uint16_t *)(h + o);
+ len >>= 1;
+ while (len-- != 0) {
+ *addr = *(uint16_t *)buf;
+ buf += 2;
+ }
+}
+
+void
+imc_read_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ uint8_t *buf, bus_size_t len)
+{
+ volatile uint32_t *addr = (volatile uint32_t *)(h + o);
+ len >>= 2;
+ while (len-- != 0) {
+ *(uint32_t *)buf = *addr;
+ buf += 4;
+ }
+}
+
+void
+imc_write_raw_4(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ const uint8_t *buf, bus_size_t len)
+{
+ volatile uint32_t *addr = (volatile uint32_t *)(h + o);
+ len >>= 2;
+ while (len-- != 0) {
+ *addr = *(uint32_t *)buf;
+ buf += 4;
+ }
+}
+
+void
+imc_read_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ uint8_t *buf, bus_size_t len)
+{
+ volatile uint64_t *addr = (volatile uint64_t *)(h + o);
+ len >>= 3;
+ while (len-- != 0) {
+ *(uint64_t *)buf = *addr;
+ buf += 8;
+ }
+}
+
+void
+imc_write_raw_8(bus_space_tag_t t, bus_space_handle_t h, bus_addr_t o,
+ const uint8_t *buf, bus_size_t len)
+{
+ volatile uint64_t *addr = (volatile uint64_t *)(h + o);
+ len >>= 3;
+ while (len-- != 0) {
+ *addr = *(uint64_t *)buf;
+ buf += 8;
+ }
+}
+
+int
+imc_space_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size,
+ int flags, bus_space_handle_t *bshp)
+{
+ *bshp = t->bus_base + offs;
+ return 0;
+}
+
+void
+imc_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
+{
+}
+
+int
+imc_space_region(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
+{
+ *nbshp = bsh + offset;
+ return 0;
+}
+
+void *
+imc_space_vaddr(bus_space_tag_t t, bus_space_handle_t h)
+{
+ return (void *)h;
+}
+
+void
+imc_space_barrier(bus_space_tag_t t, bus_space_handle_t h, bus_size_t offs,
+ bus_size_t len, int flags)
+{
+ __asm__ __volatile__ ("sync" ::: "memory");
+}
+
+#if NEISA > 0
+int
+imc_eisa_io_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags,
+ bus_space_handle_t *bshp)
+{
+ if (offs + size > EISA_IO_END - EISA_IO_BASE)
+ return EINVAL;
+
+ *bshp = t->bus_base + offs;
+ return 0;
+}
+
+int
+imc_eisa_io_region(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t offset,
+ bus_size_t size, bus_space_handle_t *nbshp)
+{
+ if ((bsh - t->bus_base) + offset + size > EISA_IO_END - EISA_IO_BASE)
+ return EINVAL;
+
+ *nbshp = bsh + offset;
+ return 0;
+}
+
+int
+imc_eisa_mem_map(bus_space_tag_t t, bus_addr_t offs, bus_size_t size, int flags,
+ bus_space_handle_t *bshp)
+{
+ if ((offs >= EISA_MEM0_BASE && offs + size <= EISA_MEM0_END) ||
+ (offs >= EISA_MEM1_BASE && offs + size <= EISA_MEM1_END)) {
+ *bshp = t->bus_base + offs;
+ return 0;
+ }
+
+ return EINVAL;
+}
+
+int
+imc_eisa_mem_region(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
+{
+ bus_addr_t orig = bsh - t->bus_base;
+
+ if ((orig >= EISA_MEM0_BASE && orig + offset + size <= EISA_MEM0_END) ||
+ (orig >= EISA_MEM1_BASE && orig + offset + size <= EISA_MEM1_END)) {
+ *nbshp = t->bus_base + offset;
+ return 0;
+ }
+
+ return EINVAL;
+}
+#endif
+
+bus_addr_t
+imc_pa_to_device(paddr_t pa)
+{
+ return (bus_addr_t)pa;
+}
+
+paddr_t
+imc_device_to_pa(bus_addr_t addr)
+{
+ return (paddr_t)addr;
+}
+
+/*
+ * Autoconf glue.
+ */
+
+int
+imc_match(struct device *parent, void *match, void *aux)
+{
+ struct mainbus_attach_args *maa = (void *)aux;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ break;
+ default:
+ return 0;
+ }
+
+ return strcmp(maa->maa_name, imc_cd.cd_name) == 0;
+}
+
+void
+imc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct imc_attach_args iaa;
+#if NEISA > 0
+ struct eisabus_attach_args eba;
+#endif
+ uint32_t reg;
+ uint32_t id, rev;
+ int have_eisa;
+
+ id = imc_read(IMC_SYSID);
+ rev = id & IMC_SYSID_REVMASK;
+
+ /* EISA exists on Indigo2 only */
+ if (sys_config.system_type != SGI_IP20 &&
+ sys_config.system_subtype == IP22_INDIGO2)
+ have_eisa = (id & IMC_SYSID_HAVEISA) != 0;
+ else
+ have_eisa = 0;
+
+ printf(": revision %d\n", rev);
+
+ /* Clear CPU/GIO error status registers to clear any leftover bits. */
+ imc_bus_reset();
+
+ /* Disable watchdog if leftover from previous reboot */
+ imc_watchdog_cb(self, 0);
+
+ /* Hook the bus error handler into the ISR */
+ set_intr(INTPRI_BUSERR, CR_INT_4, imc_bus_error);
+
+ /*
+ * Enable parity reporting on GIO/main memory transactions.
+ * Disable parity checking on CPU bus transactions (as turning
+ * it on seems to cause spurious bus errors), but enable parity
+ * checking on CPU reads from main memory (note that this bit
+ * has the opposite sense... Turning it on turns the checks off!).
+ * Finally, turn on interrupt writes to the CPU from the MC.
+ */
+ reg = imc_read(IMC_CPUCTRL0);
+ reg &= ~IMC_CPUCTRL0_NCHKMEMPAR;
+ reg |= (IMC_CPUCTRL0_GPR | IMC_CPUCTRL0_MPR | IMC_CPUCTRL0_INTENA);
+ imc_write(IMC_CPUCTRL0, reg);
+
+ /* Setup the MC write buffer depth */
+ reg = imc_read(IMC_CPUCTRL1);
+ reg = (reg & ~IMC_CPUCTRL1_MCHWMSK) | 13;
+
+ /*
+ * Force endianness on the onboard HPC and both slots.
+ * This should be safe for Fullhouse, but leave it conditional
+ * for now.
+ */
+ switch (sys_config.system_type) {
+ case SGI_IP22:
+ if (sys_config.system_subtype != IP22_INDY)
+ break;
+ /* FALLTHROUGH */
+ case SGI_IP20:
+ reg |= IMC_CPUCTRL1_HPCFX;
+ reg |= IMC_CPUCTRL1_EXP0FX;
+ reg |= IMC_CPUCTRL1_EXP1FX;
+ reg &= ~IMC_CPUCTRL1_HPCLITTLE;
+ reg &= ~IMC_CPUCTRL1_EXP0LITTLE;
+ reg &= ~IMC_CPUCTRL1_EXP1LITTLE;
+ break;
+ }
+ imc_write(IMC_CPUCTRL1, reg);
+
+ /*
+ * Set GIO64 arbitrator configuration register:
+ *
+ * Preserve PROM-set graphics-related bits, as they seem to depend
+ * on the graphics variant present and I'm not sure how to figure
+ * that out or 100% sure what the correct settings are for each.
+ */
+ reg = imc_read(IMC_GIO64ARB);
+ reg &= (IMC_GIO64ARB_GRX64 | IMC_GIO64ARB_GRXRT | IMC_GIO64ARB_GRXMST);
+
+ /*
+ * Rest of settings are machine/board dependent
+ * XXX I wonder if this even works as advertized. The logic apparently
+ * XXX comes from Linux, but the EISA settings look horribly broken to
+ * XXX me -- miod
+ */
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ reg |= IMC_GIO64ARB_ONEGIO;
+ reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT;
+ reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST;
+ reg &= ~(IMC_GIO64ARB_HPC64 |
+ IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EISA64 |
+ IMC_GIO64ARB_EXP064 | IMC_GIO64ARB_EXP164 |
+ IMC_GIO64ARB_EXP0PIPE | IMC_GIO64ARB_EXP1PIPE);
+ break;
+ default:
+ /*
+ * GIO64 invariant for all IP22 platforms: one GIO bus,
+ * HPC1 @ 64
+ */
+ reg |= IMC_GIO64ARB_ONEGIO | IMC_GIO64ARB_HPC64;
+
+ switch (sys_config.system_subtype) {
+ default:
+ case IP22_INDY:
+ /* XXX is MST mutually exclusive? */
+ reg |= IMC_GIO64ARB_EXP0RT | IMC_GIO64ARB_EXP1RT;
+ reg |= IMC_GIO64ARB_EXP0MST | IMC_GIO64ARB_EXP1MST;
+
+ /* EISA can bus-master, is 64-bit */
+ reg |= IMC_GIO64ARB_EISAMST | IMC_GIO64ARB_EISA64;
+ break;
+
+ case IP22_INDIGO2:
+ /*
+ * All Fullhouse boards have a 64-bit HPC2 and pipelined
+ * EXP0 slot.
+ */
+ reg |= IMC_GIO64ARB_HPCEXP64 | IMC_GIO64ARB_EXP0PIPE;
+
+ if (rev < 2) {
+ /* EXP0 realtime, EXP1 can master */
+ reg |= IMC_GIO64ARB_EXP0RT |
+ IMC_GIO64ARB_EXP1MST;
+ } else {
+ /* EXP1 pipelined as well, EISA masters */
+ reg |= IMC_GIO64ARB_EXP1PIPE |
+ IMC_GIO64ARB_EISAMST;
+ }
+ break;
+ }
+ }
+
+ imc_write(IMC_GIO64ARB, reg);
+
+#if NEISA > 0
+ if (have_eisa) {
+ memset(&eba, 0, sizeof(eba));
+ eba.eba_busname = "eisa";
+ eba.eba_iot = &imcbus_eisa_io_tag;
+ eba.eba_memt = &imcbus_eisa_mem_tag;
+ eba.eba_dmat = &imc_bus_dma_tag;
+ eba.eba_ec = NULL;
+ config_found(self, &eba, imc_print);
+ }
+#endif
+
+ memset(&iaa, 0, sizeof(iaa));
+ iaa.iaa_name = "gio";
+ iaa.iaa_st = &imcbus_tag;
+ iaa.iaa_dmat = &imc_bus_dma_tag;
+ config_found(self, &iaa, imc_print);
+
+ /* Clear CPU/GIO error status registers to clear any leftover bits. */
+ imc_bus_reset();
+
+ /* Register watchdog */
+ wdog_register(self, imc_watchdog_cb);
+}
+
+int
+imc_print(void *aux, const char *name)
+{
+ struct imc_attach_args *iaa = aux;
+
+ if (name != NULL)
+ printf("%s at %s", iaa->iaa_name, name);
+
+ return UNCONF;
+}
+
+void
+imc_bus_reset()
+{
+ imc_write(IMC_CPU_ERRSTAT, 0);
+ imc_write(IMC_GIO_ERRSTAT, 0);
+}
+
+uint32_t
+imc_bus_error(uint32_t hwpend, struct trap_frame *tf)
+{
+ printf("bus error: cpu_stat %08x addr %08x, gio_stat %08x addr %08x\n",
+ imc_read(IMC_CPU_ERRSTAT),
+ imc_read(IMC_CPU_ERRADDR),
+ imc_read(IMC_GIO_ERRSTAT),
+ imc_read(IMC_GIO_ERRADDR));
+ imc_bus_reset();
+
+ return hwpend;
+}
+
+int
+imc_watchdog_cb(void *v, int period)
+{
+ uint32_t reg;
+
+ if (period == 0) {
+ /* reset... */
+ imc_write(IMC_WDOG, 0);
+ /* ...and disable */
+ reg = imc_read(IMC_CPUCTRL0);
+ reg &= ~(IMC_CPUCTRL0_WDOG);
+ imc_write(IMC_CPUCTRL0, reg);
+
+ return 0;
+ } else {
+ /* enable... */
+ reg = imc_read(IMC_CPUCTRL0);
+ reg |= IMC_CPUCTRL0_WDOG;
+ imc_write(IMC_CPUCTRL0, reg);
+ /* ...and reset */
+ imc_write(IMC_WDOG, 0);
+
+ /*
+ * The watchdog period is not controllable; it will fire
+ * when the 20 bit counter, running on a 64 usec clock,
+ * overflows.
+ */
+ return (64 << 20) / 1000000;
+ }
+}
+
+/* intended to be called from gio/gio.c only */
+int
+imc_gio64_arb_config(int slot, uint32_t flags)
+{
+ uint32_t reg;
+
+ if (sys_config.system_type == SGI_IP20 ||
+ sys_config.system_subtype != IP22_INDIGO2) {
+ /* GIO_SLOT_GFX is only usable on Fullhouse */
+ if (slot == GIO_SLOT_GFX)
+ return EINVAL;
+ } else {
+ /* GIO_SLOT_EXP1 is unusable on Fullhouse */
+ if (slot == GIO_SLOT_EXP1)
+ return EINVAL;
+ }
+
+ /* GIO_SLOT_GFX is always pipelined */
+ if (slot == GIO_SLOT_GFX && (flags & GIO_ARB_NOPIPE))
+ return EINVAL;
+
+ /* IP20 does not support pipelining (XXX what about Indy?) */
+ if (((flags & GIO_ARB_PIPE) || (flags & GIO_ARB_NOPIPE)) &&
+ sys_config.system_type == SGI_IP20)
+ return EINVAL;
+
+ reg = imc_read(IMC_GIO64ARB);
+
+ if (flags & GIO_ARB_RT) {
+ if (slot == GIO_SLOT_EXP0)
+ reg |= IMC_GIO64ARB_EXP0RT;
+ else if (slot == GIO_SLOT_EXP1)
+ reg |= IMC_GIO64ARB_EXP1RT;
+ else if (slot == GIO_SLOT_GFX)
+ reg |= IMC_GIO64ARB_GRXRT;
+ }
+
+ if (flags & GIO_ARB_MST) {
+ if (slot == GIO_SLOT_EXP0)
+ reg |= IMC_GIO64ARB_EXP0MST;
+ else if (slot == GIO_SLOT_EXP1)
+ reg |= IMC_GIO64ARB_EXP1MST;
+ else if (slot == GIO_SLOT_GFX)
+ reg |= IMC_GIO64ARB_GRXMST;
+ }
+
+ if (flags & GIO_ARB_PIPE) {
+ if (slot == GIO_SLOT_EXP0)
+ reg |= IMC_GIO64ARB_EXP0PIPE;
+ else if (slot == GIO_SLOT_EXP1)
+ reg |= IMC_GIO64ARB_EXP1PIPE;
+ }
+
+ if (flags & GIO_ARB_LB) {
+ if (slot == GIO_SLOT_EXP0)
+ reg &= ~IMC_GIO64ARB_EXP0RT;
+ else if (slot == GIO_SLOT_EXP1)
+ reg &= ~IMC_GIO64ARB_EXP1RT;
+ else if (slot == GIO_SLOT_GFX)
+ reg &= ~IMC_GIO64ARB_GRXRT;
+ }
+
+ if (flags & GIO_ARB_SLV) {
+ if (slot == GIO_SLOT_EXP0)
+ reg &= ~IMC_GIO64ARB_EXP0MST;
+ else if (slot == GIO_SLOT_EXP1)
+ reg &= ~IMC_GIO64ARB_EXP1MST;
+ else if (slot == GIO_SLOT_GFX)
+ reg &= ~IMC_GIO64ARB_GRXMST;
+ }
+
+ if (flags & GIO_ARB_NOPIPE) {
+ if (slot == GIO_SLOT_EXP0)
+ reg &= ~IMC_GIO64ARB_EXP0PIPE;
+ else if (slot == GIO_SLOT_EXP1)
+ reg &= ~IMC_GIO64ARB_EXP1PIPE;
+ }
+
+ if (flags & GIO_ARB_32BIT) {
+ if (slot == GIO_SLOT_EXP0)
+ reg &= ~IMC_GIO64ARB_EXP064;
+ else if (slot == GIO_SLOT_EXP1)
+ reg &= ~IMC_GIO64ARB_EXP164;
+ }
+
+ if (flags & GIO_ARB_64BIT) {
+ if (slot == GIO_SLOT_EXP0)
+ reg |= IMC_GIO64ARB_EXP064;
+ else if (slot == GIO_SLOT_EXP1)
+ reg |= IMC_GIO64ARB_EXP164;
+ }
+
+ if (flags & GIO_ARB_HPC2_32BIT)
+ reg &= ~IMC_GIO64ARB_HPCEXP64;
+
+ if (flags & GIO_ARB_HPC2_64BIT)
+ reg |= IMC_GIO64ARB_HPCEXP64;
+
+ imc_write(IMC_GIO64ARB, reg);
+
+ return 0;
+}
+
+/*
+ * According to chapter 19 of the "IRIX Device Driver Programmer's Guide",
+ * some GIO devices, which do not drive all data lines, may cause false
+ * memory read parity errors on the SysAD bus. The workaround is to disable
+ * parity checking.
+ */
+void
+imc_disable_sysad_parity(void)
+{
+ uint32_t reg;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ break;
+ default:
+ return;
+ }
+
+ reg = imc_read(IMC_CPUCTRL0);
+ reg |= IMC_CPUCTRL0_NCHKMEMPAR;
+ imc_write(IMC_CPUCTRL0, reg);
+}
+
+void
+imc_enable_sysad_parity(void)
+{
+ uint32_t reg;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ break;
+ default:
+ return;
+ }
+
+ reg = imc_read(IMC_CPUCTRL0);
+ reg &= ~IMC_CPUCTRL0_NCHKMEMPAR;
+ imc_write(IMC_CPUCTRL0, reg);
+}
+
+#if 0
+int
+imc_is_sysad_parity_enabled(void)
+{
+ uint32_t reg;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ break;
+ default:
+ return 0;
+ }
+
+ reg = imc_read(IMC_CPUCTRL0);
+
+ return reg & IMC_CPUCTRL0_NCHKMEMPAR;
+}
+#endif
diff --git a/sys/arch/sgi/localbus/imcreg.h b/sys/arch/sgi/localbus/imcreg.h
new file mode 100644
index 00000000000..fd27ff4c9be
--- /dev/null
+++ b/sys/arch/sgi/localbus/imcreg.h
@@ -0,0 +1,143 @@
+/* $OpenBSD: imcreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: imcreg.h,v 1.4 2005/12/11 12:18:52 christos Exp $ */
+
+/*
+ * Copyright (c) 2001 Rafal K. Boni
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#define IMC_BASE 0x1fa00000
+
+#define IMC_CPUCTRL0 0x04 /* CPU control, register 0 */
+
+#define IMC_CPUCTRL0_REFMASK 0x000f /* # lines to refresh */
+#define IMC_CPUCTRL0_RFE 0x0010 /* refresh enable */
+#define IMC_CPUCTRL0_GPR 0x0020 /* GIO parity enable */
+#define IMC_CPUCTRL0_MPR 0x0040 /* memory parity enable */
+#define IMC_CPUCTRL0_CPR 0x0080 /* cpu bus parity enable */
+#define IMC_CPUCTRL0_WDOG 0x0100 /* watchdog enable */
+#define IMC_CPUCTRL0_SIN 0x0200 /* reset system */
+#define IMC_CPUCTRL0_GRR 0x0400 /* graphics reset */
+#define IMC_CPUCTRL0_ENLOCK 0x0800 /* enable EISA memory lock */
+#define IMC_CPUCTRL0_CMDPAR 0x1000 /* SysCmd parity enable */
+#define IMC_CPUCTRL0_INTENA 0x2000 /* enable CPU interrupts */
+#define IMC_CPUCTRL0_SNOOPENA 0x4000 /* enable gfx DMA snoop */
+#define IMC_CPUCTRL0_PROM_WRENA 0x8000 /* disable buserr on PROM
+ * writes */
+#define IMC_CPUCTRL0_WRST 0x00010000 /* warm restart (reset cpu) */
+/* Bit 17 reserved 0x00020000 */
+#define IMC_CPUCTRL0_LITTLE 0x00040000 /* MC little-endian toggle */
+#define IMC_CPUCTRL0_WRRST 0x00080000 /* cpu warm reset */
+#define IMC_CPUCTRL0_MUXHWMSK 0x01f00000 /* MUX fifo high-water mask */
+#define IMC_CPUCTRL0_BADPAR 0x02000000 /* generate bad parity on
+ * CPU->memory writes */
+#define IMC_CPUCTRL0_NCHKMEMPAR 0x04000000 /* disable CPU parity check
+ * on memory reads. */
+#define IMC_CPUCTRL0_BACK2 0x08000000 /* enable back2back GIO wrt */
+#define IMC_CPUCTRL0_BUSRTMSK 0xf0000000 /* stall cycle for berr data */
+
+#define IMC_CPUCTRL1 0x0c /* CPU control, register 1 */
+#define IMC_CPUCTRL1_MCHWMSK 0x0000000f /* MC FIFO high water mask */
+#define IMC_CPUCTRL1_ABORTEN 0x00000010 /* Enable GIO bus timeouts */
+/* Bits 5 - 11 reserved 0x00000fe0 */
+#define IMC_CPUCTRL1_HPCFX 0x00001000 /* HPC endian fix */
+#define IMC_CPUCTRL1_HPCLITTLE 0x00002000 /* HPC DMA is little-endian */
+#define IMC_CPUCTRL1_EXP0FX 0x00004000 /* EXP0 endian fix */
+#define IMC_CPUCTRL1_EXP0LITTLE 0x00008000 /* EXP0 DMA is little-endian */
+#define IMC_CPUCTRL1_EXP1FX 0x00010000 /* EXP1 endian fix */
+#define IMC_CPUCTRL1_EXP1LITTLE 0x00020000 /* EXP1 DMA is little-endian */
+
+#define IMC_WDOG 0x14 /* Watchdog counter */
+#define IMC_WDOG_MASK 0x001fffff /* counter mask */
+
+#define IMC_SYSID 0x1c /* MC revision register */
+#define IMC_SYSID_REVMASK 0x0000000f /* MC revision mask */
+#define IMC_SYSID_HAVEISA 0x00000010 /* EISA present */
+
+#define IMC_RPSSDIV 0x2c /* RPSS divider */
+#define IMC_RPSSDIV_DIVMSK 0x000000ff /* RPC divider mask */
+#define IMC_RPSSDIV_INCMSK 0x0000ff00 /* RPC increment mask */
+
+#define IMC_EEPROM 0x34 /* EEPROM serial interface */
+/* Bit 1 is reserved 0x00000001 */
+#define IMC_EEPROM_CS 0x00000002 /* EEPROM chip select */
+#define IMC_EEPROM_SCK 0x00000004 /* EEPROM serial clock */
+#define IMC_EEPROM_SO 0x00000008 /* Serial data to EEPROM */
+#define IMC_EEPROM_SI 0x00000010 /* Serial data from EEPROM */
+
+#define IMC_CTRLD 0x44 /* Refresh counter preload */
+#define IMC_CTRLD_MSK 0x000000ff /* Counter preload mask */
+
+#define IMC_REFCTR 0x4c /* Refresh counter */
+#define IMC_REFCTR_MSK 0x000000ff /* Refresh counter mask */
+
+#define IMC_GIO64ARB 0x84 /* GIO64 arbitration params */
+#define IMC_GIO64ARB_HPC64 0x00000001 /* HPC addr size (32/64bit) */
+#define IMC_GIO64ARB_GRX64 0x00000002 /* Gfx addr size (32/64bit) */
+#define IMC_GIO64ARB_EXP064 0x00000004 /* EXP0 addr size (32/64bit) */
+#define IMC_GIO64ARB_EXP164 0x00000008 /* EXP0 addr size (32/64bit) */
+#define IMC_GIO64ARB_EISA64 0x00000010 /* EISA addr size (32/64bit) */
+#define IMC_GIO64ARB_HPCEXP64 0x00000020 /* HPC2 addr size (32/64bit) */
+#define IMC_GIO64ARB_GRXRT 0x00000040 /* Gfx is realtime device */
+#define IMC_GIO64ARB_EXP0RT 0x00000080 /* EXP0 is realtime device */
+#define IMC_GIO64ARB_EXP1RT 0x00000100 /* EXP1 is realtime device */
+#define IMC_GIO64ARB_EISAMST 0x00000200 /* EISA can be busmaster */
+#define IMC_GIO64ARB_ONEGIO 0x00000400 /* Only one GIO64 bus */
+#define IMC_GIO64ARB_GRXMST 0x00000800 /* Gfx can be busmaster */
+#define IMC_GIO64ARB_EXP0MST 0x00001000 /* EXP0 can be busmaster */
+#define IMC_GIO64ARB_EXP1MST 0x00002000 /* EXP1 can be busmaster */
+#define IMC_GIO64ARB_EXP0PIPE 0x00004000 /* EXP0 is pipelined */
+#define IMC_GIO64ARB_EXP1PIPE 0x00008000 /* EXP1 is pipelined */
+
+#define IMC_CPUTIME 0x8c /* Arbiter CPU time period */
+
+#define IMC_LBTIME 0x9c /* Arbiter long-burst time */
+
+#define IMC_MEMCFG0 0xc4 /* Mem config, register 0 */
+#define IMC_MEMCFG1 0xcc /* Mem config, register 1 */
+#define IMC_MEMC_BANK_MASK 0x0000ffff
+#define IMC_MEMC_BANK_SHIFT 16
+#define IMC_MEMC_ADDR_MASK 0x00ff
+#define IMC_MEMC_ADDR_SHIFT 0
+#define IMC_MEMC_SIZE_MASK 0x1f00
+#define IMC_MEMC_SIZE_SHIFT 8
+#define IMC_MEMC_LSHIFT 22 /* 4MB units */
+#define IMC_MEMC_LSHIFT_HUGE 24 /* 16MB units */
+#define IMC_MEMC_VALID 0x2000
+#define IMC_MEMC_SUBBANKS 0x4000
+
+#define IMC_CPU_MEMACC 0xd4 /* CPU mem access config */
+
+#define IMC_GIO_MEMACC 0xdc /* GIO mem access config */
+
+#define IMC_CPU_ERRADDR 0xe4 /* CPU error address */
+
+#define IMC_CPU_ERRSTAT 0xec /* CPU error status */
+
+#define IMC_GIO_ERRADDR 0xf4 /* GIO error address */
+
+#define IMC_GIO_ERRSTAT 0xfc /* GIO error status */
+
+#define IMC_RPSS 0x1004 /* RPSS counter */
diff --git a/sys/arch/sgi/localbus/imcvar.h b/sys/arch/sgi/localbus/imcvar.h
new file mode 100644
index 00000000000..651d23f9d3e
--- /dev/null
+++ b/sys/arch/sgi/localbus/imcvar.h
@@ -0,0 +1,42 @@
+/* $OpenBSD: imcvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: imcvar.h,v 1.1 2006/08/30 23:44:52 rumble Exp $ */
+
+/*
+ * Copyright (c) 2006 Stephen M. Rumble
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+struct imc_attach_args {
+ const char *iaa_name;
+ bus_space_tag_t iaa_st;
+ bus_dma_tag_t iaa_dmat;
+};
+
+int imc_gio64_arb_config(int, uint32_t);
+void imc_disable_sysad_parity(void);
+void imc_enable_sysad_parity(void);
+int imc_is_sysad_parity_enabled(void);
+
+#define imc_read(o) \
+ *(volatile uint32_t *)PHYS_TO_XKPHYS(IMC_BASE + (o), CCA_NC)
+#define imc_write(o,v) \
+ *(volatile uint32_t *)PHYS_TO_XKPHYS(IMC_BASE + (o), CCA_NC) = (v)
diff --git a/sys/arch/sgi/localbus/int.c b/sys/arch/sgi/localbus/int.c
new file mode 100644
index 00000000000..01e8b2a5dde
--- /dev/null
+++ b/sys/arch/sgi/localbus/int.c
@@ -0,0 +1,369 @@
+/* $OpenBSD: int.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: int.c,v 1.24 2011/07/01 18:53:46 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2009 Stephen M. Rumble
+ * Copyright (c) 2004 Christopher SEKIYA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * INT2 (IP20, IP22) /INT3 (IP24) interrupt controllers
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <mips64/archtype.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/ic/i8253reg.h>
+
+#include <sgi/localbus/intreg.h>
+#include <sgi/localbus/intvar.h>
+#include <sgi/sgi/ip22.h>
+
+int int2_match(struct device *, void *, void *);
+void int2_attach(struct device *, struct device *, void *);
+int int2_mappable_intr(void *);
+
+const struct cfattach int_ca = {
+ sizeof(struct device), int2_match, int2_attach
+};
+
+struct cfdriver int_cd = {
+ NULL, "int", DV_DULL
+};
+
+paddr_t int2_base;
+
+#define int2_read(r) *(volatile uint8_t *)(int2_base + (r))
+#define int2_write(r, v) *(volatile uint8_t *)(int2_base + (r)) = (v)
+
+/*
+ * INT2 Interrupt handling declarations: 16 local sources on 2 levels.
+ * (we don't use the i8254 timer interrupts)
+ *
+ * In addition to this, INT3 provides 8 so-called mappable interrupts, which
+ * are cascaded to either one of the unused two INT2 VME interrupts.
+ * To make things easier from a software viewpoint, we pretend there are
+ * 16 of them - one set of 8 per cascaded interrupt. This allows for
+ * faster recognition on where to connect these interrupts - as long as
+ * interrupt vector assignment makes sure no mappable interrupt is
+ * registered on both cascaded interrupts.
+ */
+
+#define INT2_NINTS (8 + 8 + 2 * 8)
+struct intrhand *int2_intrhand[INT2_NINTS];
+
+uint32_t int2_intem;
+uint8_t int2_l0imask[NIPLS], int2_l1imask[NIPLS];
+
+void int2_splx(int);
+uint32_t int2_l0intr(uint32_t, struct trap_frame *);
+void int2_l0makemasks(void);
+uint32_t int2_l1intr(uint32_t, struct trap_frame *);
+void int2_l1makemasks(void);
+
+/*
+ * Level 0 interrupt handler.
+ */
+
+uint32_t save_l0imr, save_l0isr, save_l0ipl;
+#define INTR_FUNCTIONNAME int2_l0intr
+#define MASK_FUNCTIONNAME int2_l0makemasks
+
+#define INTR_LOCAL_DECLS
+#define MASK_LOCAL_DECLS
+#define INTR_GETMASKS \
+do { \
+ isr = int2_read(INT2_LOCAL0_STATUS); \
+ imr = int2_read(INT2_LOCAL0_MASK); \
+ bit = 7; \
+save_l0isr = isr; save_l0imr = imr; save_l0ipl = frame->ipl; \
+} while (0)
+#define INTR_MASKPENDING \
+ int2_write(INT2_LOCAL0_MASK, imr & ~isr)
+#define INTR_IMASK(ipl) int2_l0imask[ipl]
+#define INTR_HANDLER(bit) int2_intrhand[bit + 0]
+#define INTR_SPURIOUS(bit) \
+do { \
+ printf("spurious int2 interrupt %d\n", bit); \
+} while (0)
+#define INTR_MASKRESTORE \
+ int2_write(INT2_LOCAL0_MASK, imr)
+#define INTR_MASKSIZE 8
+
+#include <sgi/sgi/intr_template.c>
+
+/*
+ * Level 1 interrupt handler.
+ */
+
+uint32_t save_l1imr, save_l1isr, save_l1ipl;
+#define INTR_FUNCTIONNAME int2_l1intr
+#define MASK_FUNCTIONNAME int2_l1makemasks
+
+#define INTR_LOCAL_DECLS
+#define MASK_LOCAL_DECLS
+#define INTR_GETMASKS \
+do { \
+ isr = int2_read(INT2_LOCAL1_STATUS); \
+ imr = int2_read(INT2_LOCAL1_MASK); \
+ bit = 7; \
+save_l1isr = isr; save_l1imr = imr; save_l1ipl = frame->ipl; \
+} while (0)
+#define INTR_MASKPENDING \
+ int2_write(INT2_LOCAL1_MASK, imr & ~isr)
+#define INTR_IMASK(ipl) int2_l1imask[ipl]
+#define INTR_HANDLER(bit) int2_intrhand[bit + 8]
+#define INTR_SPURIOUS(bit) \
+do { \
+ printf("spurious int2 interrupt %d\n", bit + 8); \
+} while (0)
+#define INTR_MASKRESTORE \
+ int2_write(INT2_LOCAL1_MASK, imr)
+#define INTR_MASKSIZE 8
+
+#include <sgi/sgi/intr_template.c>
+
+void *
+int2_intr_establish(int irq, int level, int (*ih_fun) (void *),
+ void *ih_arg, const char *ih_what)
+{
+ struct intrhand **p, *q, *ih;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (irq < 0 || irq >= INT2_NINTS)
+ panic("int2_intr_establish: illegal irq %d", irq);
+#endif
+
+ ih = malloc(sizeof *ih, M_DEVBUF, M_NOWAIT);
+ if (ih == NULL)
+ return NULL;
+
+ ih->ih_next = NULL;
+ ih->ih_fun = ih_fun;
+ ih->ih_arg = ih_arg;
+ ih->ih_level = level;
+ ih->ih_irq = irq;
+ if (ih_what != NULL)
+ evcount_attach(&ih->ih_count, ih_what, &ih->ih_irq);
+
+ s = splhigh();
+
+ for (p = &int2_intrhand[irq]; (q = *p) != NULL; p = &q->ih_next)
+ ;
+ *p = ih;
+
+ int2_intem |= 1 << irq;
+ switch (irq >> 3) {
+ case 0:
+ int2_l0makemasks();
+ break;
+ case 1:
+ int2_l1makemasks();
+ break;
+ /*
+ * We do not maintain masks for mappable interrupts. They are
+ * masked as a whole, by the level 0 or 1 interrupt they cascade to.
+ */
+ case 2:
+ int2_write(INT2_MAP_MASK0,
+ int2_read(INT2_MAP_MASK0) | (1 << (irq & 7)));
+ break;
+ case 3:
+ int2_write(INT2_MAP_MASK1,
+ int2_read(INT2_MAP_MASK1) | (1 << (irq & 7)));
+ break;
+ }
+
+ splx(s); /* will cause hardware mask update */
+
+ return ih;
+}
+
+void
+int2_splx(int newipl)
+{
+ struct cpu_info *ci = curcpu();
+ uint32_t sr;
+
+ __asm__ ("\t.set noreorder\n");
+ ci->ci_ipl = newipl;
+ __asm__ ("sync\n\t.set reorder\n");
+
+ sr = disableintr(); /* XXX overkill? */
+ int2_write(INT2_LOCAL1_MASK, (int2_intem >> 8) & ~int2_l1imask[newipl]);
+ int2_write(INT2_LOCAL0_MASK, int2_intem & ~int2_l0imask[newipl]);
+ setsr(sr);
+
+ if (ci->ci_softpending != 0 && newipl < IPL_SOFTINT)
+ setsoftintr0();
+}
+
+/*
+ * Mappable interrupts handler.
+ */
+
+int
+int2_mappable_intr(void *arg)
+{
+ uint which = (unsigned long)arg;
+ uint64_t imr, isr;
+ uint i, intnum;
+ struct intrhand *ih;
+ int rc, ret;
+
+ isr = int2_read(INT2_MAP_STATUS);
+ imr = int2_read(INT2_MAP_MASK0 + (which << 2));
+
+ isr &= imr;
+ if (isr == 0)
+ return 0; /* not for us */
+
+ /*
+ * Don't bother masking sources here - all mappable interrupts are
+ * tied to either a level 1 or level 0 interrupt, and the dispatcher
+ * is registered at IPL_TTY, so we can safely assume we are running
+ * at IPL_TTY now.
+ */
+
+ for (i = 0; i < 8; i++) {
+ intnum = i + 16 + (which << 3);
+ if (isr & (1 << i)) {
+ rc = 0;
+ for (ih = int2_intrhand[intnum]; ih != NULL;
+ ih = ih->ih_next) {
+ ret = (*ih->ih_fun)(ih->ih_arg);
+ if (ret != 0) {
+ rc = 1;
+ atomic_add_uint64(&ih->ih_count.ec_count,
+ 1);
+ }
+ if (ret == 1)
+ break;
+ }
+ if (rc == 0)
+ printf("spurious int2 mapped interrupt %d\n",
+ i);
+ }
+ }
+
+ return 1;
+}
+
+int
+int2_match(struct device *parent, void *match, void *aux)
+{
+ struct mainbus_attach_args *maa = (void *)aux;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ break;
+ default:
+ return 0;
+ }
+
+ return !strcmp(maa->maa_name, int_cd.cd_name);
+}
+
+void
+int2_attach(struct device *parent, struct device *self, void *aux)
+{
+ uint32_t address;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ address = INT2_IP20;
+ break;
+ default:
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ if (sys_config.system_subtype == IP22_INDIGO2)
+ address = INT2_IP22;
+ else
+ address = INT2_IP24;
+ break;
+ }
+
+ printf(" addr 0x%x\n", address);
+ int2_base = PHYS_TO_XKPHYS((uint64_t)address, CCA_NC);
+
+ /* Clean out interrupt masks */
+ int2_write(INT2_LOCAL0_MASK, 0);
+ int2_write(INT2_LOCAL1_MASK, 0);
+ int2_write(INT2_MAP_MASK0, 0);
+ int2_write(INT2_MAP_MASK1, 0);
+
+ /* Reset timer interrupts */
+ int2_write(INT2_TIMER_CONTROL,
+ TIMER_SEL0 | TIMER_16BIT | TIMER_SWSTROBE);
+ int2_write(INT2_TIMER_CONTROL,
+ TIMER_SEL1 | TIMER_16BIT | TIMER_SWSTROBE);
+ int2_write(INT2_TIMER_CONTROL,
+ TIMER_SEL2 | TIMER_16BIT | TIMER_SWSTROBE);
+ __asm__ __volatile__ ("sync" ::: "memory");
+ delay(4);
+ int2_write(INT2_TIMER_CLEAR, 0x03);
+
+ set_intr(INTPRI_L1, CR_INT_1, int2_l1intr);
+ set_intr(INTPRI_L0, CR_INT_0, int2_l0intr);
+ register_splx_handler(int2_splx);
+
+ if (sys_config.system_type != SGI_IP20) {
+ /* Wire interrupts 7, 11 to mappable interrupt 0,1 handlers */
+ int2_intr_establish(7, IPL_TTY, int2_mappable_intr,
+ (void *)0, NULL);
+ int2_intr_establish(8 + 3, IPL_TTY, int2_mappable_intr,
+ (void *)1, NULL);
+ }
+}
+
+/*
+ * Wait for the FIFO Full interrupt condition (Local 0 bit 0) to clear.
+ */
+void
+int2_wait_fifo(uint32_t flag)
+{
+ if (int2_base == 0)
+ delay(5000); /* XXX */
+ else
+ while (int2_read(INT2_LOCAL0_STATUS) & flag)
+ ;
+}
diff --git a/sys/arch/sgi/localbus/intreg.h b/sys/arch/sgi/localbus/intreg.h
new file mode 100644
index 00000000000..c6deca4e933
--- /dev/null
+++ b/sys/arch/sgi/localbus/intreg.h
@@ -0,0 +1,51 @@
+/* $OpenBSD: intreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: int2reg.h,v 1.5 2009/02/12 06:33:57 rumble Exp $ */
+
+/*
+ * Copyright (c) 2004 Christopher SEKIYA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* The INT has known locations on all SGI machines */
+#define INT2_IP20 0x1fb801c0
+#define INT2_IP22 0x1fbd9000
+#define INT2_IP24 0x1fbd9880
+
+/* The following registers are all 8 bit. */
+#define INT2_LOCAL0_STATUS 0x03
+#define INT2_LOCAL0_STATUS_FIFO 0x01
+#define INT2_LOCAL0_MASK 0x07
+#define INT2_LOCAL1_STATUS 0x0b
+#define INT2_LOCAL1_MASK 0x0f
+#define INT2_MAP_STATUS 0x13
+#define INT2_MAP_MASK0 0x17
+#define INT2_MAP_MASK1 0x1b
+#define INT2_MAP_POL 0x1f
+#define INT2_TIMER_CLEAR 0x23
+#define INT2_ERROR_STATUS 0x27
+#define INT2_TIMER_0 0x33
+#define INT2_TIMER_1 0x37
+#define INT2_TIMER_2 0x3b
+#define INT2_TIMER_CONTROL 0x3f
diff --git a/sys/arch/sgi/localbus/intvar.h b/sys/arch/sgi/localbus/intvar.h
new file mode 100644
index 00000000000..c8f6a133862
--- /dev/null
+++ b/sys/arch/sgi/localbus/intvar.h
@@ -0,0 +1,34 @@
+/* $OpenBSD: intvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: int2var.h,v 1.3 2008/08/23 17:25:54 tsutsui Exp $ */
+
+/*
+ * Copyright (c) 2004 Christopher SEKIYA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+void *int2_intr_establish(int, int, int (*)(void *),
+ void *, const char *);
+
+void int2_wait_fifo(uint32_t);