/* $OpenBSD: gio.c,v 1.18 2014/05/19 21:18:42 miod Exp $ */ /* $NetBSD: gio.c,v 1.32 2011/07/01 18:53:46 dyoung Exp $ */ /* * Copyright (c) 2012 Miodrag Vallat. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* * Copyright (c) 2000 Soren S. Jorvang * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed for the * NetBSD Project. See http://www.NetBSD.org/ for * information about NetBSD. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "grtwo.h" #include "impact.h" #include "light.h" #include "newport.h" #if NGRTWO > 0 #include #endif #if NIMPACT_GIO > 0 #include #endif #if NLIGHT > 0 #include #endif #if NNEWPORT > 0 #include #endif int gio_match(struct device *, void *, void *); void gio_attach(struct device *, struct device *, void *); int gio_print(void *, const char *); int gio_print_fb(void *, const char *); int gio_search(struct device *, void *, void *); int gio_submatch(struct device *, void *, void *); uint32_t gio_id(vaddr_t, paddr_t, int); int gio_is_framebuffer_id(uint32_t); struct gio_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_dma_tag_t sc_dmat; }; const struct cfattach gio_ca = { sizeof(struct gio_softc), gio_match, gio_attach }; struct cfdriver gio_cd = { NULL, "gio", DV_DULL }; /* Address of the console frame buffer registers, if applicable */ paddr_t giofb_consaddr; /* Id of the console frame buffer */ uint32_t giofb_consid; /* Names of the frame buffers, as obtained by ARCBios */ const char *giofb_names[GIO_MAX_FB]; struct gio_probe { uint32_t slot; uint64_t base; uint32_t mach_type; uint32_t mach_subtype; }; /* * Expansion Slot Base Addresses * * IP20 and IP24 have two GIO connectors: GIO_SLOT_EXP0 and * GIO_SLOT_EXP1. * * On IP24 these slots exist on the graphics board or the IOPLUS * "mezzanine" on Indy and Challenge S, respectively. The IOPLUS or * graphics board connects to the mainboard via a single GIO64 connector. * * IP22 has either three or four physical connectors, but only two * electrically distinct slots: GIO_SLOT_GFX and GIO_SLOT_EXP0. * * It should also be noted that DMA is (mostly) not supported in Challenge S's * GIO_SLOT_EXP1. See gio(4) for the story. */ static const struct gio_probe slot_bases[] = { /* GFX is only a slot on Indigo 2 */ { GIO_SLOT_GFX, GIO_ADDR_GFX, SGI_IP22, IP22_INDIGO2 }, /* EXP0 is available on all systems */ { GIO_SLOT_EXP0, GIO_ADDR_EXP0, SGI_IP20, -1 }, { GIO_SLOT_EXP0, GIO_ADDR_EXP0, SGI_IP22, -1 }, /* EXP1 does not exist on Indigo 2 */ { GIO_SLOT_EXP1, GIO_ADDR_EXP1, SGI_IP20, -1 }, { GIO_SLOT_EXP1, GIO_ADDR_EXP1, SGI_IP22, IP22_INDY }, { GIO_SLOT_EXP1, GIO_ADDR_EXP1, SGI_IP22, IP22_CHALLS }, { 0, 0, 0, 0 } }; /* * Graphic Board Base Addresses * * Graphics boards are not treated like expansion slot cards. Their base * addresses do not necessarily correspond to GIO slot addresses and they * do not contain product identification words. * * This list needs to be sorted in address order, to match the descriptions * obtained from ARCBios. */ static const struct gio_probe gfx_bases[] = { { -1, GIO_ADDR_GFX, SGI_IP20, -1 }, { -1, GIO_ADDR_GFX, SGI_IP22, -1 }, /* IP20 LG1/LG2 */ { -1, GIO_ADDR_GFX + 0x003f0000, SGI_IP20, -1 }, { -1, GIO_ADDR_GFX + 0x003f8000, SGI_IP20, -1 }, /* second head */ { -1, GIO_ADDR_EXP0, SGI_IP22, -1 }, { -1, GIO_ADDR_EXP1, SGI_IP22, -1 }, { 0, 0, 0, 0 } }; int gio_match(struct device *parent, void *match, void *aux) { struct imc_attach_args *iaa = aux; if (strcmp(iaa->iaa_name, gio_cd.cd_name) != 0) return 0; return 1; } void gio_attach(struct device *parent, struct device *self, void *aux) { struct gio_softc *sc = (struct gio_softc *)self; struct imc_attach_args *iaa = (struct imc_attach_args *)aux; struct gio_attach_args ga; uint32_t gfx[GIO_MAX_FB], id; uint i, j, ngfx; int sys_type; printf("\n"); sc->sc_iot = iaa->iaa_st; sc->sc_dmat = iaa->iaa_dmat; switch (sys_config.system_type) { case SGI_IP20: sys_type = SGI_IP20; break; default: case SGI_IP22: case SGI_IP26: case SGI_IP28: sys_type = SGI_IP22; break; } ngfx = 0; memset(gfx, 0, sizeof(gfx)); /* * Try and attach graphics devices first. * Unfortunately, they - not being GIO devices after all (except for * Impact) - do not contain a Product Identification Word, nor have * a slot number. * * Record addresses to which graphics devices attach so that * we do not confuse them with expansion slots, should the * addresses coincide. * * If only the ARCBios component tree would be so kind as to give * us the address of the frame buffer components... */ if (sys_type != SGI_IP22 || sys_config.system_subtype != IP22_CHALLS) { for (i = 0; gfx_bases[i].base != 0; i++) { /* skip slots that don't apply to us */ if (gfx_bases[i].mach_type != sys_type) continue; if (gfx_bases[i].mach_subtype != -1 && gfx_bases[i].mach_subtype != sys_config.system_subtype) continue; ga.ga_addr = gfx_bases[i].base; ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); /* no need to probe a glass console again */ if (ga.ga_addr == giofb_consaddr && giofb_consid != 0) id = giofb_consid; else { id = gio_id(ga.ga_ioh, ga.ga_addr, 1); if (!gio_is_framebuffer_id(id)) continue; } ga.ga_iot = sc->sc_iot; ga.ga_dmat = sc->sc_dmat; ga.ga_slot = -1; ga.ga_product = id; /* * Note that this relies upon ARCBios listing frame * buffers in ascending address order, which seems * to be the case so far on multihead Indigo2 systems. */ if (ngfx < GIO_MAX_FB) ga.ga_descr = giofb_names[ngfx]; else ga.ga_descr = NULL; /* shouldn't happen */ if (config_found_sm(self, &ga, gio_print_fb, gio_submatch)) gfx[ngfx] = gfx_bases[i].base; ngfx++; } } /* * Now attach any GIO expansion cards. * * Be sure to skip any addresses to which a graphics device has * already been attached. */ for (i = 0; slot_bases[i].base != 0; i++) { int skip = 0; /* skip slots that don't apply to us */ if (slot_bases[i].mach_type != sys_type) continue; if (slot_bases[i].mach_subtype != -1 && slot_bases[i].mach_subtype != sys_config.system_subtype) continue; for (j = 0; j < ngfx; j++) { if (slot_bases[i].base == gfx[j]) { skip = 1; break; } } if (skip) continue; ga.ga_addr = slot_bases[i].base; ga.ga_iot = sc->sc_iot; ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); id = gio_id(ga.ga_ioh, ga.ga_addr, 0); if (id == 0) continue; ga.ga_dmat = sc->sc_dmat; ga.ga_slot = slot_bases[i].slot; ga.ga_product = id; ga.ga_descr = NULL; config_found_sm(self, &ga, gio_print, gio_submatch); } config_search(gio_search, self, aux); } /* * Try and figure out whether there is a device at the given slot address. */ uint32_t gio_id(vaddr_t va, paddr_t pa, int maybe_gfx) { uint32_t id32, mystery; uint16_t id16 = 0; uint8_t id8 = 0; /* * First, attempt to read the address with various sizes. * * - GIO32 devices will only support reads from 32-bit aligned * addresses, in all sizes (at least for the ID register). * - frame buffers will support aligned reads from any size at * any address, but will actually return the access width if * the slot is pipelined. */ if (guarded_read_4(va, &id32) != 0) return 0; /* * If the address doesn't match a base slot address, then we are * only probing for a light(4) frame buffer. */ if (pa != GIO_ADDR_GFX && pa != GIO_ADDR_EXP0 && pa != GIO_ADDR_EXP1) { if (maybe_gfx == 0) return 0; else { if (pa == LIGHT_ADDR_0 || pa == LIGHT_ADDR_1) { if (guarded_read_4(va + REX_PAGE1_SET + REX_P1REG_XYOFFSET, &id32) != 0) return 0; if (id32 == 0x08000800) return GIO_PRODUCT_FAKEID_LIGHT; } return 0; } } /* * GIO32 devices with a 32-bit ID register will not necessarily * answer to addresses not aligned on 32 bit boundaries. */ if (guarded_read_2(va | 2, &id16) != 0 || guarded_read_1(va | 3, &id8) != 0) { if (GIO_PRODUCT_32BIT_ID(id32)) return id32; else /* not a frame buffer anyway */ return GIO_PRODUCT_PRODUCTID(id32); } /* * Of course, GIO32 devices with a 8-bit ID register can use the * other bytes in the first 32-bit word for other purposes. */ if ((id32 & 0xffff) == id16 && (id32 & 0xff) == id8) { if (GIO_PRODUCT_32BIT_ID(id32)) return id32; else if (!GIO_PRODUCT_32BIT_ID(id8) && id8 != 0x00) return /*GIO_PRODUCT_PRODUCTID*/(id8); } /* * If there is a frame buffer device, then either we have hit a * device register (grtwo), or we did not fault because the slot * is pipelined (newport). * In the latter case, we attempt to probe a known register offset. */ if (maybe_gfx) { /* * On (at least) Indy systems with newport graphics, the * presence of a SCSI Expansion board (030-8133) in either * slot will cause extra bits to be set in the topmost byte * of the 32-bit access to the pipelined slot (i.e. the * value of id32 is 0x18000004, not 0x00000004). * * This would prevent newport from being recognized * properly. * * This behaviour seems to be specific to the SCSI board, * since the E++ board does not trigger it. This would * rule out an HPC1.x-specific cause. * * We work around this by ignoring the topmost byte of id32 * from this point on, but it's ugly and isaish... * * Note that this is not necessary on Indigo 2 since this * troublesome board can not be installed on such a system. * Indigo are probably safe from this issues, for they can't * use newport graphics; but the issue at hand might be * HPC 1.x related, so better play safe. */ if (sys_config.system_type == SGI_IP20 || (sys_config.system_type == SGI_IP22 && sys_config.system_subtype != IP22_INDIGO2)) id32 &= ~0xff000000; if (id32 != 4 || id16 != 2 || id8 != 1) { if (guarded_read_4(va + HQ2_MYSTERY, &mystery) == 0 && mystery == HQ2_MYSTERY_VALUE) return GIO_PRODUCT_FAKEID_GRTWO; else return 0; } /* could be newport(4) */ if (pa == GIO_ADDR_GFX || pa == GIO_ADDR_EXP0) { va += NEWPORT_REX3_OFFSET; if (guarded_read_4(va, &id32) == 0 && guarded_read_2(va | 2, &id16) == 0 && guarded_read_1(va | 3, &id8) == 0) { if (id32 != 4 || id16 != 2 || id8 != 1) return GIO_PRODUCT_FAKEID_NEWPORT; } } return 0; } return 0; } int gio_print(void *aux, const char *pnp) { struct gio_attach_args *ga = aux; const char *descr; int product, revision; uint i; product = GIO_PRODUCT_PRODUCTID(ga->ga_product); if (GIO_PRODUCT_32BIT_ID(ga->ga_product)) revision = GIO_PRODUCT_REVISION(ga->ga_product); else revision = 0; descr = "unknown GIO card"; for (i = 0; gio_knowndevs[i].productid != 0; i++) { if (gio_knowndevs[i].productid == product) { descr = gio_knowndevs[i].product; break; } } if (pnp != NULL) { printf("%s", descr); if (ga->ga_product != -1) printf(" (product 0x%02x revision 0x%02x)", product, revision); printf(" at %s", pnp); } if (ga->ga_slot != -1) printf(" slot %d", ga->ga_slot); printf(" addr 0x%llx", ga->ga_addr); return UNCONF; } int gio_print_fb(void *aux, const char *pnp) { struct gio_attach_args *ga = aux; const char *fbname; if (pnp != NULL) { switch (ga->ga_product) { case GIO_PRODUCT_FAKEID_GRTWO: fbname = "grtwo"; break; case GIO_PRODUCT_FAKEID_LIGHT: fbname = "light"; break; case GIO_PRODUCT_FAKEID_NEWPORT: fbname = "newport"; break; default: if (GIO_PRODUCT_32BIT_ID(ga->ga_product) && GIO_PRODUCT_PRODUCTID(ga->ga_product) == GIO_PRODUCT_IMPACT) fbname = "impact"; else /* should never happen */ fbname = "framebuffer"; break; } printf("%s at %s", fbname, pnp); } printf(" addr 0x%llx", ga->ga_addr); return UNCONF; } int gio_search(struct device *parent, void *vcf, void *aux) { struct gio_softc *sc = (struct gio_softc *)parent; struct cfdata *cf = (struct cfdata *)vcf; struct gio_attach_args ga; /* Handled by direct configuration, so skip here */ if (cf->cf_loc[1 /*GIOCF_ADDR*/] == -1) return 0; ga.ga_addr = (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/]; ga.ga_iot = sc->sc_iot; ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); ga.ga_dmat = sc->sc_dmat; ga.ga_slot = cf->cf_loc[0 /*GIOCF_SLOT*/]; ga.ga_product = -1; ga.ga_descr = NULL; if ((*cf->cf_attach->ca_match)(parent, cf, &ga) == 0) return 0; config_attach(parent, cf, &ga, gio_print); return 1; } int gio_submatch(struct device *parent, void *vcf, void *aux) { struct cfdata *cf = (struct cfdata *)vcf; struct gio_attach_args *ga = (struct gio_attach_args *)aux; if (cf->cf_loc[0 /*GIOCF_SLOT*/] != -1 && cf->cf_loc[0 /*GIOCF_SLOT*/] != ga->ga_slot) return 0; if (cf->cf_loc[1 /*GIOCF_ADDR*/] != -1 && (uint64_t)cf->cf_loc[1 /*GIOCF_ADDR*/] != ga->ga_addr) return 0; return (*cf->cf_attach->ca_match)(parent, cf, aux); } int giofb_cnprobe() { struct gio_attach_args ga; uint32_t id; int i; int sys_type; switch (sys_config.system_type) { case SGI_IP20: sys_type = SGI_IP20; break; default: case SGI_IP22: case SGI_IP26: case SGI_IP28: sys_type = SGI_IP22; break; } for (i = 0; gfx_bases[i].base != 0; i++) { if (giofb_consaddr != 0 && gfx_bases[i].base != giofb_consaddr) continue; /* skip bases that don't apply to us */ if (gfx_bases[i].mach_type != sys_type) continue; if (gfx_bases[i].mach_subtype != -1 && gfx_bases[i].mach_subtype != sys_config.system_subtype) continue; ga.ga_addr = gfx_bases[i].base; ga.ga_iot = &imcbus_tag; ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); ga.ga_dmat = &imc_bus_dma_tag; ga.ga_slot = -1; ga.ga_descr = NULL; id = gio_id(ga.ga_ioh, ga.ga_addr, 1); if (!gio_is_framebuffer_id(id)) continue; ga.ga_product = giofb_consid = id; switch (id) { default: #if NIMPACT_GIO > 0 if (GIO_PRODUCT_32BIT_ID(id) && GIO_PRODUCT_PRODUCTID(id) == GIO_PRODUCT_IMPACT) { if (impact_gio_cnprobe(&ga) != 0) return 0; } #endif break; case GIO_PRODUCT_FAKEID_GRTWO: #if NGRTWO > 0 if (grtwo_cnprobe(&ga) != 0) return 0; #endif break; case GIO_PRODUCT_FAKEID_LIGHT: #if NLIGHT > 0 if (light_cnprobe(&ga) != 0) return 0; #endif break; case GIO_PRODUCT_FAKEID_NEWPORT: #if NNEWPORT > 0 if (newport_cnprobe(&ga) != 0) return 0; #endif break; } } return ENXIO; } int giofb_cnattach() { struct gio_attach_args ga; ga.ga_addr = giofb_consaddr; ga.ga_iot = &imcbus_tag; ga.ga_ioh = PHYS_TO_XKPHYS(ga.ga_addr, CCA_NC); ga.ga_dmat = &imc_bus_dma_tag; ga.ga_slot = -1; ga.ga_product = giofb_consid; ga.ga_descr = NULL; switch (giofb_consid) { default: #if NIMPACT_GIO > 0 if (GIO_PRODUCT_32BIT_ID(giofb_consid) && GIO_PRODUCT_PRODUCTID(giofb_consid) == GIO_PRODUCT_IMPACT) { if (impact_gio_cnattach(&ga) == 0) return 0; } #endif break; case GIO_PRODUCT_FAKEID_GRTWO: #if NGRTWO > 0 if (grtwo_cnattach(&ga) == 0) return 0; #endif break; case GIO_PRODUCT_FAKEID_LIGHT: #if NLIGHT > 0 if (light_cnattach(&ga) == 0) return 0; #endif break; case GIO_PRODUCT_FAKEID_NEWPORT: #if NNEWPORT > 0 if (newport_cnattach(&ga) == 0) return 0; #endif break; } giofb_consaddr = 0; return ENXIO; } int gio_is_framebuffer_id(uint32_t id) { switch (id) { case GIO_PRODUCT_FAKEID_GRTWO: case GIO_PRODUCT_FAKEID_LIGHT: case GIO_PRODUCT_FAKEID_NEWPORT: return 1; default: if (GIO_PRODUCT_32BIT_ID(id) && GIO_PRODUCT_PRODUCTID(id) == GIO_PRODUCT_IMPACT) return 1; else return 0; } } /* * Devices living in the expansion slots must enable or disable some * GIO arbiter settings. This is accomplished via imc(4) registers. */ int gio_arb_config(int slot, uint32_t flags) { if (flags == 0) return (EINVAL); if (flags & ~(GIO_ARB_RT | GIO_ARB_LB | GIO_ARB_MST | GIO_ARB_SLV | GIO_ARB_PIPE | GIO_ARB_NOPIPE | GIO_ARB_32BIT | GIO_ARB_64BIT | GIO_ARB_HPC2_32BIT | GIO_ARB_HPC2_64BIT)) return (EINVAL); if (((flags & GIO_ARB_RT) && (flags & GIO_ARB_LB)) || ((flags & GIO_ARB_MST) && (flags & GIO_ARB_SLV)) || ((flags & GIO_ARB_PIPE) && (flags & GIO_ARB_NOPIPE)) || ((flags & GIO_ARB_32BIT) && (flags & GIO_ARB_64BIT)) || ((flags & GIO_ARB_HPC2_32BIT) && (flags & GIO_ARB_HPC2_64BIT))) return (EINVAL); return (imc_gio64_arb_config(slot, flags)); } /* * Return the logical interrupt number for an expansion board (not a frame * buffer!) in the specified slot. * * Indy and Challenge S have a single GIO interrupt per GIO slot, but * distinct slot interrups. Indigo and Indigo2 have three GIO interrupts per * slot, but at a given GIO interrupt level, all slots share the same * interrupt on the interrupt controller. * * Expansion boards appear to always use the intermediate level. */ int gio_intr_map(int slot) { switch (sys_config.system_type) { case SGI_IP20: if (slot == GIO_SLOT_GFX) return -1; return INT2_L0_INTR(INT2_L0_GIO_LVL1); case SGI_IP22: case SGI_IP26: case SGI_IP28: if (sys_config.system_subtype == IP22_INDIGO2) { if (slot == GIO_SLOT_EXP1) return -1; return INT2_L0_INTR(INT2_L0_GIO_LVL1); } else { if (slot == GIO_SLOT_GFX) return -1; return INT2_MAP1_INTR(slot == GIO_SLOT_EXP0 ? INT2_MAP_GIO_SLOT0 : INT2_MAP_GIO_SLOT1); } default: return -1; } } void * gio_intr_establish(int intr, int level, int (*func)(void *), void *arg, const char *what) { return int2_intr_establish(intr, level, func, arg, what); } const char * gio_product_string(int prid) { int i; for (i = 0; gio_knowndevs[i].product != NULL; i++) if (gio_knowndevs[i].productid == prid) return (gio_knowndevs[i].product); return (NULL); }