diff options
Diffstat (limited to 'sys/arch/sgi/xbow/xbow.c')
-rw-r--r-- | sys/arch/sgi/xbow/xbow.c | 514 |
1 files changed, 514 insertions, 0 deletions
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); +} |