diff options
author | Miod Vallat <miod@cvs.openbsd.org> | 2008-04-07 22:47:41 +0000 |
---|---|---|
committer | Miod Vallat <miod@cvs.openbsd.org> | 2008-04-07 22:47:41 +0000 |
commit | 679c34c359caf34bae1086308848c5ebca2336bb (patch) | |
tree | c78158be034adac0539ab893d56d4aad91fdccf2 /sys/arch | |
parent | f9a2f634636631d02226178ddd002d9792325cbf (diff) |
A first cut at XBow bus support, very minimal, limited to a local bus only;
HUB driver (for IP27) is a stub, and interrupt support is a shoot in the dark
and will need some serious debugging until it is sane, but I want to reduce
the weight of these diffs first.
Based on a lot of tinkering and experiments, as well as knowledge extracted
from the Linux source code.
Diffstat (limited to 'sys/arch')
-rw-r--r-- | sys/arch/sgi/xbow/Makefile | 7 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/devlist2h.awk | 168 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/files.xbow | 16 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbow.c | 514 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbow.h | 125 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbowdevs | 42 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridge.c | 631 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xbridgereg.h | 95 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xheart.c | 458 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xheartreg.h | 46 | ||||
-rw-r--r-- | sys/arch/sgi/xbow/xhub.c | 72 |
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 */ +} |