/* $NetBSD: sbus.c,v 1.10 1996/04/22 02:35:03 abrown Exp $ */ /* * Copyright (c) 1992, 1993 * The Regents of the University of California. All rights reserved. * * This software was developed by the Computer Systems Engineering group * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and * contributed to Berkeley. * * All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Lawrence Berkeley Laboratory. * * 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 the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``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 REGENTS OR CONTRIBUTORS 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. * * @(#)sbus.c 8.1 (Berkeley) 6/11/93 */ /* * Sbus stuff. */ #include #include #include #include #include #include int sbus_print __P((void *, char *)); void sbusreset __P((int)); /* autoconfiguration driver */ void sbus_attach __P((struct device *, struct device *, void *)); int sbus_match __P((struct device *, void *, void *)); struct cfattach sbus_ca = { sizeof(struct sbus_softc), sbus_match, sbus_attach }; struct cfdriver sbus_cd = { NULL, "sbus", DV_DULL }; /* * Print the location of some sbus-attached device (called just * before attaching that device). If `sbus' is not NULL, the * device was found but not configured; print the sbus as well. * Return UNCONF (config_find ignores this if the device was configured). */ int sbus_print(args, sbus) void *args; char *sbus; { register struct confargs *ca = args; if (sbus) printf("%s at %s", ca->ca_ra.ra_name, sbus); printf(" slot %d offset 0x%x", ca->ca_slot, ca->ca_offset); return (UNCONF); } int sbus_match(parent, vcf, aux) struct device *parent; void *vcf, *aux; { struct cfdata *cf = vcf; register struct confargs *ca = aux; register struct romaux *ra = &ca->ca_ra; if (CPU_ISSUN4) return (0); return (strcmp(cf->cf_driver->cd_name, ra->ra_name) == 0); } /* * Attach an Sbus. */ void sbus_attach(parent, self, aux) struct device *parent; struct device *self; void *aux; { register struct sbus_softc *sc = (struct sbus_softc *)self; struct confargs *ca = aux; register struct romaux *ra = &ca->ca_ra; register int node; register char *name; struct confargs oca; /* * XXX there is only one Sbus, for now -- do not know how to * address children on others */ if (sc->sc_dev.dv_unit > 0) { printf(" unsupported\n"); return; } /* * Record clock frequency for synchronous SCSI. * IS THIS THE CORRECT DEFAULT?? */ node = ra->ra_node; sc->sc_clockfreq = getpropint(node, "clock-frequency", 25*1000*1000); printf(": clock = %s MHz\n", clockfreq(sc->sc_clockfreq)); /* * Get the SBus burst transfer size if burst transfers are supported */ sc->sc_burst = getpropint(node, "burst-sizes", 0); if (ra->ra_bp != NULL && strcmp(ra->ra_bp->name, "sbus") == 0) oca.ca_ra.ra_bp = ra->ra_bp + 1; else oca.ca_ra.ra_bp = NULL; sc->sc_range = ra->ra_range; sc->sc_nrange = ra->ra_nrange; /* * Loop through ROM children, fixing any relative addresses * and then configuring each device. */ for (node = firstchild(node); node; node = nextsibling(node)) { name = getpropstring(node, "name"); if (!romprop(&oca.ca_ra, name, node)) continue; sbus_translate(self, &oca); oca.ca_bustype = BUS_SBUS; (void) config_found(&sc->sc_dev, (void *)&oca, sbus_print); } } void sbus_translate(dev, ca) struct device *dev; struct confargs *ca; { struct sbus_softc *sc = (struct sbus_softc *)dev; register int base, slot; register int i; if (sc->sc_nrange == 0) { /* Old-style Sbus configuration */ base = (int)ca->ca_ra.ra_paddr; if (SBUS_ABS(base)) { ca->ca_slot = SBUS_ABS_TO_SLOT(base); ca->ca_offset = SBUS_ABS_TO_OFFSET(base); } else { ca->ca_slot = slot = ca->ca_ra.ra_iospace; ca->ca_offset = base; ca->ca_ra.ra_paddr = (void *)SBUS_ADDR(slot, base); /* Fix any remaining register banks */ for (i = 1; i < ca->ca_ra.ra_nreg; i++) { base = (int)ca->ca_ra.ra_reg[i].rr_paddr; ca->ca_ra.ra_reg[i].rr_paddr = (void *)SBUS_ADDR(slot, base); } } } else { ca->ca_slot = ca->ca_ra.ra_iospace; ca->ca_offset = (int)ca->ca_ra.ra_paddr; /* Translate into parent address spaces */ for (i = 0; i < ca->ca_ra.ra_nreg; i++) { int j, cspace = ca->ca_ra.ra_reg[i].rr_iospace; for (j = 0; j < sc->sc_nrange; j++) { if (sc->sc_range[j].cspace == cspace) { (int)ca->ca_ra.ra_reg[i].rr_paddr += sc->sc_range[j].poffset; (int)ca->ca_ra.ra_reg[i].rr_iospace = sc->sc_range[j].pspace; break; } } } } } /* * Each attached device calls sbus_establish after it initializes * its sbusdev portion. */ void sbus_establish(sd, dev) register struct sbusdev *sd; register struct device *dev; { register struct sbus_softc *sc; register struct device *curdev; /* * We have to look for the sbus by name, since it is not necessarily * our immediate parent (i.e. sun4m /iommu/sbus/espdma/esp) * We don't just use the device structure of the above-attached * sbus, since we might (in the future) support multiple sbus's. */ for (curdev = dev->dv_parent; ; curdev = curdev->dv_parent) { if (!curdev || !curdev->dv_xname) panic("sbus_establish: can't find sbus parent for %s", (sd->sd_dev->dv_xname ? sd->sd_dev->dv_xname : "")); if (strncmp(curdev->dv_xname, "sbus", 4) == 0) break; } sc = (struct sbus_softc *) curdev; sd->sd_dev = dev; sd->sd_bchain = sc->sc_sbdev; sc->sc_sbdev = sd; } /* * Reset the given sbus. (???) */ void sbusreset(sbus) int sbus; { register struct sbusdev *sd; struct sbus_softc *sc = sbus_cd.cd_devs[sbus]; struct device *dev; printf("reset %s:", sc->sc_dev.dv_xname); for (sd = sc->sc_sbdev; sd != NULL; sd = sd->sd_bchain) { if (sd->sd_reset) { dev = sd->sd_dev; (*sd->sd_reset)(dev); printf(" %s", dev->dv_xname); } } }