summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/sgi/xbow/Makefile7
-rw-r--r--sys/arch/sgi/xbow/devlist2h.awk168
-rw-r--r--sys/arch/sgi/xbow/files.xbow16
-rw-r--r--sys/arch/sgi/xbow/xbow.c514
-rw-r--r--sys/arch/sgi/xbow/xbow.h125
-rw-r--r--sys/arch/sgi/xbow/xbowdevs42
-rw-r--r--sys/arch/sgi/xbow/xbridge.c631
-rw-r--r--sys/arch/sgi/xbow/xbridgereg.h95
-rw-r--r--sys/arch/sgi/xbow/xheart.c458
-rw-r--r--sys/arch/sgi/xbow/xheartreg.h46
-rw-r--r--sys/arch/sgi/xbow/xhub.c72
11 files changed, 2174 insertions, 0 deletions
diff --git a/sys/arch/sgi/xbow/Makefile b/sys/arch/sgi/xbow/Makefile
new file mode 100644
index 00000000000..b0a96ac8d49
--- /dev/null
+++ b/sys/arch/sgi/xbow/Makefile
@@ -0,0 +1,7 @@
+# $OpenBSD: Makefile,v 1.1 2008/04/07 22:47:40 miod Exp $
+
+AWK= awk
+
+xbowdevs.h xbowdevs_data.h: xbowdevs devlist2h.awk
+ /bin/rm -f xbowdevs.h xbowdevs_data.h
+ ${AWK} -f devlist2h.awk xbowdevs
diff --git a/sys/arch/sgi/xbow/devlist2h.awk b/sys/arch/sgi/xbow/devlist2h.awk
new file mode 100644
index 00000000000..3bea098490d
--- /dev/null
+++ b/sys/arch/sgi/xbow/devlist2h.awk
@@ -0,0 +1,168 @@
+#! /usr/bin/awk -f
+# $OpenBSD: devlist2h.awk,v 1.1 2008/04/07 22:47:40 miod Exp $
+# $NetBSD: devlist2h.awk,v 1.2 1996/01/22 21:08:09 cgd Exp $
+#
+# Copyright (c) 1995, 1996 Christopher G. Demetriou
+# 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 by Christopher G. Demetriou.
+# 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.
+#
+BEGIN {
+ nproducts = nvendor_dup = nvendors = 0
+ dfile="xbowdevs_data.h"
+ hfile="xbowdevs.h"
+}
+NR == 1 {
+ VERSION = $0
+ gsub("\\$", "", VERSION)
+
+ printf("/*\n") > dfile
+ printf(" * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \
+ > dfile
+ printf(" *\n") > dfile
+ printf(" * generated from:\n") > dfile
+ printf(" *\t%s\n", VERSION) > dfile
+ printf(" */\n\n") > dfile
+
+ printf("/*\n") > hfile
+ printf(" * THIS FILE AUTOMATICALLY GENERATED. DO NOT EDIT.\n") \
+ > hfile
+ printf(" *\n") > hfile
+ printf(" * generated from:\n") > hfile
+ printf(" *\t%s\n", VERSION) > hfile
+ printf(" */\n") > hfile
+
+ next
+}
+$1 == "vendor" {
+ nvendors++
+
+ if ($2 in vendorindex) {
+ printf("duplicate vendor name %s\n", $2);
+ nvendor_dup++;
+ }
+
+ vendorindex[$2] = nvendors; # record index for this name, for later.
+ vendors[nvendors, 1] = $2; # name
+ vendors[nvendors, 2] = $3; # id
+ printf("#define\tPCI_VENDOR_%s\t%s\n", vendors[nvendors, 1],
+ vendors[nvendors, 2]) > hfile
+
+ next
+}
+$1 == "product" {
+ nproducts++
+
+ products[nproducts, 1] = $2; # vendor name
+ products[nproducts, 2] = $3; # product id
+ products[nproducts, 3] = $4; # id
+ printf("#define\tPCI_PRODUCT_%s_%s\t%s\t", products[nproducts, 1],
+ products[nproducts, 2], products[nproducts, 3]) > hfile
+
+ i=4; f = 5;
+
+ # comments
+ ocomment = oparen = 0
+ if (f <= NF) {
+ printf("\t/* ") > hfile
+ ocomment = 1;
+ }
+ while (f <= NF) {
+ if ($f == "#") {
+ printf("(") > hfile
+ oparen = 1
+ f++
+ continue
+ }
+ if (oparen) {
+ printf("%s", $f) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ f++
+ continue
+ }
+ products[nproducts, i] = $f
+ printf("%s", products[nproducts, i]) > hfile
+ if (f < NF)
+ printf(" ") > hfile
+ i++; f++;
+ }
+ if (oparen)
+ printf(")") > hfile
+ if (ocomment)
+ printf(" */") > hfile
+ printf("\n") > hfile
+
+ next
+}
+{
+ if ($0 == "")
+ blanklines++
+ print $0 > hfile
+ if (blanklines < 2)
+ print $0 > dfile
+}
+END {
+ # print out the match tables
+
+ printf("\n") > dfile
+
+ if (nvendor_dup > 0)
+ exit(1);
+
+ printf("/* Descriptions of known devices. */\n") > dfile
+
+ printf("struct xbow_product {\n") > dfile
+ printf("\tuint32_t vendor;\n") > dfile
+ printf("\tuint32_t product;\n") > dfile
+ printf("\tconst char *productname;\n") > dfile
+ printf("};\n\n") > dfile
+
+
+ printf("static const struct xbow_product xbow_products[] = {\n") \
+ > dfile
+ for (i = 1; i <= nproducts; i++) {
+ printf("\t{\n") > dfile
+ printf("\t PCI_VENDOR_%s, PCI_PRODUCT_%s_%s,\n",
+ products[i, 1], products[i, 1], products[i, 2]) \
+ > dfile
+
+ printf("\t \"") > dfile
+ j = 4;
+ needspace = 0;
+ while (products[i, j] != "") {
+ if (needspace)
+ printf(" ") > dfile
+ printf("%s", products[i, j]) > dfile
+ needspace = 1
+ j++
+ }
+ printf("\",\n") > dfile
+ printf("\t},\n") > dfile
+ }
+ printf("\t{ 0, 0, NULL, }\n") > dfile
+ printf("};\n\n") > dfile
+}
diff --git a/sys/arch/sgi/xbow/files.xbow b/sys/arch/sgi/xbow/files.xbow
new file mode 100644
index 00000000000..e350ddd747b
--- /dev/null
+++ b/sys/arch/sgi/xbow/files.xbow
@@ -0,0 +1,16 @@
+# $OpenBSD: files.xbow,v 1.1 2008/04/07 22:47:40 miod Exp $
+
+# IP30 Heart
+device xheart {} : onewirebus
+attach xheart at xbow
+file arch/sgi/xbow/xheart.c xheart
+
+# IP27 Hub
+device xhub {}
+attach xhub at xbow
+file arch/sgi/xbow/xhub.c xhub
+
+# PCI Bridge
+device xbridge {} : pcibus
+attach xbridge at xbow
+file arch/sgi/xbow/xbridge.c xbridge
diff --git a/sys/arch/sgi/xbow/xbow.c b/sys/arch/sgi/xbow/xbow.c
new file mode 100644
index 00000000000..c9dc0d1b4ae
--- /dev/null
+++ b/sys/arch/sgi/xbow/xbow.c
@@ -0,0 +1,514 @@
+/* $OpenBSD: xbow.c,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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) 2004 Opsycon AB (www.opsycon.se)
+ *
+ * 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.
+ *
+ * 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.
+ *
+ */
+
+/*
+ * XBOW is the mux between two nodes and XIO.
+ *
+ * A Crossbow (XBOW) connects two nodeboards via their respecive
+ * HUB to up to six different I/O controllers in XIO slots. In a
+ * multiprocessor system all processors have access to the XIO
+ * slots but may need to pass traffic via the routers.
+ *
+ * To each XIO port on the XBOW a XIO interface is attached. Such
+ * interfaces can be for example PCI bridges wich then add another
+ * level to the hierarchy.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+
+#include <mips64/archtype.h>
+
+#include <machine/atomic.h>
+#include <machine/autoconf.h>
+#include <machine/intr.h>
+
+#include <sgi/xbow/xbow.h>
+#include <sgi/xbow/xbowdevs.h>
+#include <sgi/xbow/xbowdevs_data.h>
+
+int xbowmatch(struct device *, void *, void *);
+void xbowattach(struct device *, struct device *, void *);
+int xbowprint_pass1(void *, const char *);
+int xbowprint_pass2(void *, const char *);
+int xbowsubmatch_pass1(struct device *, void *, void *);
+int xbowsubmatch_pass2(struct device *, void *, void *);
+void xbow_enumerate(struct device *, int,
+ int (*)(struct device *, void *, void *), cfprint_t);
+
+uint32_t xbow_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint64_t xbow_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void xbow_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint32_t);
+void xbow_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint64_t);
+int xbow_space_map_short(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+int xbow_space_map_long(bus_space_tag_t, bus_addr_t, bus_size_t, int,
+ bus_space_handle_t *);
+void xbow_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+int xbow_space_region_short(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+int xbow_space_region_long(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ bus_size_t, bus_space_handle_t *);
+void *xbow_space_vaddr(bus_space_tag_t, bus_space_handle_t);
+
+const struct xbow_product *xbow_identify(uint32_t, uint32_t);
+
+const struct cfattach xbow_ca = {
+ sizeof(struct device), xbowmatch, xbowattach
+};
+
+struct cfdriver xbow_cd = {
+ NULL, "xbow", DV_DULL
+};
+
+static const bus_space_t xbowbus_short_tag = {
+ NULL,
+ (bus_addr_t)0, /* will be modified in widgets bus_space_t */
+ (bus_addr_t)0,
+ 0,
+ xbow_read_1,
+ xbow_write_1,
+ xbow_read_2,
+ xbow_write_2,
+ xbow_read_4,
+ xbow_write_4,
+ xbow_read_8,
+ xbow_write_8,
+ xbow_space_map_short,
+ xbow_space_unmap,
+ xbow_space_region_short
+
+};
+
+static const bus_space_t xbowbus_long_tag = {
+ NULL,
+ (bus_addr_t)0, /* will be modified in widgets bus_space_t */
+ (bus_addr_t)0,
+ 0,
+ xbow_read_1,
+ xbow_write_1,
+ xbow_read_2,
+ xbow_write_2,
+ xbow_read_4,
+ xbow_write_4,
+ xbow_read_8,
+ xbow_write_8,
+ xbow_space_map_long,
+ xbow_space_unmap,
+ xbow_space_region_long
+
+};
+
+/*
+ * Function pointers to hide widget window mapping differences accross
+ * systems.
+ */
+paddr_t (*xbow_widget_short)(int16_t, u_int);
+paddr_t (*xbow_widget_long)(int16_t, u_int);
+unsigned int xbow_long_shift = 29;
+
+int (*xbow_widget_id)(int16_t, u_int, uint32_t *);
+
+/*
+ * Attachment glue.
+ */
+
+int
+xbowmatch(struct device *parent, void *match, void *aux)
+{
+ switch (sys_config.system_type) {
+ case SGI_O200:
+ case SGI_OCTANE:
+ return (1);
+ default:
+ return (0);
+ }
+}
+
+const struct xbow_product *
+xbow_identify(uint32_t vendor, uint32_t product)
+{
+ const struct xbow_product *p;
+
+ for (p = xbow_products; p->productname != NULL; p++)
+ if (p->vendor == vendor && p->product == product)
+ return p;
+
+ return NULL;
+}
+
+int
+xbowprint_pass1(void *aux, const char *pnp)
+{
+ struct xbow_attach_args *xaa = aux;
+ const struct xbow_product *p;
+
+ p = xbow_identify(xaa->xaa_vendor, xaa->xaa_product);
+
+ if (pnp == NULL) {
+ printf(" widget %d", xaa->xaa_widget);
+ if (p != NULL)
+ printf(": %s", p->productname);
+ }
+
+ return (QUIET);
+}
+
+int
+xbowprint_pass2(void *aux, const char *pnp)
+{
+ struct xbow_attach_args *xaa = aux;
+ const struct xbow_product *p;
+
+ p = xbow_identify(xaa->xaa_vendor, xaa->xaa_product);
+
+ if (pnp != NULL) {
+ if (p != NULL)
+ printf("\"%s\"", p->productname);
+ else
+ printf("vendor %x product %x",
+ xaa->xaa_vendor, xaa->xaa_product);
+ printf(" revision %d at %s",
+ xaa->xaa_revision, pnp);
+ }
+ printf(" widget %d", xaa->xaa_widget);
+ if (pnp == NULL) {
+ if (p != NULL)
+ printf(": %s", p->productname);
+ }
+
+ return (UNCONF);
+}
+
+int
+xbowsubmatch_pass1(struct device *parent, void *vcf, void *aux)
+{
+ int rc;
+
+ if (xbow_intr_widget != 0)
+ return 0;
+
+ rc = xbowsubmatch_pass2(parent, vcf, aux);
+ if (rc < 20)
+ rc = 0;
+
+ return rc;
+}
+
+int
+xbowsubmatch_pass2(struct device *parent, void *vcf, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+ struct cfdata *cf = vcf;
+
+ if (cf->cf_loc[0] != -1 && cf->cf_loc[0] != xaa->xaa_vendor)
+ return 0;
+ if (cf->cf_loc[1] != -1 && cf->cf_loc[1] != xaa->xaa_product)
+ return 0;
+
+ return (*cf->cf_attach->ca_match)(parent, vcf, aux);
+}
+
+void
+xbowattach(struct device *parent, struct device *self, void *aux)
+{
+ int16_t nasid = 0; /* XXX for now... */
+ uint32_t wid, vendor, product;
+ const struct xbow_product *p;
+
+ /*
+ * This assumes widget 0 is the XBow itself (or an XXBow).
+ * If it isn't - feel free to haunt my bedroom at night.
+ */
+ if (xbow_widget_id(nasid, 0, &wid) != 0)
+ panic("no xbow");
+ vendor = (wid & WIDGET_ID_VENDOR_MASK) >> WIDGET_ID_VENDOR_SHIFT;
+ product = (wid & WIDGET_ID_PRODUCT_MASK) >> WIDGET_ID_PRODUCT_SHIFT;
+ p = xbow_identify(vendor, product);
+ printf(": %s revision %d\n",
+ p != NULL ? p->productname : "unknown xbow",
+ (wid & WIDGET_ID_REV_MASK) >> WIDGET_ID_REV_SHIFT);
+
+ /*
+ * Enumerate the other widgets.
+ * We'll do two passes - one to give the first Heart or a Hub a
+ * chance to setup interrupt routing, and one to attach all other
+ * widgets.
+ */
+ xbow_enumerate(self, 0, xbowsubmatch_pass1, xbowprint_pass1);
+ xbow_enumerate(self, xbow_intr_widget, xbowsubmatch_pass2,
+ xbowprint_pass2);
+}
+
+void
+xbow_enumerate(struct device *self, int skip,
+ int (*sm)(struct device *, void *, void *), cfprint_t print)
+{
+ int16_t nasid = 0; /* XXX for now... */
+ struct xbow_attach_args xaa;
+ int widget;
+ uint32_t wid;
+
+ for (widget = 8; widget <= 15; widget++) {
+ struct mips_bus_space *bs, *bl;
+
+ if (widget == skip)
+ continue;
+
+ if (xbow_widget_id(nasid, widget, &wid) != 0)
+ continue;
+
+ /*
+ * Build a pair of bus_space_t suitable for this widget.
+ */
+ bs = malloc(sizeof (*bs), M_DEVBUF, M_NOWAIT);
+ if (bs == NULL)
+ continue;
+ bl = malloc(sizeof (*bl), M_DEVBUF, M_NOWAIT);
+ if (bl == NULL) {
+ free(bs, M_DEVBUF);
+ continue;
+ }
+
+ xbow_build_bus_space(bs, nasid, widget, 0);
+ xbow_build_bus_space(bl, nasid, widget, 1);
+
+ xaa.xaa_widget = widget;
+ xaa.xaa_vendor = (wid & WIDGET_ID_VENDOR_MASK) >>
+ WIDGET_ID_VENDOR_SHIFT;
+ xaa.xaa_product = (wid & WIDGET_ID_PRODUCT_MASK) >>
+ WIDGET_ID_PRODUCT_SHIFT;
+ xaa.xaa_revision = (wid & WIDGET_ID_REV_MASK) >>
+ WIDGET_ID_REV_SHIFT;
+ xaa.xaa_short_tag = bs;
+ xaa.xaa_long_tag = bl;
+
+ if (config_found_sm(self, &xaa, print, sm) == NULL) {
+ /* nothing attached, no need to keep the bus_space */
+ free(bs, M_DEVBUF);
+ free(bl, M_DEVBUF);
+ }
+ }
+}
+
+
+/*
+ * Bus access primitives.
+ */
+
+void
+xbow_build_bus_space(struct mips_bus_space *bs, int nasid, int widget, int lwin)
+{
+ if (lwin) {
+ bcopy(&xbowbus_long_tag, bs, sizeof (*bs));
+ bs->bus_base = (*xbow_widget_long)(nasid, widget);
+ } else {
+ bcopy(&xbowbus_short_tag, bs, sizeof (*bs));
+ bs->bus_base = (*xbow_widget_short)(nasid, widget);
+ }
+}
+
+uint8_t
+xbow_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint8_t *)(h + o);
+}
+
+uint16_t
+xbow_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint16_t *)(h + o);
+}
+
+uint32_t
+xbow_read_4(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint32_t *)(h + o);
+}
+
+uint64_t
+xbow_read_8(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint64_t *)(h + o);
+}
+
+void
+xbow_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
+xbow_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
+xbow_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
+xbow_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;
+}
+
+int
+xbow_space_map_short(bus_space_tag_t t, bus_addr_t offs, bus_size_t size,
+ int cacheable, bus_space_handle_t *bshp)
+{
+ bus_addr_t bpa;
+
+ bpa = t->bus_base + offs;
+
+ /* check that this does not overflow the window */
+ if (((bpa + size - 1) >> 24) != (t->bus_base >> 24))
+ return (EINVAL);
+
+ *bshp = bpa;
+ return 0;
+}
+
+int
+xbow_space_map_long(bus_space_tag_t t, bus_addr_t offs, bus_size_t size,
+ int cacheable, bus_space_handle_t *bshp)
+{
+ bus_addr_t bpa;
+
+ bpa = t->bus_base + offs;
+
+ /* check that this does not overflow the window */
+ if (((bpa + size - 1) >> xbow_long_shift) !=
+ (t->bus_base >> xbow_long_shift))
+ return (EINVAL);
+
+ *bshp = bpa;
+ return 0;
+}
+
+void
+xbow_space_unmap(bus_space_tag_t t, bus_space_handle_t bsh, bus_size_t size)
+{
+}
+
+int
+xbow_space_region_short(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
+{
+ /* check that this does not overflow the window */
+ if (((bsh + offset) >> 24) != (bsh >> 24))
+ return (EINVAL);
+
+ *nbshp = bsh + offset;
+ return 0;
+}
+
+int
+xbow_space_region_long(bus_space_tag_t t, bus_space_handle_t bsh,
+ bus_size_t offset, bus_size_t size, bus_space_handle_t *nbshp)
+{
+ /* check that this does not overflow the window */
+ if (((bsh + offset) >> xbow_long_shift) != (bsh >> xbow_long_shift))
+ return (EINVAL);
+
+ *nbshp = bsh + offset;
+ return 0;
+}
+
+void *
+xbow_space_vaddr(bus_space_tag_t t, bus_space_handle_t h)
+{
+ return (void *)h;
+}
+
+/*
+ * Interrupt handling code.
+ *
+ * Interrupt handling should be done at the Heart/Hub driver level, we only
+ * act as a proxy here.
+ */
+
+int xbow_intr_widget = 0;
+unsigned int xbow_intr_widget_register = 0;
+int (*xbow_intr_widget_intr_register)(int, int, int *) = NULL;
+int (*xbow_intr_widget_intr_establish)(int (*)(void *), void *, int, int,
+ const char *) = NULL;
+void (*xbow_intr_widget_intr_disestablish)(int) = NULL;
+
+int
+xbow_intr_register(int widget, int level, int *intrbit)
+{
+ if (xbow_intr_widget_intr_register == NULL)
+ return EINVAL;
+
+ return (*xbow_intr_widget_intr_register)(widget, level, intrbit);
+}
+
+int
+xbow_intr_establish(int (*func)(void *), void *arg, int intrbit, int level,
+ const char *name)
+{
+ if (xbow_intr_widget_intr_establish == NULL)
+ return EINVAL;
+
+ return (*xbow_intr_widget_intr_establish)(func, arg, intrbit, level,
+ name);
+}
+
+void
+xbow_intr_disestablish(int intrbit)
+{
+ if (xbow_intr_widget_intr_disestablish == NULL)
+ return;
+
+ (*xbow_intr_widget_intr_disestablish)(intrbit);
+}
diff --git a/sys/arch/sgi/xbow/xbow.h b/sys/arch/sgi/xbow/xbow.h
new file mode 100644
index 00000000000..1a5bed14da6
--- /dev/null
+++ b/sys/arch/sgi/xbow/xbow.h
@@ -0,0 +1,125 @@
+/* $OpenBSD: xbow.h,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+#ifndef _XBOW_H_
+#define _XBOW_H_
+
+/*
+ * Devices connected to the XBow are called ``widgets'' and are
+ * identified by a common widget memory area at the beginning of their
+ * memory space.
+ *
+ * Each widget has its own memory space, upon which windows of different
+ * sizes can be defined. OpenBSD only cares about the shortest window
+ * (which fits in 32 bits of address space), and the longest window (which
+ * provides access to the complete widget space).
+ *
+ * Apart from the crossbow itself being widget #0, the widgets are divided
+ * in two groups: widgets #8 to #b are the ``upper'' widgets, while widgets
+ * #c to #f are the ``lower'' widgets.
+ *
+ * Widgets are uniquely identified with their widget number on the XBow
+ * bus. However, the way they are mapped and accessed will depend on the
+ * processor (well, the processor board node) requesting access. Hence the
+ * two parameters needed to map a widget.
+ */
+
+extern paddr_t (*xbow_widget_short)(int16_t, u_int);
+extern paddr_t (*xbow_widget_long)(int16_t, u_int);
+extern unsigned int xbow_long_shift;
+
+extern int (*xbow_widget_id)(int16_t, u_int, uint32_t *);
+extern int xbow_intr_widget;
+extern unsigned int xbow_intr_widget_register;
+
+extern int (*xbow_intr_widget_intr_register)(int, int, int *);
+extern int (*xbow_intr_widget_intr_establish)(int (*)(void *), void *,
+ int, int, const char *);
+extern void (*xbow_intr_widget_intr_disestablish)(int);
+
+/*
+ * Common Widget Registers. Every widget provides them.
+ */
+
+/* all registers are 32 bits within big-endian 64 bit blocks */
+#define WIDGET_ID 0x0004
+#define WIDGET_ID_REV_MASK 0xf0000000
+#define WIDGET_ID_REV_SHIFT 28
+#define WIDGET_ID_PRODUCT_MASK 0x0ffff000
+#define WIDGET_ID_PRODUCT_SHIFT 12
+#define WIDGET_ID_VENDOR_MASK 0x00000ffe
+#define WIDGET_ID_VENDOR_SHIFT 1
+#define WIDGET_STATUS 0x000c
+#define WIDGET_ERR_ADDR_UPPER 0x0014
+#define WIDGET_ERR_ADDR_LOWER 0x001c
+#define WIDGET_CONTROL 0x0024
+#define WIDGET_REQ_TIMEOUT 0x002c
+#define WIDGET_INTDEST_ADDR_UPPER 0x0034
+#define WIDGET_INTDEST_ADDR_LOWER 0x003c
+#define WIDGET_ERR_CMD_WORD 0x0044
+#define WIDGET_LLP_CFG 0x004c
+#define WIDGET_TFLUSH 0x0054
+
+/*
+ * Crossbow Specific Registers.
+ */
+
+#define XBOW_WID_ARB_RELOAD 0x005c
+#define XBOW_PERFCNTR_A 0x0064
+#define XBOW_PERFCNTR_B 0x006c
+#define XBOW_NIC 0x0074
+#define XBOW_WIDGET_LINK(w) (0x0100 + ((w) & 7) * 0x0040)
+
+/*
+ * Per-widget ``Link'' Register Set.
+ */
+#define WIDGET_LINK_IBF 0x0004
+#define WIDGET_LINK_CONTROL 0x000c
+#define WIDGET_CONTROL_ALIVE 0x80000000
+#define WIDGET_LINK_STATUS 0x0014
+#define WIDGET_STATUS_ALIVE 0x80000000
+#define WIDGET_LINK_ARB_UPPER 0x001c
+#define WIDGET_LINK_ARB_LOWER 0x0024
+#define WIDGET_LINK_STATUS_CLEAR 0x002c
+#define WIDGET_LINK_RESET 0x0034
+#define WIDGET_LINK_AUX_STATUS 0x003c
+
+
+struct xbow_attach_args {
+ int xaa_widget;
+
+ uint32_t xaa_vendor;
+ uint32_t xaa_product;
+ uint32_t xaa_revision;
+
+ bus_space_tag_t xaa_short_tag;
+ bus_space_tag_t xaa_long_tag;
+};
+
+void xbow_build_bus_space(struct mips_bus_space *, int, int, int);
+int xbow_intr_register(int, int, int *);
+int xbow_intr_establish(int (*)(void *), void *, int, int, const char *);
+void xbow_intr_disestablish(int);
+
+uint8_t xbow_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint16_t xbow_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void xbow_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t);
+void xbow_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint16_t);
+
+#endif /* _XBOW_H_ */
diff --git a/sys/arch/sgi/xbow/xbowdevs b/sys/arch/sgi/xbow/xbowdevs
new file mode 100644
index 00000000000..ee2e12495d6
--- /dev/null
+++ b/sys/arch/sgi/xbow/xbowdevs
@@ -0,0 +1,42 @@
+$OpenBSD: xbowdevs,v 1.1 2008/04/07 22:47:40 miod Exp $
+/*
+ * Copyright (c) 2008 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.
+ */
+
+vendor SGI 0x0000
+vendor SGI2 0x0023
+vendor SGI3 0x0024
+vendor SGI4 0x0036
+vendor SGI5 0x02aa
+
+/*
+ * List of known products. Grouped by ``manufacturer''.
+ */
+
+product SGI XBOW 0x0000 XBow
+product SGI XXBOW 0xd000 XXBow
+
+product SGI2 ODYSSEY 0xc013 Odyssey
+
+product SGI3 TPU 0xc202 TPU
+product SGI3 XBRIDGE 0xd002 XBridge
+
+product SGI4 HEART 0xc001 Heart
+product SGI4 BRIDGE 0xc002 Bridge
+product SGI4 HUB 0xc101 Hub
+product SGI4 BEDROCK 0xc110 Bedrock
+
+product SGI5 IMPACT 0xc003 ImpactSR
+product SGI5 KONA 0xc102 Kona
diff --git a/sys/arch/sgi/xbow/xbridge.c b/sys/arch/sgi/xbow/xbridge.c
new file mode 100644
index 00000000000..fc3198986b1
--- /dev/null
+++ b/sys/arch/sgi/xbow/xbridge.c
@@ -0,0 +1,631 @@
+/* $OpenBSD: xbridge.c,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * XBow Bridge Widget driver.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+
+#include <machine/atomic.h>
+#include <machine/autoconf.h>
+#include <machine/bus.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+
+#include <mips64/archtype.h>
+#include <sgi/xbow/xbow.h>
+#include <sgi/xbow/xbowdevs.h>
+
+#include <sgi/xbow/xbridgereg.h>
+
+#include <sgi/sgi/ip30.h>
+
+int xbridge_match(struct device *, void *, void *);
+void xbridge_attach(struct device *, struct device *, void *);
+int xbridge_print(void *, const char *);
+
+struct xbridge_intr;
+
+struct xbridge_softc {
+ struct device sc_dev;
+ int sc_rev;
+ int sc_widget;
+ struct mips_pci_chipset sc_pc;
+
+ struct mips_bus_space *sc_mem_bus_space;
+ struct mips_bus_space *sc_io_bus_space;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_regh;
+
+ int sc_intrbit[BRIDGE_NINTRS];
+ struct xbridge_intr *sc_intr[BRIDGE_NINTRS];
+};
+
+const struct cfattach xbridge_ca = {
+ sizeof(struct xbridge_softc), xbridge_match, xbridge_attach,
+};
+
+struct cfdriver xbridge_cd = {
+ NULL, "xbridge", DV_DULL,
+};
+
+void xbridge_attach_hook(struct device *, struct device *,
+ struct pcibus_attach_args *);
+pcitag_t xbridge_make_tag(void *, int, int, int);
+void xbridge_decompose_tag(void *, pcitag_t, int *, int *, int *);
+int xbridge_bus_maxdevs(void *, int);
+pcireg_t xbridge_conf_read(void *, pcitag_t, int);
+void xbridge_conf_write(void *, pcitag_t, int, pcireg_t);
+
+int xbridge_intr_map(struct pci_attach_args *, pci_intr_handle_t *);
+const char *xbridge_intr_string(void *, pci_intr_handle_t);
+void *xbridge_intr_establish(void *, pci_intr_handle_t, int,
+ int (*func)(void *), void *, char *);
+void xbridge_intr_disestablish(void *, void *);
+int xbridge_intr_handler(void *);
+
+uint8_t xbridge_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+uint16_t xbridge_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t);
+void xbridge_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint8_t);
+void xbridge_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t,
+ uint16_t);
+
+bus_addr_t xbridge_pa_to_device(paddr_t);
+paddr_t xbridge_device_to_pa(bus_addr_t);
+
+struct machine_bus_dma_tag xbridge_dma_tag = {
+ NULL, /* _cookie */
+ _dmamap_create,
+ _dmamap_destroy,
+ _dmamap_load,
+ _dmamap_load_mbuf,
+ _dmamap_load_uio,
+ _dmamap_load_raw,
+ _dmamap_unload,
+ _dmamap_sync,
+ _dmamem_alloc,
+ _dmamem_free,
+ _dmamem_map,
+ _dmamem_unmap,
+ _dmamem_mmap,
+ xbridge_pa_to_device,
+ xbridge_device_to_pa,
+ 0ULL /* no mask */
+};
+
+int
+xbridge_match(struct device *parent, void *match, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+
+ if (xaa->xaa_vendor == PCI_VENDOR_SGI4 &&
+ xaa->xaa_product == PCI_PRODUCT_SGI4_BRIDGE)
+ return 1;
+
+ return 0;
+}
+
+void
+xbridge_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct xbridge_softc *sc = (struct xbridge_softc *)self;
+ struct pcibus_attach_args pba;
+ struct xbow_attach_args *xaa = aux;
+ int i;
+
+ sc->sc_rev = xaa->xaa_revision;
+ sc->sc_widget = xaa->xaa_widget;
+
+ printf(" revision %d\n", sc->sc_rev);
+
+ /*
+ * Map Bridge registers.
+ */
+
+ sc->sc_iot = xaa->xaa_short_tag;
+ if (bus_space_map(sc->sc_iot, 0, BRIDGE_REGISTERS_SIZE, 0,
+ &sc->sc_regh)) {
+ printf("%s: unable to map control registers\n", self->dv_xname);
+ return;
+ }
+
+ /*
+ * Create bus_space accessors... we inherit them from xbow, but
+ * it is necessary to perform endianness conversion for the
+ * low-order address bits.
+ */
+
+ sc->sc_mem_bus_space = malloc(sizeof (*sc->sc_mem_bus_space),
+ M_DEVBUF, M_NOWAIT);
+ if (sc->sc_mem_bus_space == NULL)
+ goto fail1;
+ sc->sc_io_bus_space = malloc(sizeof (*sc->sc_io_bus_space),
+ M_DEVBUF, M_NOWAIT);
+ if (sc->sc_io_bus_space == NULL)
+ goto fail2;
+
+ bcopy(xaa->xaa_long_tag, sc->sc_mem_bus_space,
+ sizeof(*sc->sc_mem_bus_space));
+ sc->sc_mem_bus_space->bus_base = xaa->xaa_long_tag->bus_base +
+ BRIDGE_PCI_MEM_SPACE_BASE;
+
+ if (sc->sc_rev >= 4) {
+ /* Unrestricted I/O mappings in the large window */
+ bcopy(xaa->xaa_long_tag, sc->sc_io_bus_space,
+ sizeof(*sc->sc_io_bus_space));
+ sc->sc_io_bus_space->bus_base +=
+ BRIDGE_PCI_IO_SPACE_BASE;
+ } else {
+ /* Programmable I/O mappings in the small window */
+ bcopy(xaa->xaa_short_tag, sc->sc_io_bus_space,
+ sizeof(*sc->sc_io_bus_space));
+ }
+
+ sc->sc_io_bus_space->_space_read_1 = xbridge_read_1;
+ sc->sc_io_bus_space->_space_read_2 = xbridge_read_2;
+ sc->sc_io_bus_space->_space_write_1 = xbridge_write_1;
+ sc->sc_io_bus_space->_space_write_2 = xbridge_write_2;
+
+ sc->sc_mem_bus_space->_space_read_1 = xbridge_read_1;
+ sc->sc_mem_bus_space->_space_read_2 = xbridge_read_2;
+ sc->sc_mem_bus_space->_space_write_1 = xbridge_write_1;
+ sc->sc_mem_bus_space->_space_write_2 = xbridge_write_2;
+
+ /*
+ * Initialize PCI methods.
+ */
+
+ sc->sc_pc.pc_conf_v = sc;
+ sc->sc_pc.pc_attach_hook = xbridge_attach_hook;
+ sc->sc_pc.pc_make_tag = xbridge_make_tag;
+ sc->sc_pc.pc_decompose_tag = xbridge_decompose_tag;
+ sc->sc_pc.pc_bus_maxdevs = xbridge_bus_maxdevs;
+ sc->sc_pc.pc_conf_read = xbridge_conf_read;
+ sc->sc_pc.pc_conf_write = xbridge_conf_write;
+ sc->sc_pc.pc_intr_v = sc;
+ sc->sc_pc.pc_intr_map = xbridge_intr_map;
+ sc->sc_pc.pc_intr_string = xbridge_intr_string;
+ sc->sc_pc.pc_intr_establish = xbridge_intr_establish;
+ sc->sc_pc.pc_intr_disestablish = xbridge_intr_disestablish;
+
+ /*
+ * XXX The following magic sequence is supposedly needed for DMA
+ * XXX to work correctly. I have no idea what it really does.
+ */
+ if (sys_config.system_type == SGI_OCTANE) {
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP,
+ (xbow_intr_widget << 20) | (1 << 17));
+#if 0
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, 0x284,
+ 0xddcc9988);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, 0x28c,
+ 0xddcc9988);
+#endif
+ } else {
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_DIR_MAP,
+ xbow_intr_widget << 20);
+ }
+
+ (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH);
+
+ /*
+ * Setup interrupt handling.
+ */
+
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV, 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_UPPER,
+ xbow_intr_widget << 16);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, WIDGET_INTDEST_ADDR_LOWER,
+ xbow_intr_widget_register);
+ (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH);
+ for (i = 0; i < BRIDGE_NINTRS; i++)
+ sc->sc_intrbit[i] = -1;
+
+ /*
+ * Attach children.
+ */
+
+ pba.pba_busname = "pci";
+ pba.pba_iot = sc->sc_io_bus_space;
+ pba.pba_memt = sc->sc_mem_bus_space;
+ pba.pba_dmat = &xbridge_dma_tag;
+ pba.pba_pc = &sc->sc_pc;
+ pba.pba_domain = pci_ndomains++;
+ pba.pba_bus = sc->sc_dev.dv_unit;
+ pba.pba_bridgetag = NULL;
+
+ config_found(self, &pba, xbridge_print);
+ return;
+
+fail2:
+ free(sc->sc_mem_bus_space, M_DEVBUF);
+fail1:
+ printf("%s: not enough memory to build access structures\n",
+ self->dv_xname);
+ return;
+}
+
+int
+xbridge_print(void *aux, const char *pnp)
+{
+ struct pcibus_attach_args *pba = aux;
+
+ if (pnp)
+ printf("%s at %s", pba->pba_busname, pnp);
+ printf(" bus %d", pba->pba_bus);
+
+ return UNCONF;
+}
+
+void
+xbridge_attach_hook(struct device *parent, struct device *self,
+ struct pcibus_attach_args *pba)
+{
+}
+
+pcitag_t
+xbridge_make_tag(void *cookie, int bus, int dev, int func)
+{
+ return (bus << 16) | (dev << 11) | (func << 8);
+}
+
+void
+xbridge_decompose_tag(void *cookie, pcitag_t tag, int *busp, int *devp,
+ int *funcp)
+{
+ if (busp != NULL)
+ *busp = (tag >> 16) & 0x7;
+ if (devp != NULL)
+ *devp = (tag >> 11) & 0x1f;
+ if (funcp != NULL)
+ *funcp = (tag >> 8) & 0x7;
+}
+
+int
+xbridge_bus_maxdevs(void *cookie, int busno)
+{
+ return BRIDGE_NSLOTS;
+}
+
+pcireg_t
+xbridge_conf_read(void *cookie, pcitag_t tag, int offset)
+{
+ struct xbridge_softc *sc = cookie;
+ pcireg_t data;
+ int bus, dev, fn;
+ paddr_t pa;
+ int s;
+
+ /* XXX should actually disable interrupts? */
+ s = splhigh();
+
+ xbridge_decompose_tag(cookie, tag, &bus, &dev, &fn);
+ if (bus != 0) {
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_PCI_CFG,
+ (bus << 16) | (dev << 11));
+ pa = sc->sc_regh + BRIDGE_PCI_CFG1_SPACE;
+ } else
+ pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + (dev << 12);
+
+ pa += (fn << 8) + offset;
+ if (guarded_read_4(pa, &data) != 0)
+ data = 0xffffffff;
+
+ splx(s);
+ return(data);
+}
+
+void
+xbridge_conf_write(void *cookie, pcitag_t tag, int offset, pcireg_t data)
+{
+ struct xbridge_softc *sc = cookie;
+ int bus, dev, fn;
+ paddr_t pa;
+ int s;
+
+ /* XXX should actually disable interrupts? */
+ s = splhigh();
+
+ xbridge_decompose_tag(cookie, tag, &bus, &dev, &fn);
+ if (bus != 0) {
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_PCI_CFG,
+ (bus << 16) | (dev << 11));
+ pa = sc->sc_regh + BRIDGE_PCI_CFG1_SPACE;
+ } else
+ pa = sc->sc_regh + BRIDGE_PCI_CFG_SPACE + (dev << 12);
+
+ pa += (fn << 8) + offset;
+ guarded_write_4(pa, data);
+
+ splx(s);
+}
+
+/*
+ * Interrupt handling.
+ * We map each slot to its own interrupt bit, which will in turn be routed to
+ * the Heart or Hub widget in charge of interrupt processing.
+ */
+
+struct xbridge_intr {
+ struct xbridge_softc *xi_bridge;
+ int xi_intrsrc;
+
+ int (*xi_func)(void *);
+ void *xi_arg;
+
+ struct evcount xi_count;
+ int xi_level;
+};
+
+int
+xbridge_intr_map(struct pci_attach_args *pa, pci_intr_handle_t *ihp)
+{
+ int bus, device, intr;
+
+ *ihp = -1;
+
+ if (pa->pa_intrpin == 0) {
+ /* No IRQ used. */
+ return 1;
+ }
+
+#ifdef DIAGNOSTIC
+ if (pa->pa_intrpin > 4) {
+ printf("%s: bad interrupt pin %d\n", __func__, pa->pa_intrpin);
+ return 1;
+ }
+#endif
+
+ xbridge_decompose_tag(pa->pa_pc, pa->pa_tag, &bus, &device, NULL);
+ if (pa->pa_intrpin & 1)
+ intr = device;
+ else
+ intr = device ^ 4;
+
+ *ihp = intr;
+
+ return 0;
+}
+
+const char *
+xbridge_intr_string(void *cookie, pci_intr_handle_t ih)
+{
+ static char str[16];
+
+ snprintf(str, sizeof(str), "irq %d", ih);
+ return(str);
+}
+
+void *
+xbridge_intr_establish(void *cookie, pci_intr_handle_t ih, int level,
+ int (*func)(void *), void *arg, char *name)
+{
+ struct xbridge_softc *sc = cookie;
+ struct xbridge_intr *xi;
+ uint32_t int_addr;
+ int intrbit = ih & 0x07;
+ int intrsrc;
+ int16_t nasid = 0; /* XXX */
+
+ if (sc->sc_intr[intrbit] != NULL) {
+ printf("%s: nested interrupts are not supported\n", __func__);
+ return NULL;
+ }
+
+ xi = (struct xbridge_intr *)malloc(sizeof(*xi), M_DEVBUF, M_NOWAIT);
+ if (xi == NULL)
+ return NULL;
+
+ /*
+ * Register the interrupt at the Heart or Hub level if it's the
+ * first time we're using this interrupt source.
+ */
+ if ((intrsrc = sc->sc_intrbit[intrbit]) == -1) {
+ if (xbow_intr_register(sc->sc_widget, level, &intrsrc) != 0)
+ return NULL;
+
+ /*
+ * We can afford registering this interrupt at `level'
+ * IPL since we do not support nested interrupt on a
+ * given source, yet.
+ */
+ if (xbow_intr_establish(xbridge_intr_handler, xi, intrsrc,
+ level, sc->sc_dev.dv_xname)) {
+ printf("%s: unable to register interrupt handler, "
+ "did xheart or xhub attach?\n",
+ sc->sc_dev.dv_xname);
+ return NULL;
+ }
+
+ sc->sc_intrbit[intrbit] = intrsrc;
+ }
+
+ xi->xi_bridge = sc;
+ xi->xi_intrsrc = intrsrc;
+ xi->xi_func = func;
+ xi->xi_arg = arg;
+ xi->xi_level = level;
+ evcount_attach(&xi->xi_count, name, &xi->xi_level, &evcount_intr);
+ sc->sc_intr[intrbit] = xi;
+
+ switch (sys_config.system_type) {
+ case SGI_OCTANE:
+ int_addr = intrsrc;
+ break;
+ default:
+ case SGI_O200:
+ int_addr = 0x20000 | intrsrc | (nasid << 8);
+ break;
+ }
+
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_ADDR(intrbit),
+ int_addr);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) |
+ (1 << intrbit));
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) |
+ (1 << intrbit));
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) |
+ (7 << (intrbit * 3)));
+ (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH);
+
+ return (void *)((uint64_t)ih | 8); /* XXX don't return zero */
+}
+
+void
+xbridge_intr_disestablish(void *cookie, void *ih)
+{
+ struct xbridge_softc *sc = cookie;
+ struct xbridge_intr *xi;
+ int intrbit = (uint64_t)ih & 0x07;
+
+ /* should not happen */
+ if ((xi = sc->sc_intr[intrbit]) == NULL)
+ return;
+
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_ADDR(intrbit), 0);
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_IER) &
+ ~(1 << intrbit));
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_MODE) &
+ ~(1 << intrbit));
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV,
+ bus_space_read_4(sc->sc_iot, sc->sc_regh, BRIDGE_INT_DEV) &
+ ~(7 << (intrbit * 3)));
+ (void)bus_space_read_4(sc->sc_iot, sc->sc_regh, WIDGET_TFLUSH);
+
+ evcount_detach(&xi->xi_count);
+
+ xbow_intr_disestablish(xi->xi_intrsrc);
+
+ sc->sc_intr[intrbit] = NULL;
+ free(xi, M_DEVBUF);
+}
+
+int
+xbridge_intr_handler(void *v)
+{
+ struct xbridge_intr *xi = v;
+ struct xbridge_softc *sc = xi->xi_bridge;
+ int rc;
+
+ if (xi == NULL) {
+ printf("%s: spurious interrupt on source %d\n",
+ sc->sc_dev.dv_xname, xi->xi_intrsrc);
+ return 0;
+ }
+
+ if ((rc = (*xi->xi_func)(xi->xi_arg)) != 0)
+ xi->xi_count.ec_count++;
+
+#if 0
+ /* Clear PCI interrupts. */
+ bus_space_write_4(sc->sc_iot, sc->sc_regh, BRIDGE_ICR, 1 << 0);
+#endif
+
+ return rc;
+}
+
+/*
+ * bus_space helpers
+ */
+
+uint8_t
+xbridge_read_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint8_t *)((h + o) ^ 3);
+}
+
+uint16_t
+xbridge_read_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o)
+{
+ return *(volatile uint16_t *)((h + o) ^ 2);
+}
+
+void
+xbridge_write_1(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
+ uint8_t v)
+{
+ *(volatile uint8_t *)((h + o) ^ 3) = v;
+}
+
+void
+xbridge_write_2(bus_space_tag_t t, bus_space_handle_t h, bus_size_t o,
+ uint16_t v)
+{
+ *(volatile uint16_t *)((h + o) ^ 2) = v;
+}
+
+/*
+ * bus_dma helpers
+ */
+
+bus_addr_t
+xbridge_pa_to_device(paddr_t pa)
+{
+ switch (sys_config.system_type) {
+ case SGI_OCTANE:
+ /*
+ * On Octane, direct DMA is not possible on memory
+ * above 2GB. Until _dmamem_alloc() is modified to
+ * make sure it doesn't use memory above this limit,
+ * add a check there. Otherwise I'll never come back
+ * and fix _dmamem_alloc().
+ */
+ if (pa > IP30_MEMORY_BASE + BRIDGE_DMA_DIRECT_LENGTH)
+ panic("dma above 2GB");
+
+ return (pa - IP30_MEMORY_BASE) + BRIDGE_DMA_DIRECT_BASE;
+
+ case SGI_O200:
+ break; /* XXX likely wrong */
+ }
+
+ return pa;
+}
+
+paddr_t
+xbridge_device_to_pa(bus_addr_t addr)
+{
+ switch (sys_config.system_type) {
+ case SGI_OCTANE:
+ return (addr - BRIDGE_DMA_DIRECT_BASE) + IP30_MEMORY_BASE;
+
+ case SGI_O200:
+ break;
+ }
+
+ return addr;
+}
diff --git a/sys/arch/sgi/xbow/xbridgereg.h b/sys/arch/sgi/xbow/xbridgereg.h
new file mode 100644
index 00000000000..cbb38b32751
--- /dev/null
+++ b/sys/arch/sgi/xbow/xbridgereg.h
@@ -0,0 +1,95 @@
+/* $OpenBSD: xbridgereg.h,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * IP27/IP30 Bridge Registers
+ */
+
+#define BRIDGE_REGISTERS_SIZE 0x00030000
+#define BRIDGE_NSLOTS 8
+#define BRIDGE_NINTRS 8
+
+#define BRIDGE_WIDGET_CONTROL_IO_SWAP 0x00800000
+#define BRIDGE_WIDGET_CONTROL_MEM_SWAP 0x00400000
+
+#define BRIDGE_DIR_MAP 0x00000084
+#define BRIDGE_NIC 0x000000b4
+#define BRIDGE_BUS_TIMEOUT 0x000000c4
+#define BRIDGE_PCI_CFG 0x000000cc
+#define BRIDGE_PCI_ERR_UPPER 0x000000d4
+#define BRIDGE_PCI_ERR_LOWER 0x000000dc
+
+/*
+ * Interrupt handling
+ */
+
+#define BRIDGE_ISR 0x00000104
+#define BRIDGE_IER 0x0000010c
+#define BRIDGE_ICR 0x00000114
+#define BRIDGE_INT_MODE 0x0000011c
+#define BRIDGE_INT_DEV 0x00000124
+#define BRIDGE_INT_HOST_ERR 0x0000012c
+#define BRIDGE_INT_ADDR(d) (0x00000134 + 8 * (d))
+
+/*
+ * Mapping control
+ *
+ * There are three ways to map a given device:
+ * - memory mapping in the long window, at BRIDGE_PCI_MEM_SPACE_BASE,
+ * shared by all devices.
+ * - I/O mapping in the long window, at BRIDGE_PCI_IO_SPACE_BASE,
+ * shared by all devices, but only on widget revision 4 or later.
+ * - programmable memory or I/O mapping at a selectable place in the
+ * short window, with an 1MB granularity. The size of this
+ * window is 2MB for the windows at 2MB and 4MB, and 1MB onwards.
+ *
+ * ARCBios will setup mappings in the short window for us, and
+ * the selected address will match BAR0.
+ */
+
+#define BRIDGE_DEVICE(d) (0x00000204 + 8 * (d))
+#define BRIDGE_DEVICE_SWAP 0x00002000
+#define BRIDGE_DEVICE_IO 0x00001000
+#define BRIDGE_DEVICE_BASE_MASK 0x00000fff
+#define BRIDGE_DEVICE_BASE_SHIFT 20
+
+#define BRIDGE_PCI_MEM_SPACE_BASE 0x0000000040000000ULL
+#define BRIDGE_PCI_MEM_SPACE_LENGTH 0x0000000040000000ULL
+#define BRIDGE_PCI_IO_SPACE_BASE 0x0000000100000000ULL
+#define BRIDGE_PCI_IO_SPACE_LENGTH 0x0000000100000000ULL
+
+/*
+ * Configuration space
+ *
+ * Access to the first bus is done in the first area, sorted by
+ * device number and function number.
+ * Access to other buses is done in the second area, after programming
+ * BRIDGE_PCI_CFG to the appropriate bus and slot number.
+ */
+
+#define BRIDGE_PCI_CFG_SPACE 0x00020000
+#define BRIDGE_PCI_CFG1_SPACE 0x00028000
+
+/*
+ * DMA addresses
+ * The Bridge can do DMA either through a direct 2GB window, or through
+ * a 1GB translated window, using its ATE memory.
+ */
+
+#define BRIDGE_DMA_DIRECT_BASE 0x80000000ULL
+#define BRIDGE_DMA_DIRECT_LENGTH 0x80000000ULL
diff --git a/sys/arch/sgi/xbow/xheart.c b/sys/arch/sgi/xbow/xheart.c
new file mode 100644
index 00000000000..cd278fcb0c0
--- /dev/null
+++ b/sys/arch/sgi/xbow/xheart.c
@@ -0,0 +1,458 @@
+/* $OpenBSD: xheart.c,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * IP30 Heart Widget
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+#include <sys/evcount.h>
+#include <sys/malloc.h>
+#include <sys/queue.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+#include <machine/intr.h>
+
+#include <sgi/xbow/xbow.h>
+#include <sgi/xbow/xbowdevs.h>
+#include <sgi/xbow/xheartreg.h>
+
+#include <dev/onewire/onewirereg.h>
+#include <dev/onewire/onewirevar.h>
+
+struct xheart_softc {
+ struct device sc_dev;
+ struct onewire_bus sc_bus;
+
+ uint64_t sc_intrmask;
+};
+
+int xheart_match(struct device *, void *, void *);
+void xheart_attach(struct device *, struct device *, void *);
+
+const struct cfattach xheart_ca = {
+ sizeof(struct xheart_softc), xheart_match, xheart_attach,
+};
+
+struct cfdriver xheart_cd = {
+ NULL, "xheart", DV_DULL,
+};
+
+int xheart_ow_reset(void *);
+int xheart_ow_read_bit(struct xheart_softc *);
+int xheart_ow_send_bit(void *, int);
+int xheart_ow_read_byte(void *);
+int xheart_ow_triplet(void *, int);
+int xheart_ow_pulse(struct xheart_softc *, int, int);
+
+int xheart_intr_register(int, int, int *);
+int xheart_intr_establish(int (*)(void *), void *, int, int, const char *);
+void xheart_intr_disestablish(int);
+intrmask_t xheart_intr_handler(intrmask_t, struct trap_frame *);
+void xheart_intr_makemasks(struct xheart_softc *);
+void xheart_do_pending_int(int);
+
+int
+xheart_match(struct device *parent, void *match, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+
+ if (xaa->xaa_vendor == PCI_VENDOR_SGI4 &&
+ xaa->xaa_product == PCI_PRODUCT_SGI4_HEART) {
+ /*
+ * Only match if no interrupt widget has registered yet.
+ * There should be only one Heart in a system anyway.
+ */
+ return xbow_intr_widget == 0 ? 20 : 0;
+ }
+
+ return 0;
+}
+
+void
+xheart_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+ struct xheart_softc *sc = (void *)self;
+ struct onewirebus_attach_args oba;
+ paddr_t heart;
+
+ printf(" revision %d\n", xaa->xaa_revision);
+
+ sc->sc_bus.bus_cookie = sc;
+ sc->sc_bus.bus_reset = xheart_ow_reset;
+ sc->sc_bus.bus_bit = xheart_ow_send_bit;
+ sc->sc_bus.bus_read_byte = xheart_ow_read_byte;
+ sc->sc_bus.bus_write_byte = NULL; /* use default routine */
+ sc->sc_bus.bus_read_block = NULL; /* use default routine */
+ sc->sc_bus.bus_write_block = NULL; /* use default routine */
+ sc->sc_bus.bus_triplet = xheart_ow_triplet;
+ sc->sc_bus.bus_matchrom = NULL; /* use default routine */
+ sc->sc_bus.bus_search = NULL; /* use default routine */
+
+ oba.oba_bus = &sc->sc_bus;
+ oba.oba_flags = ONEWIRE_SCAN_NOW | ONEWIRE_NO_PERIODIC_SCAN;
+ config_found(self, &oba, onewirebus_print);
+
+ /*
+ * If no other widget has claimed interrupts routing, do it now.
+ */
+ if (xbow_intr_widget == 0) {
+ xbow_intr_widget = xaa->xaa_widget;
+ xbow_intr_widget_register = 0x80;
+ xbow_intr_widget_intr_register = xheart_intr_register;
+ xbow_intr_widget_intr_establish = xheart_intr_establish;
+ xbow_intr_widget_intr_disestablish = xheart_intr_disestablish;
+ sc->sc_intrmask = 0;
+
+ /*
+ * Acknowledge and disable all interrupts.
+ */
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE, CCA_NC);
+ *(volatile uint64_t*)(heart + HEART_ISR_CLR) =
+ 0xffffffffffffffff;
+ *(volatile uint64_t*)(heart + HEART_IMR(0)) = 0UL;
+ *(volatile uint64_t*)(heart + HEART_IMR(1)) = 0UL;
+ *(volatile uint64_t*)(heart + HEART_IMR(2)) = 0UL;
+ *(volatile uint64_t*)(heart + HEART_IMR(3)) = 0UL;
+
+ set_intr(INTPRI_XBOWMUX, CR_INT_0, xheart_intr_handler);
+ register_pending_int_handler(xheart_do_pending_int);
+ }
+}
+
+/*
+ * Number-In-a-Can (1-Wire) interface
+ */
+
+int
+xheart_ow_reset(void *v)
+{
+ struct xheart_softc *sc = v;
+ return xheart_ow_pulse(sc, 500, 65);
+}
+
+int
+xheart_ow_read_bit(struct xheart_softc *sc)
+{
+ return xheart_ow_pulse(sc, 6, 13);
+}
+
+int
+xheart_ow_send_bit(void *v, int bit)
+{
+ struct xheart_softc *sc = v;
+ int rc;
+
+ if (bit != 0)
+ rc = xheart_ow_pulse(sc, 6, 110);
+ else
+ rc = xheart_ow_pulse(sc, 80, 30);
+ return rc;
+}
+
+int
+xheart_ow_read_byte(void *v)
+{
+ struct xheart_softc *sc = v;
+ unsigned int byte = 0;
+ int i;
+
+ for (i = 0; i < 8; i++)
+ byte |= xheart_ow_read_bit(sc) << i;
+
+ return byte;
+}
+
+int
+xheart_ow_triplet(void *v, int dir)
+{
+ struct xheart_softc *sc = v;
+ int rc;
+
+ rc = xheart_ow_read_bit(sc);
+ rc <<= 1;
+ rc |= xheart_ow_read_bit(sc);
+
+ switch (rc) {
+ case 0x0:
+ xheart_ow_send_bit(v, dir);
+ break;
+ case 0x1:
+ xheart_ow_send_bit(v, 0);
+ break;
+ default:
+ xheart_ow_send_bit(v, 1);
+ break;
+ }
+
+ return (rc);
+}
+
+int
+xheart_ow_pulse(struct xheart_softc *sc, int pulse, int data)
+{
+ uint64_t mcr_value;
+ paddr_t heart;
+
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE + HEART_MICROLAN, CCA_NC);
+ mcr_value = (pulse << 10) | (data << 2);
+ *(volatile uint64_t *)heart = mcr_value;
+ do {
+ mcr_value = *(volatile uint64_t *)heart;
+ } while ((mcr_value & 0x00000002) == 0);
+
+ delay(500);
+
+ return (mcr_value & 1);
+}
+
+/*
+ * HEART interrupt handling routines
+ */
+
+/*
+ * Find a suitable interrupt bit for the given interrupt.
+ */
+int
+xheart_intr_register(int widget, int level, int *intrbit)
+{
+ struct xheart_softc *sc = (void *)xheart_cd.cd_devs[0];
+ int bit;
+
+ /*
+ * All interrupts will be serviced at hardware level 0,
+ * so the `level' argument can be ignored.
+ * On HEART, the low 16 bits of the interrupt register
+ * are level 0 sources.
+ */
+ for (bit = 15; bit >= 0; bit--)
+ if ((sc->sc_intrmask & (1 << bit)) == 0)
+ break;
+
+ if (bit < 0)
+ return EINVAL;
+
+ *intrbit = bit;
+ return 0;
+}
+
+/*
+ * Register an interrupt handler for a given source, and enable it.
+ */
+int
+xheart_intr_establish(int (*func)(void *), void *arg, int intrbit,
+ int level, const char *name)
+{
+ struct xheart_softc *sc = (void *)xheart_cd.cd_devs[0];
+ struct intrhand *ih;
+ paddr_t heart;
+
+#ifdef DIAGNOSTIC
+ if (intrbit < 0 || intrbit >= 16)
+ return EINVAL;
+#endif
+
+ /*
+ * HEART interrupts are not supposed to be shared - the interrupt
+ * mask is large enough for all widgets.
+ */
+ if (intrhand[intrbit] != NULL)
+ return EEXIST;
+
+ ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
+ if (ih == NULL)
+ return ENOMEM;
+
+ ih->ih_next = NULL;
+ ih->ih_fun = func;
+ ih->ih_arg = arg;
+ ih->ih_level = level;
+ ih->ih_irq = intrbit;
+ evcount_attach(&ih->ih_count, name, &ih->ih_level, &evcount_intr);
+ intrhand[intrbit] = ih;
+
+ sc->sc_intrmask |= 1UL << intrbit;
+ xheart_intr_makemasks(sc);
+
+ /* XXX this assumes we run on cpu0 */
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE, CCA_NC);
+ *(volatile uint64_t *)(heart + HEART_IMR(0)) |= 1UL << intrbit;
+
+ return 0;
+}
+
+void
+xheart_intr_disestablish(int intrbit)
+{
+ struct xheart_softc *sc = (void *)xheart_cd.cd_devs[0];
+ struct intrhand *ih;
+ paddr_t heart;
+ int s;
+
+#ifdef DIAGNOSTIC
+ if (intrbit < 0 || intrbit >= 16)
+ return;
+#endif
+
+ s = splhigh();
+
+ if ((ih = intrhand[intrbit]) == NULL) {
+ splx(s);
+ return;
+ }
+
+ /* XXX this assumes we run on cpu0 */
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE, CCA_NC);
+ *(volatile uint64_t *)(heart + HEART_IMR(0)) &= ~(1UL << intrbit);
+
+ intrhand[intrbit] = NULL;
+
+ sc->sc_intrmask &= ~(1UL << intrbit);
+ xheart_intr_makemasks(sc);
+
+ free(ih, M_DEVBUF);
+
+ splx(s);
+}
+
+/*
+ * Xheart interrupt handler driver.
+ */
+
+intrmask_t heart_intem = 0;
+
+/*
+ * Recompute interrupt masks.
+ */
+void
+xheart_intr_makemasks(struct xheart_softc *sc)
+{
+ int irq, level;
+ struct intrhand *q;
+ intrmask_t intrlevel[INTMASKSIZE];
+
+ /* First, figure out which levels each IRQ uses. */
+ for (irq = 0; irq < INTMASKSIZE; irq++) {
+ int levels = 0;
+ for (q = intrhand[irq]; q; q = q->ih_next)
+ levels |= 1 << q->ih_level;
+ intrlevel[irq] = levels;
+ }
+
+ /* Then figure out which IRQs use each level. */
+ for (level = IPL_NONE; level < NIPLS; level++) {
+ int irqs = 0;
+ for (irq = 0; irq < INTMASKSIZE; irq++)
+ if (intrlevel[irq] & (1 << level))
+ irqs |= 1 << irq;
+ imask[level] = irqs | SINT_ALLMASK;
+ }
+
+ /*
+ * There are tty, network and disk drivers that use free() at interrupt
+ * time, so vm > (tty | net | bio).
+ *
+ * Enforce a hierarchy that gives slow devices a better chance at not
+ * dropping data.
+ */
+ imask[IPL_NET] |= imask[IPL_BIO];
+ imask[IPL_TTY] |= imask[IPL_NET];
+ imask[IPL_VM] |= imask[IPL_TTY];
+ imask[IPL_CLOCK] |= imask[IPL_VM] | SPL_CLOCKMASK;
+
+ /*
+ * These are pseudo-levels.
+ */
+ imask[IPL_NONE] = 0;
+ imask[IPL_HIGH] = -1;
+
+ /* Lastly, determine which IRQs are actually in use. */
+ heart_intem = sc->sc_intrmask & 0x00000000ffffffffL;
+ hw_setintrmask(0);
+}
+
+void
+xheart_do_pending_int(int newcpl)
+{
+ /* Update masks to new cpl. Order highly important! */
+ __asm__ (" .set noreorder\n");
+ cpl = newcpl;
+ __asm__ (" sync\n .set reorder\n");
+ hw_setintrmask(newcpl);
+ /* If we still have softints pending trigger processing. */
+ if (ipending & SINT_ALLMASK & ~newcpl)
+ setsoftintr0();
+}
+
+intrmask_t
+xheart_intr_handler(intrmask_t hwpend, struct trap_frame *frame)
+{
+ paddr_t heart;
+ uint64_t imr, isr;
+ int bit;
+ intrmask_t mask;
+ struct intrhand *ih;
+
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE, CCA_NC);
+ isr = *(volatile uint64_t *)(heart + HEART_ISR);
+ imr = *(volatile uint64_t *)(heart + HEART_IMR(0));
+
+ isr &= imr;
+ if (isr == 0)
+ return 0; /* not for us */
+
+ /*
+ * If interrupts are spl-masked, mark them as pending and mask
+ * them in hardware.
+ */
+ if ((mask = isr & frame->cpl) != 0) {
+ atomic_setbits_int(&ipending, mask);
+ *(volatile uint64_t *)(heart + HEART_IMR(0)) &= ~mask;
+ isr &= ~mask;
+ }
+
+ /*
+ * Now process unmasked interrupts.
+ */
+ mask = isr & ~frame->cpl;
+ atomic_clearbits_int(&ipending, mask);
+ for (bit = 15, mask = 1 << 15; bit >= 0; bit--, mask >>= 1) {
+ if ((isr & mask) == 0)
+ continue;
+
+ for (ih = intrhand[bit]; ih != NULL; ih = ih->ih_next) {
+ if ((*ih->ih_fun)(ih->ih_arg) != 0)
+ ih->ih_count.ec_count++;
+ }
+ }
+
+ return CR_INT_0; /* hwpend */
+}
+
+void
+hw_setintrmask(intrmask_t m)
+{
+ paddr_t heart;
+ heart = PHYS_TO_XKPHYS(HEART_PIU_BASE, CCA_NC);
+ *(volatile uint64_t *)(heart + HEART_IMR(0)) = heart_intem & ~m;
+}
diff --git a/sys/arch/sgi/xbow/xheartreg.h b/sys/arch/sgi/xbow/xheartreg.h
new file mode 100644
index 00000000000..d5c5b48da9d
--- /dev/null
+++ b/sys/arch/sgi/xbow/xheartreg.h
@@ -0,0 +1,46 @@
+/* $OpenBSD: xheartreg.h,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * IP30 HEART registers
+ */
+
+/* physical address in PIU mode */
+#define HEART_PIU_BASE 0x000000000ff00000UL
+
+#define HEART_MODE 0x0000
+#define HEART_MEMORY_STATUS 0x0020 /* 8 32 bit registers */
+#define HEART_MEMORY_VALID 0x80000000
+#define HEART_MEMORY_SIZE_MASK 0x003f0000
+#define HEART_MEMORY_SIZE_SHIFT 16
+#define HEART_MEMORY_ADDR_MASK 0x000001ff
+#define HEART_MEMORY_ADDR_SHIFT 0
+#define HEART_MEMORY_UNIT_SHIFT 25 /* 32MB */
+
+#define HEART_MICROLAN 0x00b8
+
+/*
+ * Interrupt handling registers.
+ * The Heart supports four different interrupt targets, although only
+ * the two cpus are used in practice.
+ */
+
+#define HEART_IMR(s) (0x00010000 + (s) * 8)
+#define HEART_ISR_SET 0x00010020
+#define HEART_ISR_CLR 0x00010028
+#define HEART_ISR 0x00010030
diff --git a/sys/arch/sgi/xbow/xhub.c b/sys/arch/sgi/xbow/xhub.c
new file mode 100644
index 00000000000..db77bd20486
--- /dev/null
+++ b/sys/arch/sgi/xbow/xhub.c
@@ -0,0 +1,72 @@
+/* $OpenBSD: xhub.c,v 1.1 2008/04/07 22:47:40 miod Exp $ */
+
+/*
+ * Copyright (c) 2008 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.
+ */
+
+/*
+ * IP27 Hub Widget
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/device.h>
+
+#include <machine/autoconf.h>
+#include <machine/cpu.h>
+
+#include <sgi/xbow/xbow.h>
+#include <sgi/xbow/xbowdevs.h>
+
+int xhub_match(struct device *, void *, void *);
+void xhub_attach(struct device *, struct device *, void *);
+
+struct cfattach xhub_ca = {
+ sizeof(struct device), xhub_match, xhub_attach,
+};
+
+struct cfdriver xhub_cd = {
+ NULL, "xhub", DV_DULL,
+};
+
+int
+xhub_match(struct device *parent, void *match, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+
+ if (xaa->xaa_vendor == PCI_VENDOR_SGI4 &&
+ xaa->xaa_product == PCI_PRODUCT_SGI4_HUB)
+ return xbow_intr_widget == 0 ? 20 : 1;
+
+ return 0;
+}
+
+void
+xhub_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct xbow_attach_args *xaa = aux;
+
+ printf(" revision %d\n", xaa->xaa_revision);
+
+ /*
+ * If no other widget has claimed interrupts routing, do it now.
+ */
+ if (xbow_intr_widget == 0) {
+ xbow_intr_widget = xaa->xaa_widget;
+ }
+
+ /* initialize interrupt handling here */
+}