summaryrefslogtreecommitdiffstats
path: root/sys/arch/sgi/gio/gio.c
diff options
context:
space:
mode:
authormiod <miod@openbsd.org>2012-03-28 20:44:23 +0000
committermiod <miod@openbsd.org>2012-03-28 20:44:23 +0000
commit89e78ff68dbb60985d19f72ea5384db19f165e9c (patch)
treece4bbedba0e946ef806252ab74d9ca08167b58a3 /sys/arch/sgi/gio/gio.c
parentAllow dma map boundary smaller than the kernel page size to work in (diff)
downloadwireguard-openbsd-89e78ff68dbb60985d19f72ea5384db19f165e9c.tar.xz
wireguard-openbsd-89e78ff68dbb60985d19f72ea5384db19f165e9c.zip
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/gio/gio.c')
-rw-r--r--sys/arch/sgi/gio/gio.c518
1 files changed, 518 insertions, 0 deletions
diff --git a/sys/arch/sgi/gio/gio.c b/sys/arch/sgi/gio/gio.c
new file mode 100644
index 00000000000..b201adab5b9
--- /dev/null
+++ b/sys/arch/sgi/gio/gio.c
@@ -0,0 +1,518 @@
+/* $OpenBSD: gio.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */
+/* $NetBSD: gio.c,v 1.32 2011/07/01 18:53:46 dyoung Exp $ */
+
+/*
+ * Copyright (c) 2000 Soren S. Jorvang
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the
+ * NetBSD Project. See http://www.NetBSD.org/ for
+ * information about NetBSD.
+ * 4. 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/systm.h>
+#include <sys/device.h>
+
+#include <mips64/archtype.h>
+
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+
+#include <sgi/gio/gioreg.h>
+#include <sgi/gio/giovar.h>
+#include <sgi/gio/giodevs_data.h>
+
+#include <sgi/localbus/imcvar.h>
+#include <sgi/localbus/intvar.h>
+#include <sgi/sgi/ip22.h>
+
+int gio_match(struct device *, void *, void *);
+void gio_attach(struct device *, struct device *, void *);
+int gio_print(void *, const char *);
+int gio_print_fb(void *, const char *);
+int gio_search(struct device *, void *, void *);
+int gio_submatch(struct device *, void *, void *);
+uint32_t gio_id(paddr_t, int);
+
+struct gio_softc {
+ struct device sc_dev;
+
+ bus_space_tag_t sc_iot;
+ bus_dma_tag_t sc_dmat;
+};
+
+const struct cfattach gio_ca = {
+ sizeof(struct gio_softc), gio_match, gio_attach
+};
+
+struct cfdriver gio_cd = {
+ NULL, "gio", DV_DULL
+};
+
+struct gio_probe {
+ uint32_t slot;
+ uint64_t base;
+ uint32_t mach_type;
+ uint32_t mach_subtype;
+};
+
+/*
+ * Expansion Slot Base Addresses
+ *
+ * IP12, IP20 and IP24 have two GIO connectors: GIO_SLOT_EXP0 and
+ * GIO_SLOT_EXP1.
+ *
+ * On IP24 these slots exist on the graphics board or the IOPLUS
+ * "mezzanine" on Indy and Challenge S, respectively. The IOPLUS or
+ * graphics board connects to the mainboard via a single GIO64 connector.
+ *
+ * IP22 has either three or four physical connectors, but only two
+ * electrically distinct slots: GIO_SLOT_GFX and GIO_SLOT_EXP0.
+ *
+ * It should also be noted that DMA is (mostly) not supported in Challenge S's
+ * GIO_SLOT_EXP1. See gio(4) for the story.
+ */
+static const struct gio_probe slot_bases[] = {
+ { GIO_SLOT_GFX, 0x1f000000, SGI_IP22, IP22_INDIGO2 },
+
+ { GIO_SLOT_EXP0, 0x1f400000, SGI_IP20, -1 },
+ { GIO_SLOT_EXP0, 0x1f400000, SGI_IP22, -1 },
+
+ { GIO_SLOT_EXP1, 0x1f600000, SGI_IP20, -1 },
+ { GIO_SLOT_EXP1, 0x1f600000, SGI_IP22, IP22_INDY },
+
+ { 0, 0, 0, 0 }
+};
+
+/*
+ * Graphic Board Base Addresses
+ *
+ * Graphics boards are not treated like expansion slot cards. Their base
+ * addresses do not necessarily correspond to GIO slot addresses and they
+ * do not contain product identification words.
+ */
+static const struct gio_probe gfx_bases[] = {
+ /* grtwo, and newport on IP22 */
+ { -1, 0x1f000000, SGI_IP20, -1 },
+ { -1, 0x1f000000, SGI_IP22, -1 },
+
+ /* light */
+ { -1, 0x1f3f0000, SGI_IP20, -1 },
+
+ /* light (dual headed) */
+ { -1, 0x1f3f8000, SGI_IP20, -1 },
+
+ /* grtwo, and newport on IP22 */
+ { -1, 0x1f400000, SGI_IP20, -1 },
+ { -1, 0x1f400000, SGI_IP22, -1 },
+
+ /* grtwo */
+ { -1, 0x1f600000, SGI_IP20, -1 },
+ { -1, 0x1f600000, SGI_IP22, -1 },
+
+ /* newport */
+ { -1, 0x1f800000, SGI_IP22, IP22_INDIGO2 },
+
+ /* newport */
+ { -1, 0x1fc00000, SGI_IP22, IP22_INDIGO2 },
+
+ { 0, 0, 0, 0 }
+};
+
+/* maximum number of graphics boards possible (arbitrarily large estimate) */
+#define MAXGFX (nitems(gfx_bases) - 1)
+
+int
+gio_match(struct device *parent, void *match, void *aux)
+{
+ struct imc_attach_args *iaa = aux;
+
+ if (strcmp(iaa->iaa_name, gio_cd.cd_name) != 0)
+ return 0;
+
+ return 1;
+}
+
+void
+gio_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct gio_softc *sc = (struct gio_softc *)self;
+ struct imc_attach_args *iaa = (struct imc_attach_args *)aux;
+ struct gio_attach_args ga;
+ uint32_t gfx[MAXGFX];
+ uint i, j, ngfx;
+
+ printf("\n");
+
+ sc->sc_iot = iaa->iaa_st;
+ sc->sc_dmat = iaa->iaa_dmat;
+
+ ngfx = 0;
+ memset(gfx, 0, sizeof(gfx));
+
+ /*
+ * Try and attach graphics devices first.
+ * Unfortunately, they - not being GIO devices after all - do not
+ * contain a Product Identification Word, nor have a slot number.
+ *
+ * Record addresses to which graphics devices attach so that
+ * we do not confuse them with expansion slots, should the
+ * addresses coincide.
+ *
+ * Unfortunately graphics devices for which we have no configured
+ * driver, which address matches a regular slot number, will show
+ * up as rogue devices attached to real slots.
+ *
+ * If only the ARCBios component tree would be so kind as to give
+ * us the address of the frame buffer components...
+ */
+ for (i = 0; gfx_bases[i].base != 0; i++) {
+ /* skip slots that don't apply to us */
+ if (gfx_bases[i].mach_type != sys_config.system_type)
+ continue;
+
+ if (gfx_bases[i].mach_subtype != -1 &&
+ gfx_bases[i].mach_subtype != sys_config.system_subtype)
+ continue;
+
+ ga.ga_slot = -1;
+ ga.ga_addr = gfx_bases[i].base;
+ ga.ga_iot = sc->sc_iot;
+ ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC);
+ ga.ga_dmat = sc->sc_dmat;
+ ga.ga_product = -1;
+
+ if (gio_id(ga.ga_ioh, 1) == 0)
+ continue;
+
+ if (config_found_sm(self, &ga, gio_print_fb, gio_submatch)) {
+ gfx[ngfx++] = gfx_bases[i].base;
+ }
+ }
+
+ /*
+ * Now attach any GIO expansion cards.
+ *
+ * Be sure to skip any addresses to which a graphics device has
+ * already been attached.
+ */
+ for (i = 0; slot_bases[i].base != 0; i++) {
+ int skip = 0;
+
+ /* skip slots that don't apply to us */
+ if (slot_bases[i].mach_type != sys_config.system_type)
+ continue;
+
+ if (slot_bases[i].mach_subtype != -1 &&
+ slot_bases[i].mach_subtype != sys_config.system_subtype)
+ continue;
+
+ for (j = 0; j < ngfx; j++) {
+ if (slot_bases[i].base == gfx[j]) {
+ skip = 1;
+ break;
+ }
+ }
+ if (skip)
+ continue;
+
+ ga.ga_slot = slot_bases[i].slot;
+ ga.ga_addr = slot_bases[i].base;
+ ga.ga_iot = sc->sc_iot;
+ ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC);
+ ga.ga_dmat = sc->sc_dmat;
+
+ if (gio_id(ga.ga_ioh, 0) == 0)
+ continue;
+
+ ga.ga_product = bus_space_read_4(ga.ga_iot, ga.ga_ioh, 0);
+
+ config_found_sm(self, &ga, gio_print, gio_submatch);
+ }
+
+ config_search(gio_search, self, aux);
+}
+
+/*
+ * Try and figure out whether there is a device at the given slot address.
+ */
+uint32_t
+gio_id(paddr_t pa, int maybe_gfx)
+{
+ uint32_t id32;
+ uint16_t id16 = 0;
+ uint8_t id8 = 0;
+
+ if (guarded_read_4(pa, &id32) != 0)
+ return 0;
+
+ id16 = id32 ^ 0xffff;
+ (void)guarded_read_2(pa | 2, &id16);
+ id8 = id16 ^ 0xff;
+ (void)guarded_read_1(pa | 3, &id8);
+
+ /*
+ * If there is a real GIO device at this address (as opposed to
+ * a graphics card), then the low-order 8 bits of each read need
+ * to be consistent...
+ */
+ if (id8 == (id16 & 0xff) && id8 == (id32 & 0xff)) {
+ if (GIO_PRODUCT_32BIT_ID(id8)) {
+ if (id16 == (id32 & 0xffff))
+ return id32;
+ } else {
+ if (id8 != 0)
+ return id32;
+ }
+ }
+
+ if (maybe_gfx)
+ return 1;
+
+ return 0;
+}
+
+int
+gio_print(void *aux, const char *pnp)
+{
+ struct gio_attach_args *ga = aux;
+ const char *descr;
+ int product, revision;
+ uint i;
+
+ product = GIO_PRODUCT_PRODUCTID(ga->ga_product);
+ if (GIO_PRODUCT_32BIT_ID(ga->ga_product))
+ revision = GIO_PRODUCT_REVISION(ga->ga_product);
+ else
+ revision = 0;
+
+ descr = "unknown GIO card";
+ for (i = 0; gio_knowndevs[i].productid != 0; i++) {
+ if (gio_knowndevs[i].productid == product) {
+ descr = gio_knowndevs[i].product;
+ break;
+ }
+ }
+
+ if (pnp != NULL) {
+ printf("%s", descr);
+ if (ga->ga_product != -1)
+ printf(" (product 0x%02x revision 0x%02x)",
+ product, revision);
+ printf(" at %s", pnp);
+ }
+
+ if (ga->ga_slot != -1)
+ printf(" slot %d", ga->ga_slot);
+ printf(" addr 0x%lx", ga->ga_addr);
+
+ return UNCONF;
+}
+
+int
+gio_print_fb(void *aux, const char *pnp)
+{
+#if 0 /* once we can know for sure there really is a frame buffer here */
+ if (pnp != NULL)
+ printf("framebuffer at %s", pnp);
+
+ if (ga->ga_addr != (uint64_t)-1)
+ printf(" addr 0x%lx", ga->ga_addr);
+
+ return UNCONF;
+#else
+ return QUIET;
+#endif
+}
+
+int
+gio_search(struct device *parent, void *vcf, void *aux)
+{
+ struct gio_softc *sc = (struct gio_softc *)parent;
+ struct cfdata *cf = (struct cfdata *)vcf;
+ struct gio_attach_args ga;
+
+ /* Handled by direct configuration, so skip here */
+ if (cf->cf_loc[1 /*GIOCF_ADDR*/] == -1)
+ return 0;
+
+ ga.ga_product = -1;
+ ga.ga_slot = cf->cf_loc[0 /*GIOCF_SLOT*/];
+ ga.ga_addr = (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/];
+ ga.ga_iot = sc->sc_iot;
+ ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC);
+ ga.ga_dmat = sc->sc_dmat;
+
+ if ((*cf->cf_attach->ca_match)(parent, cf, &ga) == 0)
+ return 0;
+
+ config_attach(parent, cf, &ga, gio_print);
+
+ return 1;
+}
+
+int
+gio_submatch(struct device *parent, void *vcf, void *aux)
+{
+ struct cfdata *cf = (struct cfdata *)vcf;
+ struct gio_attach_args *ga = (struct gio_attach_args *)aux;
+
+ if (cf->cf_loc[0 /*GIOCF_SLOT*/] != -1 &&
+ cf->cf_loc[0 /*GIOCF_SLOT*/] != ga->ga_slot)
+ return 0;
+
+ if (cf->cf_loc[1 /*GIOCF_ADDR*/] != -1 &&
+ (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/] != ga->ga_addr)
+ return 0;
+
+ return (*cf->cf_attach->ca_match)(parent, cf, aux);
+}
+
+#if 0 /* XXX involve wscons_machdep somehow */
+int
+gio_cnattach(void)
+{
+ extern struct machine_bus_dma_tag imc_bus_dma_tag; /* XXX */
+ extern bus_space_t imcbus_tag; /* XXX */
+ struct gio_attach_args ga;
+ uint32_t dummy;
+ int i;
+
+ for (i = 0; gfx_bases[i].base != 0; i++) {
+ /* skip bases that don't apply to us */
+ if (gfx_bases[i].mach_type != sys_config.system_type)
+ continue;
+
+ if (gfx_bases[i].mach_subtype != -1 &&
+ gfx_bases[i].mach_subtype != sys_config.system_subtype)
+ continue;
+
+ ga.ga_slot = -1;
+ ga.ga_addr = gfx_bases[i].base;
+ ga.ga_iot = &imcbus_tag; /* XXX */
+ ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC);
+ ga.ga_dmat = &imc_bus_dma_tag; /* XXX */
+ ga.ga_product = -1;
+
+ if (gio_id(ga.ga_ioh, 1) == 0)
+ continue;
+
+#if NGRTWO > 0
+ if (grtwo_cnattach(&ga) == 0)
+ return 0;
+#endif
+
+#if NLIGHT > 0
+ if (light_cnattach(&ga) == 0)
+ return 0;
+#endif
+
+#if NNEWPORT > 0
+ if (newport_cnattach(&ga) == 0)
+ return 0;
+#endif
+
+ }
+
+ return ENXIO;
+}
+#endif
+
+/*
+ * Devices living in the expansion slots must enable or disable some
+ * GIO arbiter settings. This is accomplished via imc(4) registers.
+ */
+int
+gio_arb_config(int slot, uint32_t flags)
+{
+ if (flags == 0)
+ return (EINVAL);
+
+ if (flags & ~(GIO_ARB_RT | GIO_ARB_LB | GIO_ARB_MST | GIO_ARB_SLV |
+ GIO_ARB_PIPE | GIO_ARB_NOPIPE | GIO_ARB_32BIT | GIO_ARB_64BIT |
+ GIO_ARB_HPC2_32BIT | GIO_ARB_HPC2_64BIT))
+ return (EINVAL);
+
+ if (((flags & GIO_ARB_RT) && (flags & GIO_ARB_LB)) ||
+ ((flags & GIO_ARB_MST) && (flags & GIO_ARB_SLV)) ||
+ ((flags & GIO_ARB_PIPE) && (flags & GIO_ARB_NOPIPE)) ||
+ ((flags & GIO_ARB_32BIT) && (flags & GIO_ARB_64BIT)) ||
+ ((flags & GIO_ARB_HPC2_32BIT) && (flags & GIO_ARB_HPC2_64BIT)))
+ return (EINVAL);
+
+ return (imc_gio64_arb_config(slot, flags));
+}
+
+/*
+ * Establish an interrupt handler for the specified slot.
+ *
+ * Indy and Challenge S have an interrupt per GIO slot. Indigo and Indigo2
+ * share a single interrupt, however.
+ */
+void *
+gio_intr_establish(int slot, int level, int (*func)(void *), void *arg,
+ const char *what)
+{
+ int intr;
+
+ switch (sys_config.system_type) {
+ case SGI_IP20:
+ if (slot == GIO_SLOT_GFX)
+ return NULL;
+ intr = 6;
+ break;
+ case SGI_IP22:
+ case SGI_IP26:
+ case SGI_IP28:
+ if (sys_config.system_subtype == IP22_INDIGO2) {
+ if (slot == GIO_SLOT_EXP1)
+ return NULL;
+ intr = 6;
+ } else {
+ if (slot == GIO_SLOT_GFX)
+ return NULL;
+ intr = (slot == GIO_SLOT_EXP0) ? 22 : 23;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ return int2_intr_establish(intr, level, func, arg, what);
+}
+
+const char *
+gio_product_string(int prid)
+{
+ int i;
+
+ for (i = 0; gio_knowndevs[i].product != NULL; i++)
+ if (gio_knowndevs[i].productid == prid)
+ return (gio_knowndevs[i].product);
+
+ return (NULL);
+}