diff options
Diffstat (limited to 'sys/arch/sgi/hpc')
-rw-r--r-- | sys/arch/sgi/hpc/dpclock.c | 312 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/dsclock.c | 192 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/files.hpc | 56 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/hpc.c | 822 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/hpcdma.c | 208 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/hpcdma.h | 63 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/hpcreg.h | 450 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/hpcvar.h | 109 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/if_sq.c | 1348 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/if_sqvar.h | 200 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/iocreg.h | 122 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/wdsc.c | 290 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/z8530sc.c | 409 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/z8530sc.h | 199 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/z8530tty.c | 1663 | ||||
-rw-r--r-- | sys/arch/sgi/hpc/zs.c | 712 |
16 files changed, 7155 insertions, 0 deletions
diff --git a/sys/arch/sgi/hpc/dpclock.c b/sys/arch/sgi/hpc/dpclock.c new file mode 100644 index 00000000000..600c3ede567 --- /dev/null +++ b/sys/arch/sgi/hpc/dpclock.c @@ -0,0 +1,312 @@ +/* $OpenBSD: dpclock.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dpclock.c,v 1.3 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) 2001 Erik Reid + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Christopher Sekiya + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * 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. 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. + */ +/* + * Copyright (c) 1988 University of Utah. + * Copyright (c) 1982, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * the Systems Programming Group of the University of Utah Computer + * Science Department. + * + * 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. 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. + * + * from: Utah $Hdr: clock.c 1.18 91/01/21$ + * + * @(#)clock.c 8.2 (Berkeley) 1/12/94 + */ + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <mips64/archtype.h> +#include <mips64/dev/clockvar.h> + +#include <dev/ic/dp8573areg.h> +#include <sgi/hpc/hpcvar.h> + +#define IRIX_BASE_YEAR 1940 + +struct dpclock_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int dpclock_match(struct device *, void *, void *); +void dpclock_attach(struct device *, struct device *, void *); + +struct cfdriver dpclock_cd = { + NULL, "dpclock", DV_DULL +}; + +const struct cfattach dpclock_ca = { + sizeof(struct dpclock_softc), dpclock_match, dpclock_attach +}; + +#define dpclock_read(sc,r) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3) +#define dpclock_write(sc,r,v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3, (v)) + +static inline int frombcd(int); +static inline int tobcd(int); +static inline int leapyear(int year); + +static inline int +frombcd(int x) +{ + return (x >> 4) * 10 + (x & 0xf); +} +static inline int +tobcd(int x) +{ + return (x / 10 * 16) + (x % 10); +} +/* + * This inline avoids some unnecessary modulo operations + * as compared with the usual macro: + * ( ((year % 4) == 0 && + * (year % 100) != 0) || + * ((year % 400) == 0) ) + * It is otherwise equivalent. + * (borrowed from kern/clock_subr.c) + */ +static inline int +leapyear(int year) +{ + int rv = 0; + + if ((year & 3) == 0) { + rv = 1; + if ((year % 100) == 0) { + rv = 0; + if ((year % 400) == 0) + rv = 1; + } + } + return (rv); +} + +void dpclock_gettime(void *, time_t, struct tod_time *); +void dpclock_settime(void *, struct tod_time *); + +int +dpclock_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + + if (strcmp(haa->ha_name, dpclock_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +dpclock_attach(struct device *parent, struct device *self, void *aux) +{ + struct dpclock_softc *sc = (void *)self; + struct hpc_attach_args *haa = aux; + uint8_t st, r; + + sc->sc_iot = haa->ha_st; + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 4 * DP8573A_NREG, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + if ((r & DP8573A_RT_MODE_CLKSS) == 0) { + printf(": clock stopped"); + dpclock_write(sc, DP8573A_RT_MODE, r | DP8573A_RT_MODE_CLKSS); + dpclock_write(sc, DP8573A_INT0_CTL, 0); + dpclock_write(sc, DP8573A_INT1_CTL, DP8573A_INT1_CTL_PWRINT); + } + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_PFLAG); + if (r & DP8573A_PFLAG_TESTMODE) { + dpclock_write(sc, DP8573A_RAM_1F, 0); + dpclock_write(sc, DP8573A_PFLAG, r & ~DP8573A_PFLAG_TESTMODE); + } + + printf("\n"); + + sys_tod.tod_get = dpclock_gettime; + sys_tod.tod_set = dpclock_settime; + sys_tod.tod_cookie = self; +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dpclock_gettime(void *cookie, time_t base, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint8_t regs[DP8573A_NREG]; + + i = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, i); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + ct->sec = frombcd(regs[DP8573A_SAVE_SEC]); + ct->min = frombcd(regs[DP8573A_SAVE_MIN]); + + if (regs[DP8573A_RT_MODE] & DP8573A_RT_MODE_1224) { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_12HR_MASK) + + ((regs[DP8573A_SAVE_HOUR] & DP8573A_RT_MODE_1224) ? 0 : 12); + + /* + * In AM/PM mode, hour range is 01-12, so adding in 12 hours + * for PM gives us 01-24, whereas we want 00-23, so map hour + * 24 to hour 0. + */ + if (ct->hour == 24) + ct->hour = 0; + } else { + ct->hour = frombcd(regs[DP8573A_SAVE_HOUR] & + DP8573A_HOUR_24HR_MASK); + } + + ct->day = frombcd(regs[DP8573A_SAVE_DOM]); + ct->mon = frombcd(regs[DP8573A_SAVE_MONTH]); + ct->year = frombcd(regs[DP8573A_YEAR]) + (IRIX_BASE_YEAR - 1900); +} + +/* + * Reset the TODR based on the time value. + */ +void +dpclock_settime(void *cookie, struct tod_time *ct) +{ + struct dpclock_softc *sc = (void *)cookie; + uint i; + uint st, r, delta; + uint8_t regs[DP8573A_NREG]; + + r = dpclock_read(sc, DP8573A_TIMESAVE_CTL); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r | DP8573A_TIMESAVE_CTL_EN); + dpclock_write(sc, DP8573A_TIMESAVE_CTL, r); + + for (i = 0; i < DP8573A_NREG; i++) + regs[i] = dpclock_read(sc, i); + + regs[DP8573A_SUBSECOND] = 0; + regs[DP8573A_SECOND] = tobcd(ct->sec); + regs[DP8573A_MINUTE] = tobcd(ct->min); + regs[DP8573A_HOUR] = tobcd(ct->hour) & DP8573A_HOUR_24HR_MASK; + regs[DP8573A_DOW] = tobcd(ct->dow); + regs[DP8573A_DOM] = tobcd(ct->day); + regs[DP8573A_MONTH] = tobcd(ct->mon); + regs[DP8573A_YEAR] = tobcd(ct->year - (IRIX_BASE_YEAR - 1900)); + + st = dpclock_read(sc, DP8573A_STATUS); + dpclock_write(sc, DP8573A_STATUS, st | DP8573A_STATUS_REGSEL); + r = dpclock_read(sc, DP8573A_RT_MODE); + dpclock_write(sc, DP8573A_RT_MODE, r & ~DP8573A_RT_MODE_CLKSS); + + for (i = 0; i < 10; i++) + dpclock_write(sc, DP8573A_COUNTERS + i, + regs[DP8573A_COUNTERS + i]); + + /* + * We now need to set the leap year counter to the correct value. + * Unfortunately it is only two bits wide, while eight years can + * happen between two leap years. Skirting this is left as an + * exercise to the reader with an Indigo in working condition + * by year 2100. + */ + delta = 0; + while (delta < 3 && !leapyear(ct->year - delta)) + delta++; + + r &= ~(DP8573A_RT_MODE_LYLSB | DP8573A_RT_MODE_LYMSB); + dpclock_write(sc, DP8573A_RT_MODE, r | delta | DP8573A_RT_MODE_CLKSS); + + dpclock_write(sc, DP8573A_STATUS, st & ~DP8573A_STATUS_REGSEL); +} diff --git a/sys/arch/sgi/hpc/dsclock.c b/sys/arch/sgi/hpc/dsclock.c new file mode 100644 index 00000000000..396d68ec9d9 --- /dev/null +++ b/sys/arch/sgi/hpc/dsclock.c @@ -0,0 +1,192 @@ +/* $OpenBSD: dsclock.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: dsclock.c,v 1.5 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Christopher Sekiya + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * 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. 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 <sys/param.h> +#include <sys/kernel.h> +#include <sys/systm.h> +#include <sys/device.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> + +#include <mips64/archtype.h> +#include <mips64/dev/clockvar.h> + +#include <dev/ic/ds1286reg.h> +#include <sgi/hpc/hpcvar.h> + +#define IRIX_BASE_YEAR 1940 + +struct dsclock_softc { + struct device sc_dev; + + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +int dsclock_match(struct device *, void *, void *); +void dsclock_attach(struct device *, struct device *, void *); + +struct cfdriver dsclock_cd = { + NULL, "dsclock", DV_DULL +}; + +const struct cfattach dsclock_ca = { + sizeof(struct dsclock_softc), dsclock_match, dsclock_attach +}; + +#define ds1286_read(sc,r) \ + bus_space_read_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3) +#define ds1286_write(sc,r,v) \ + bus_space_write_1((sc)->sc_iot, (sc)->sc_ioh, ((r) << 2) + 3, (v)) + +static inline int frombcd(int); +static inline int tobcd(int); + +static inline int +frombcd(int x) +{ + return (x >> 4) * 10 + (x & 0xf); +} +static inline int +tobcd(int x) +{ + return (x / 10 * 16) + (x % 10); +} + +void dsclock_gettime(void *, time_t, struct tod_time *); +void dsclock_settime(void *, struct tod_time *); + +int +dsclock_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + + if (strcmp(haa->ha_name, dsclock_cd.cd_name) != 0) + return 0; + + return 1; +} + +void +dsclock_attach(struct device *parent, struct device *self, void *aux) +{ + struct dsclock_softc *sc = (void *)self; + struct hpc_attach_args *haa = aux; + + sc->sc_iot = haa->ha_st; + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_devoff, + 4 * 8192, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + printf("\n"); + + sys_tod.tod_get = dsclock_gettime; + sys_tod.tod_set = dsclock_settime; + sys_tod.tod_cookie = self; +} + +/* + * Get the time of day, based on the clock's value and/or the base value. + */ +void +dsclock_gettime(void *cookie, time_t base, struct tod_time *ct) +{ + struct dsclock_softc *sc = (void *)cookie; + ds1286_todregs regs; + int s; + + s = splhigh(); + DS1286_GETTOD(sc, ®s) + splx(s); + + ct->sec = frombcd(regs[DS1286_SEC]); + ct->min = frombcd(regs[DS1286_MIN]); + + if (regs[DS1286_HOUR] & DS1286_HOUR_12MODE) { + ct->hour = frombcd(regs[DS1286_HOUR] & DS1286_HOUR_12HR_MASK) + + ((regs[DS1286_HOUR] & DS1286_HOUR_12HR_PM) ? 12 : 0); + + /* + * In AM/PM mode, hour range is 01-12, so adding in 12 hours + * for PM gives us 01-24, whereas we want 00-23, so map hour + * 24 to hour 0. + */ + if (ct->hour == 24) + ct->hour = 0; + } else { + ct->hour = frombcd(regs[DS1286_HOUR] & DS1286_HOUR_24HR_MASK); + } + + ct->day = frombcd(regs[DS1286_DOM]); + ct->mon = frombcd(regs[DS1286_MONTH] & DS1286_MONTH_MASK); + ct->year = frombcd(regs[DS1286_YEAR]) + (IRIX_BASE_YEAR - 1900); +} + +/* + * Reset the TODR based on the time value. + */ +void +dsclock_settime(void *cookie, struct tod_time *ct) +{ + struct dsclock_softc *sc = (void *)cookie; + ds1286_todregs regs; + int s; + + s = splhigh(); + DS1286_GETTOD(sc, ®s); + splx(s); + + regs[DS1286_SUBSEC] = 0; + regs[DS1286_SEC] = tobcd(ct->sec); + regs[DS1286_MIN] = tobcd(ct->min); + regs[DS1286_HOUR] = tobcd(ct->hour) & DS1286_HOUR_24HR_MASK; + regs[DS1286_DOW] = tobcd(ct->dow); + regs[DS1286_DOM] = tobcd(ct->day); + + /* Leave wave-generator bits as set originally */ + regs[DS1286_MONTH] &= ~DS1286_MONTH_MASK; + regs[DS1286_MONTH] |= tobcd(ct->mon) & DS1286_MONTH_MASK; + + regs[DS1286_YEAR] = tobcd(ct->year - (IRIX_BASE_YEAR - 1900)); + + s = splhigh(); + DS1286_PUTTOD(sc, ®s); + splx(s); +} diff --git a/sys/arch/sgi/hpc/files.hpc b/sys/arch/sgi/hpc/files.hpc new file mode 100644 index 00000000000..4463f066a45 --- /dev/null +++ b/sys/arch/sgi/hpc/files.hpc @@ -0,0 +1,56 @@ +# $OpenBSD: files.hpc,v 1.1 2012/03/28 20:44:23 miod Exp $ +# $NetBSD: files.hpc,v 1.14 2009/05/14 01:10:19 macallan Exp $ + +# IP20 RTC +device dpclock +attach dpclock at hpc +file arch/sgi/hpc/dpclock.c dpclock + +# IP22/24 RTC +device dsclock +attach dsclock at hpc +file arch/sgi/hpc/dsclock.c dsclock + +device sq: ether, ifnet +attach sq at hpc +file arch/sgi/hpc/if_sq.c sq + +define hpcdma +file arch/sgi/hpc/hpcdma.c hpcdma + +device wdsc: wd33c93, scsi, hpcdma +attach wdsc at hpc +file arch/sgi/hpc/wdsc.c wdsc + +device haltwo: audio, auconv, mulaw +attach haltwo at hpc +file arch/sgi/hpc/haltwo.c haltwo + +device zs {[channel = -1]} +attach zs at hpc with zs_hpc +file arch/sgi/hpc/zs.c zs needs-flag +file arch/sgi/hpc/z8530sc.c zs + +device zstty: tty +attach zstty at zs +file arch/sgi/hpc/z8530tty.c zstty needs-flag + +device zskbd: wskbddev +attach zskbd at zs +file arch/sgi/hpc/zs_kbd.c zskbd needs-flag +file arch/sgi/dev/wskbdmap_sgi.c zskbd + +device zsms: wsmousedev +attach zsms at zs +file arch/sgi/hpc/zs_ms.c zsms + +attach pckbc at hpc with pckbc_hpc +file arch/sgi/hpc/pckbc_hpc.c pckbc_hpc + +#device pione +#attach pione at hpc +#file arch/sgi/hpc/pione.c pione + +device panel +attach panel at hpc +file arch/sgi/hpc/panel.c panel diff --git a/sys/arch/sgi/hpc/hpc.c b/sys/arch/sgi/hpc/hpc.c new file mode 100644 index 00000000000..ca2c31c06ed --- /dev/null +++ b/sys/arch/sgi/hpc/hpc.c @@ -0,0 +1,822 @@ +/* $OpenBSD: hpc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpc.c,v 1.66 2011/07/01 18:53:46 dyoung Exp $ */ +/* $NetBSD: ioc.c,v 1.9 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2003 Christopher Sekiya + * 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. + */ +/* + * Copyright (c) 2000 Soren S. Jorvang + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 2001 Jason R. Thorpe + * 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. + */ + +/* + * Combined driver for the HPC (High performance Peripheral Controller) + * and IOC2 (I/O Controller) chips. + * + * It would theoretically be better to attach an IOC driver to HPC on + * IOC systems (IP22/24/26/28), and attach the few onboard devices + * which attach directly to HPC on IP20, to IOC. But since IOC depends + * too much on HPC, the complexity this would introduce is not worth + * the hassle. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/timeout.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> + +#include <sgi/gio/gioreg.h> +#include <sgi/gio/giovar.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/iocreg.h> +#include <sgi/sgi/ip22.h> + +#include <dev/ic/smc93cx6var.h> + +struct hpc_device { + const char *hd_name; + bus_addr_t hd_base; + bus_addr_t hd_devoff; + bus_addr_t hd_dmaoff; + int hd_irq; + int hd_sysmask; +}; + +static const struct hpc_device hpc1_devices[] = { + /* probe order is important for IP20 zs */ + + { "zs", /* Indigo serial 0/1 duart 1 */ + HPC_BASE_ADDRESS_0, + 0x0d10, 0, + 5, + HPCDEV_IP20 }, + + { "zs", /* Indigo kbd/ms duart 0 */ + HPC_BASE_ADDRESS_0, + 0x0d00, 0, + 5, + HPCDEV_IP20 }, + + { "sq", /* Indigo onboard ethernet */ + HPC_BASE_ADDRESS_0, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 3, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 0 (Indigo) */ + HPC_BASE_ADDRESS_1, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 6, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 0 (Indy) */ + HPC_BASE_ADDRESS_1, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 16 + 6, + HPCDEV_IP24 }, + + { "sq", /* E++ GIO adapter slot 1 (Indigo) */ + HPC_BASE_ADDRESS_2, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 6, + HPCDEV_IP20 }, + + { "sq", /* E++ GIO adapter slot 1 (Indy/Challenge S) */ + HPC_BASE_ADDRESS_2, + HPC1_ENET_DEVREGS, HPC1_ENET_REGS, + 16 + 7, + HPCDEV_IP24 }, + + { "wdsc", /* Indigo onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 2, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 0 (Indigo) */ + HPC_BASE_ADDRESS_1, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 6, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 0 (Indy) */ + HPC_BASE_ADDRESS_1, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 16 + 6, + HPCDEV_IP24 }, + + { "wdsc", /* GIO32 SCSI adapter slot 1 (Indigo) */ + HPC_BASE_ADDRESS_2, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 6, + HPCDEV_IP20 }, + + { "wdsc", /* GIO32 SCSI adapter slot 1 (Indy/Challenge S) */ + HPC_BASE_ADDRESS_2, + HPC1_SCSI0_DEVREGS, HPC1_SCSI0_REGS, + 16 + 7, + HPCDEV_IP24 }, + + { NULL, + 0, + 0, 0, + 0, + 0 + } +}; + +static const struct hpc_device hpc3_devices[] = { + { "zs", /* serial 0/1 duart 0 */ + HPC_BASE_ADDRESS_0, + /* XXX Magic numbers */ + HPC3_PBUS_CH6_DEVREGS + IOC_SERIAL_REGS, 0, + 24 + 5, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "pckbc", /* Indigo2/Indy ps2 keyboard/mouse controller */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_KB_REGS, 0, + 24 + 4, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "sq", /* Indigo2/Indy/Challenge S/Challenge M onboard enet */ + HPC_BASE_ADDRESS_0, + HPC3_ENET_DEVREGS, HPC3_ENET_REGS, + 3, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "sq", /* Challenge S IOPLUS secondary ethernet */ + HPC_BASE_ADDRESS_1, + HPC3_ENET_DEVREGS, HPC3_ENET_REGS, + 0, + HPCDEV_IP24 }, + + { "wdsc", /* Indigo2/Indy/Challenge S/Challenge M onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC3_SCSI0_DEVREGS, HPC3_SCSI0_REGS, + 1, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "wdsc", /* Indigo2/Challenge M secondary onboard SCSI */ + HPC_BASE_ADDRESS_0, + HPC3_SCSI1_DEVREGS, HPC3_SCSI1_REGS, + 2, + HPCDEV_IP22 }, + + { "haltwo", /* Indigo2/Indy onboard audio */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH0_DEVREGS, HPC3_PBUS_DMAREGS, + 8 + 4, /* really the HPC DMA complete interrupt */ + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "pione", /* Indigo2/Indy/Challenge S/Challenge M onboard pport */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_PLP_REGS, 0, + 5, + HPCDEV_IP22 | HPCDEV_IP24 }, + + { "panel", /* Indy front panel */ + HPC_BASE_ADDRESS_0, + HPC3_PBUS_CH6_DEVREGS + IOC_PANEL, 0, + 9, + HPCDEV_IP24 }, + + { NULL, + 0, + 0, 0, + 0, + 0 + } +}; + +struct hpc_softc { + struct device sc_dev; + + bus_addr_t sc_base; + + bus_space_tag_t sc_ct; + bus_space_handle_t sc_ch; + bus_dma_tag_t sc_dmat; + + struct timeout sc_blink_tmo; +}; + +static struct hpc_values hpc1_values = { + .revision = 1, + .scsi0_regs = HPC1_SCSI0_REGS, + .scsi0_regs_size = HPC1_SCSI0_REGS_SIZE, + .scsi0_cbp = HPC1_SCSI0_CBP, + .scsi0_ndbp = HPC1_SCSI0_NDBP, + .scsi0_bc = HPC1_SCSI0_BC, + .scsi0_ctl = HPC1_SCSI0_CTL, + .scsi0_gio = HPC1_SCSI0_GIO, + .scsi0_dev = HPC1_SCSI0_DEV, + .scsi0_dmacfg = HPC1_SCSI0_DMACFG, + .scsi0_piocfg = HPC1_SCSI0_PIOCFG, + .scsi1_regs = 0, + .scsi1_regs_size = 0, + .scsi1_cbp = 0, + .scsi1_ndbp = 0, + .scsi1_bc = 0, + .scsi1_ctl = 0, + .scsi1_gio = 0, + .scsi1_dev = 0, + .scsi1_dmacfg = 0, + .scsi1_piocfg = 0, + .enet_regs = HPC1_ENET_REGS, + .enet_regs_size = HPC1_ENET_REGS_SIZE, + .enet_intdelay = HPC1_ENET_INTDELAY, + .enet_intdelayval = HPC1_ENET_INTDELAY_OFF, + .enetr_cbp = HPC1_ENETR_CBP, + .enetr_ndbp = HPC1_ENETR_NDBP, + .enetr_bc = HPC1_ENETR_BC, + .enetr_ctl = HPC1_ENETR_CTL, + .enetr_ctl_active = HPC1_ENETR_CTL_ACTIVE, + .enetr_reset = HPC1_ENETR_RESET, + .enetr_dmacfg = 0, + .enetr_piocfg = 0, + .enetx_cbp = HPC1_ENETX_CBP, + .enetx_ndbp = HPC1_ENETX_NDBP, + .enetx_bc = HPC1_ENETX_BC, + .enetx_ctl = HPC1_ENETX_CTL, + .enetx_ctl_active = HPC1_ENETX_CTL_ACTIVE, + .enetx_dev = 0, + .enetr_fifo = HPC1_ENETR_FIFO, + .enetr_fifo_size = HPC1_ENETR_FIFO_SIZE, + .enetx_fifo = HPC1_ENETX_FIFO, + .enetx_fifo_size = HPC1_ENETX_FIFO_SIZE, + .enet_dma_boundary = 4096, + .enet_devregs = HPC1_ENET_DEVREGS, + .enet_devregs_size = HPC1_ENET_DEVREGS_SIZE, + .pbus_fifo = 0, + .pbus_fifo_size = 0, + .pbus_bbram = 0, +#define MAX_SCSI_XFER (roundup(MAXPHYS, PAGE_SIZE)) + .scsi_dma_segs = (MAX_SCSI_XFER / 4096), + .scsi_dma_segs_size = 4096, + .scsi_dma_datain_cmd = (HPC1_SCSI_DMACTL_ACTIVE | HPC1_SCSI_DMACTL_DIR), + .scsi_dma_dataout_cmd = HPC1_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_flush = HPC1_SCSI_DMACTL_FLUSH, + .scsi_dmactl_active = HPC1_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_reset = HPC1_SCSI_DMACTL_RESET +}; + +static struct hpc_values hpc3_values = { + .revision = 3, + .scsi0_regs = HPC3_SCSI0_REGS, + .scsi0_regs_size = HPC3_SCSI0_REGS_SIZE, + .scsi0_cbp = HPC3_SCSI0_CBP, + .scsi0_ndbp = HPC3_SCSI0_NDBP, + .scsi0_bc = HPC3_SCSI0_BC, + .scsi0_ctl = HPC3_SCSI0_CTL, + .scsi0_gio = HPC3_SCSI0_GIO, + .scsi0_dev = HPC3_SCSI0_DEV, + .scsi0_dmacfg = HPC3_SCSI0_DMACFG, + .scsi0_piocfg = HPC3_SCSI0_PIOCFG, + .scsi1_regs = HPC3_SCSI1_REGS, + .scsi1_regs_size = HPC3_SCSI1_REGS_SIZE, + .scsi1_cbp = HPC3_SCSI1_CBP, + .scsi1_ndbp = HPC3_SCSI1_NDBP, + .scsi1_bc = HPC3_SCSI1_BC, + .scsi1_ctl = HPC3_SCSI1_CTL, + .scsi1_gio = HPC3_SCSI1_GIO, + .scsi1_dev = HPC3_SCSI1_DEV, + .scsi1_dmacfg = HPC3_SCSI1_DMACFG, + .scsi1_piocfg = HPC3_SCSI1_PIOCFG, + .enet_regs = HPC3_ENET_REGS, + .enet_regs_size = HPC3_ENET_REGS_SIZE, + .enet_intdelay = 0, + .enet_intdelayval = 0, + .enetr_cbp = HPC3_ENETR_CBP, + .enetr_ndbp = HPC3_ENETR_NDBP, + .enetr_bc = HPC3_ENETR_BC, + .enetr_ctl = HPC3_ENETR_CTL, + .enetr_ctl_active = HPC3_ENETR_CTL_ACTIVE, + .enetr_reset = HPC3_ENETR_RESET, + .enetr_dmacfg = HPC3_ENETR_DMACFG, + .enetr_piocfg = HPC3_ENETR_PIOCFG, + .enetx_cbp = HPC3_ENETX_CBP, + .enetx_ndbp = HPC3_ENETX_NDBP, + .enetx_bc = HPC3_ENETX_BC, + .enetx_ctl = HPC3_ENETX_CTL, + .enetx_ctl_active = HPC3_ENETX_CTL_ACTIVE, + .enetx_dev = HPC3_ENETX_DEV, + .enetr_fifo = HPC3_ENETR_FIFO, + .enetr_fifo_size = HPC3_ENETR_FIFO_SIZE, + .enetx_fifo = HPC3_ENETX_FIFO, + .enetx_fifo_size = HPC3_ENETX_FIFO_SIZE, + .enet_dma_boundary = 8192, + .enet_devregs = HPC3_ENET_DEVREGS, + .enet_devregs_size = HPC3_ENET_DEVREGS_SIZE, + .pbus_fifo = HPC3_PBUS_FIFO, + .pbus_fifo_size = HPC3_PBUS_FIFO_SIZE, + .pbus_bbram = HPC3_PBUS_BBRAM, + .scsi_dma_segs = (MAX_SCSI_XFER / 8192), + .scsi_dma_segs_size = 8192, + .scsi_dma_datain_cmd = HPC3_SCSI_DMACTL_ACTIVE, + .scsi_dma_dataout_cmd =(HPC3_SCSI_DMACTL_ACTIVE | HPC3_SCSI_DMACTL_DIR), + .scsi_dmactl_flush = HPC3_SCSI_DMACTL_FLUSH, + .scsi_dmactl_active = HPC3_SCSI_DMACTL_ACTIVE, + .scsi_dmactl_reset = HPC3_SCSI_DMACTL_RESET +}; + +int hpc_match(struct device *, void *, void *); +void hpc_attach(struct device *, struct device *, void *); +int hpc_print(void *, const char *); + +int hpc_revision(struct hpc_softc *, struct gio_attach_args *); +int hpc_submatch(struct device *, void *, void *); +int hpc_power_intr(void *); +void hpc_blink(void *); +void hpc_blink_ioc(void *); +int hpc_read_eeprom(int, bus_space_tag_t, bus_space_handle_t, uint8_t *, + size_t); + +const struct cfattach hpc_ca = { + sizeof(struct hpc_softc), hpc_match, hpc_attach +}; + +struct cfdriver hpc_cd = { + NULL, "hpc", DV_DULL +}; + +uint8_t hpc_read_1(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint16_t hpc_read_2(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void hpc_read_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_1(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint8_t); +void hpc_write_2(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint16_t); +void hpc_write_raw_2(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +uint32_t hpc_read_4(bus_space_tag_t, bus_space_handle_t, bus_size_t); +uint64_t hpc_read_8(bus_space_tag_t, bus_space_handle_t, bus_size_t); +void hpc_write_4(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint32_t); +void hpc_write_8(bus_space_tag_t, bus_space_handle_t, bus_size_t, uint64_t); +void hpc_read_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_raw_4(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +void hpc_read_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + uint8_t *, bus_size_t); +void hpc_write_raw_8(bus_space_tag_t, bus_space_handle_t, bus_addr_t, + const uint8_t *, bus_size_t); +int hpc_space_map(bus_space_tag_t, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); +void hpc_space_unmap(bus_space_tag_t, bus_space_handle_t, bus_size_t); +int hpc_space_region(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, bus_space_handle_t *); +void *hpc_space_vaddr(bus_space_tag_t, bus_space_handle_t); +void hpc_space_barrier(bus_space_tag_t, bus_space_handle_t, bus_size_t, + bus_size_t, int); + +int +hpc_match(struct device *parent, void *vcf, void *aux) +{ + struct gio_attach_args* ga = aux; + uint32_t dummy; + + /* Make sure it's actually there and readable */ + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr, CCA_NC), &dummy) == 0) + return 1; + + return 0; +} + +void +hpc_attach(struct device *parent, struct device *self, void *aux) +{ + struct hpc_softc *sc = (struct hpc_softc *)self; + struct gio_attach_args* ga = aux; + struct hpc_attach_args ha; + const struct hpc_device *hd; + uint32_t dummy; + uint32_t hpctype; + int isonboard; + int isioplus; + int sysmask = 0; + + sc->sc_base = ga->ga_addr; + sc->sc_ct = ga->ga_iot; + sc->sc_ch = PHYS_TO_XKPHYS(sc->sc_base, CCA_NC); + sc->sc_dmat = ga->ga_dmat; + + switch (sys_config.system_type) { + case SGI_IP20: + sysmask = HPCDEV_IP20; + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (sys_config.system_subtype == IP22_INDIGO2) + sysmask = HPCDEV_IP22; + else + sysmask = HPCDEV_IP24; + break; + }; + + if ((hpctype = hpc_revision(sc, ga)) == 0) { + printf(": could not identify HPC revision\n"); + return; + } + + /* force big-endian mode */ + if (hpctype == 15) + bus_space_write_4(sc->sc_ct, sc->sc_ch, HPC1_BIGENDIAN, 0); + + /* + * All machines have only one HPC on the mainboard itself. ''Extra'' + * HPCs require bus arbiter and other magic to run happily. + */ + isonboard = (sc->sc_base == HPC_BASE_ADDRESS_0); + isioplus = (sc->sc_base == HPC_BASE_ADDRESS_1 && hpctype == 3 && + sysmask == HPCDEV_IP24); + + printf(": SGI HPC%d%s (%s)\n", (hpctype == 3) ? 3 : 1, + (hpctype == 15) ? ".5" : "", (isonboard) ? "onboard" : + (isioplus) ? "IOPLUS mezzanine" : "GIO slot"); + + /* + * Configure the IOC. + */ + if (isonboard && sys_config.system_type != SGI_IP20) { + /* Reset IOC */ + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET, + IOC_RESET_PARALLEL | IOC_RESET_PCKBC | IOC_RESET_EISA | + IOC_RESET_ISDN | IOC_RESET_LED_GREEN ); + + /* + * Set the 10BaseT port to use UTP cable, set autoselect mode + * for the Ethernet interface (AUI vs. TP), set the two serial + * ports to PC mode. + */ + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_WRITE, + IOC_WRITE_ENET_AUTO | IOC_WRITE_ENET_UTP | + IOC_WRITE_PC_UART2 | IOC_WRITE_PC_UART1); + + /* XXX: the firmware should have taken care of this already */ +#if 0 + if (sys_config.system_subtype == IP22_INDY) { + bus_space_write_4(sc->sc_ct, sc->sc_ch, + IOC_BASE + IOC_GCSEL, 0xff); + bus_space_write_4(sc->sc_ct, sc->sc_ch, + IOC_BASE + IOC_GCREG, 0xff); + } +#endif + } + + /* + * Configure the bus arbiter appropriately. + * + * In the case of Challenge S, we must tell the IOPLUS board which + * DMA channel to use (we steal it from one of the slots). SGI allows + * an HPC1.5 in slot 1, in which case IOPLUS must use EXP0, or any + * other DMA-capable board in slot 0, which leaves us to use EXP1. Of + * course, this means that only one GIO board may use DMA. + * + * Note that this never happens on Indigo2. + */ + if (isioplus) { + int arb_slot; + + if (guarded_read_4(PHYS_TO_XKPHYS(HPC_BASE_ADDRESS_2, CCA_NC), + &dummy) != 0) + arb_slot = GIO_SLOT_EXP1; + else + arb_slot = GIO_SLOT_EXP0; + + if (gio_arb_config(arb_slot, GIO_ARB_LB | GIO_ARB_MST | + GIO_ARB_64BIT | GIO_ARB_HPC2_64BIT)) { + printf("%s: failed to configure GIO bus arbiter\n", + sc->sc_dev.dv_xname); + return; + } + + printf("%s: using EXP%d's DMA channel\n", + sc->sc_dev.dv_xname, + (arb_slot == GIO_SLOT_EXP0) ? 0 : 1); + + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CFGPIO_REGS, 0x0003ffff); + + if (arb_slot == GIO_SLOT_EXP0) + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CH0_DEVREGS, 0x20202020); + else + bus_space_write_4(sc->sc_ct, sc->sc_ch, + HPC3_PBUS_CH0_DEVREGS, 0x30303030); + } else if (!isonboard) { + int arb_slot; + + arb_slot = (sc->sc_base == HPC_BASE_ADDRESS_1) ? + GIO_SLOT_EXP0 : GIO_SLOT_EXP1; + + if (gio_arb_config(arb_slot, GIO_ARB_RT | GIO_ARB_MST)) { + printf("%s: failed to configure GIO bus arbiter\n", + sc->sc_dev.dv_xname); + return; + } + } + + hpc_read_eeprom(hpctype, sc->sc_ct, sc->sc_ch, + ha.hpc_eeprom, sizeof(ha.hpc_eeprom)); + + hd = hpctype == 3 ? hpc3_devices : hpc1_devices; + for (; hd->hd_name != NULL; hd++) { + if (!(hd->hd_sysmask & sysmask) || hd->hd_base != sc->sc_base) + continue; + + ha.ha_name = hd->hd_name; + ha.ha_devoff = hd->hd_devoff; + ha.ha_dmaoff = hd->hd_dmaoff; + ha.ha_irq = hd->hd_irq; + + ha.ha_st = sc->sc_ct; + ha.ha_sh = sc->sc_ch; + ha.ha_dmat = sc->sc_dmat; + if (hpctype == 3) + ha.hpc_regs = &hpc3_values; + else + ha.hpc_regs = &hpc1_values; + ha.hpc_regs->revision = hpctype; + + /* + * XXX On hpc@gio boards such as the E++, this will cause + * XXX `wdsc not configured' messages (or sq on SCSI + * XXX boards. We need to move some device detection + * XXX in there, or figure out if there is a way to know + * XXX what is really connected. + */ + config_found_sm(self, &ha, hpc_print, hpc_submatch); + } + + /* + * Attach the clock chip as well if on hpc0. + */ + if (isonboard) { + if (sys_config.system_type == SGI_IP20) { + ha.ha_name = "dpclock"; + ha.ha_devoff = HPC1_PBUS_BBRAM; + } else { + ha.ha_name = "dsclock"; + ha.ha_devoff = HPC3_PBUS_BBRAM; + } + ha.ha_dmaoff = 0; + ha.ha_irq = -1; + ha.ha_st = sc->sc_ct; + ha.ha_sh = sc->sc_ch; + ha.ha_dmat = sc->sc_dmat; + ha.hpc_regs = NULL; + + config_found_sm(self, &ha, hpc_print, hpc_submatch); + + if (sys_config.system_type == SGI_IP20) { + timeout_set(&sc->sc_blink_tmo, hpc_blink, sc); + hpc_blink(sc); + } else { + timeout_set(&sc->sc_blink_tmo, hpc_blink_ioc, sc); + hpc_blink_ioc(sc); + } + } +} + +/* + * HPC revision detection isn't as simple as it should be. Devices probe + * differently depending on their slots, but luckily there is only one + * instance in which we have to decide the major revision (HPC1 vs HPC3). + * + * The HPC is found in the following configurations: + * o Indigo R4k + * One on-board HPC1 or HPC1.5. + * Up to two additional HPC1.5's in GIO slots 0 and 1. + * o Indy + * One on-board HPC3. + * Up to two additional HPC1.5's in GIO slots 0 and 1. + * o Challenge S + * One on-board HPC3. + * Up to one additional HPC3 on the IOPLUS board (if installed). + * Up to one additional HPC1.5 in slot 1 of the IOPLUS board. + * o Indigo2, Challenge M + * One on-board HPC3. + * + * All we really have to worry about is the IP22 case. + */ +int +hpc_revision(struct hpc_softc *sc, struct gio_attach_args *ga) +{ + uint32_t reg; + + /* No hardware ever supported the last hpc base address. */ + if (ga->ga_addr == HPC_BASE_ADDRESS_3) + return 0; + + switch (sys_config.system_type) { + case SGI_IP20: + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr + HPC1_BIGENDIAN, + CCA_NC), ®) != 0) { + if (((reg >> HPC1_REVSHIFT) & HPC1_REVMASK) == + HPC1_REV15) + return 15; + else + return 1; + } + return 1; + + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + /* + * If IP22, probe slot 0 to determine if HPC1.5 or HPC3. Slot 1 + * must be HPC1.5. + */ + if (ga->ga_addr == HPC_BASE_ADDRESS_0) + return 3; + + if (ga->ga_addr == HPC_BASE_ADDRESS_2) + return 15; + + /* + * Probe for it. We use one of the PBUS registers. Note + * that this probe succeeds with my E++ adapter in slot 1 + * (bad), but it appears to always do the right thing in + * slot 0 (good!) and we're only worried about that one + * anyhow. + */ + if (guarded_read_4(PHYS_TO_XKPHYS(ga->ga_addr + + HPC3_PBUS_CH7_BP, CCA_NC), ®) != 0) + return 15; + else + return 3; + } + + return 0; +} + +int +hpc_submatch(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = (struct cfdata *)vcf; + struct hpc_attach_args *ha = (struct hpc_attach_args *)aux; + + if (cf->cf_loc[0 /*HPCCF_OFFSET*/] != -1 && + (bus_addr_t)cf->cf_loc[0 /*HPCCF_OFFSET*/] != ha->ha_devoff) + return 0; + + return (*cf->cf_attach->ca_match)(parent, cf, aux); +} + +int +hpc_print(void *aux, const char *pnp) +{ + struct hpc_attach_args *ha = aux; + + if (pnp) + printf("%s at %s", ha->ha_name, pnp); + + printf(" offset 0x%08lx", ha->ha_devoff); + if (ha->ha_irq >= 0) + printf(" irq %d", ha->ha_irq); + + return UNCONF; +} + +void +hpc_blink(void *arg) +{ + struct hpc_softc *sc = arg; + + bus_space_write_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS, + bus_space_read_1(sc->sc_ct, sc->sc_ch, HPC1_AUX_REGS) ^ + HPC1_AUX_CONSLED); + + timeout_add(&sc->sc_blink_tmo, + (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1))); +} + +void +hpc_blink_ioc(void *arg) +{ + struct hpc_softc *sc = arg; + uint32_t value; + + /* This is a bit odd. To strobe the green LED, we have to toggle the + red control bit. */ + value = bus_space_read_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET) & + 0xff; + value ^= IOC_RESET_LED_RED; + bus_space_write_4(sc->sc_ct, sc->sc_ch, IOC_BASE + IOC_RESET, value); + + timeout_add(&sc->sc_blink_tmo, + (((averunnable.ldavg[0] + FSCALE) * hz) >> (FSHIFT + 1))); +} + +/* + * Read the eeprom associated with one of the HPC's. + * + * NB: An eeprom is not always present, but the HPC should be able to + * handle this gracefully. Any consumers should validate the data to + * ensure it's reasonable. + */ +int +hpc_read_eeprom(int hpctype, bus_space_tag_t t, bus_space_handle_t h, + uint8_t *buf, size_t len) +{ + struct seeprom_descriptor sd; + bus_space_handle_t bsh; + bus_size_t offset; + + if (!len || len & 0x1) + return (1); + + offset = (hpctype == 3) ? HPC3_EEPROM_DATA : HPC1_AUX_REGS; + + if (bus_space_subregion(t, h, offset, 1, &bsh) != 0) + return (1); + + sd.sd_chip = C56_66; + sd.sd_tag = t; + sd.sd_bsh = bsh; + sd.sd_regsize = 1; + sd.sd_control_offset = 0; + sd.sd_status_offset = 0; + sd.sd_dataout_offset = 0; + sd.sd_DI = 0x10; /* EEPROM -> CPU */ + sd.sd_DO = 0x08; /* CPU -> EEPROM */ + sd.sd_CK = 0x04; + sd.sd_CS = 0x02; + sd.sd_MS = 0; + sd.sd_RDY = 0; + + if (read_seeprom(&sd, (uint16_t *)buf, 0, len / 2) != 1) + return (1); + + bus_space_unmap(t, bsh, 1); + + return 0; +} diff --git a/sys/arch/sgi/hpc/hpcdma.c b/sys/arch/sgi/hpc/hpcdma.c new file mode 100644 index 00000000000..53dc86994ce --- /dev/null +++ b/sys/arch/sgi/hpc/hpcdma.c @@ -0,0 +1,208 @@ +/* $OpenBSD: hpcdma.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcdma.c,v 1.21 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Support for SCSI DMA provided by the HPC. + * + * Note: We use SCSI0 offsets, etc. here. Since the layout of SCSI0 + * and SCSI1 are the same, this is no problem. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/buf.h> + +#include <uvm/uvm_extern.h> + +#include <machine/bus.h> + +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcdma.h> + +/* + * Allocate DMA Chain descriptor list + */ +void +hpcdma_init(struct hpc_attach_args *haa, struct hpc_dma_softc *sc, int ndesc) +{ + bus_dma_segment_t seg; + int rseg, allocsz; + + sc->sc_bst = haa->ha_st; + sc->sc_dmat = haa->ha_dmat; + sc->sc_ndesc = ndesc; + sc->sc_flags = 0; + + if (bus_space_subregion(haa->ha_st, haa->ha_sh, haa->ha_dmaoff, + sc->hpc->scsi0_regs_size, &sc->sc_bsh) != 0) { + printf(": can't map DMA registers\n"); + return; + } + + /* Alloc 1 additional descriptor - needed for DMA bug fix */ + allocsz = sizeof(struct hpc_dma_desc) * (ndesc + 1); + + /* + * Allocate a block of memory for dma chaining pointers + */ + if (bus_dmamem_alloc(sc->sc_dmat, allocsz, 0, 0, &seg, 1, &rseg, + BUS_DMA_NOWAIT)) { + printf(": can't allocate sglist\n"); + return; + } + /* Map pages into kernel memory */ + if (bus_dmamem_map(sc->sc_dmat, &seg, rseg, allocsz, + (caddr_t *)&sc->sc_desc_kva, BUS_DMA_NOWAIT)) { + printf(": can't map sglist\n"); + bus_dmamem_free(sc->sc_dmat, &seg, rseg); + return; + } + + if (bus_dmamap_create(sc->sc_dmat, allocsz, 1 /*seg*/, allocsz, 0, + BUS_DMA_WAITOK, &sc->sc_dmamap) != 0) { + printf(": failed to create dmamap\n"); + return; + } + + if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, + sc->sc_desc_kva, allocsz, NULL, BUS_DMA_NOWAIT)) { + printf(": can't load sglist\n"); + return; + } + + sc->sc_desc_pa = sc->sc_dmamap->dm_segs[0].ds_addr; +} + + +void +hpcdma_sglist_create(struct hpc_dma_softc *sc, bus_dmamap_t dmamap) +{ + struct hpc_dma_desc *hva; + bus_addr_t hpa; + bus_dma_segment_t *segp; + int i; + + KASSERT(dmamap->dm_nsegs <= sc->sc_ndesc); + + hva = sc->sc_desc_kva; + hpa = sc->sc_desc_pa; + segp = dmamap->dm_segs; + +#ifdef DMA_DEBUG + printf("DMA_SGLIST<"); +#endif + for (i = dmamap->dm_nsegs; i; i--) { +#ifdef DMA_DEBUG + printf("%p:%ld, ", (void *)segp->ds_addr, segp->ds_len); +#endif + hpa += sizeof(struct hpc_dma_desc); /* next chain desc */ + if (sc->hpc->revision == 3) { + hva->hpc3_hdd_bufptr = segp->ds_addr; + hva->hpc3_hdd_ctl = segp->ds_len; + hva->hdd_descptr = hpa; + } else /* HPC 1/1.5 */ { + /* + * there doesn't seem to be any good way of doing this + * via an abstraction layer + */ + hva->hpc1_hdd_bufptr = segp->ds_addr; + hva->hpc1_hdd_ctl = segp->ds_len; + hva->hdd_descptr = hpa; + } + ++hva; + ++segp; + } + + /* Work around HPC3 DMA bug */ + if (sc->hpc->revision == 3) { + hva->hpc3_hdd_bufptr = 0; + hva->hpc3_hdd_ctl = HPC3_HDD_CTL_EOCHAIN; + hva->hdd_descptr = 0; + } else { + hva--; + hva->hpc1_hdd_bufptr |= HPC1_HDD_CTL_EOCHAIN; + hva->hdd_descptr = 0; + } + +#ifdef DMA_DEBUG + printf(">\n"); +#endif + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, + 0, sizeof(struct hpc_dma_desc) * (dmamap->dm_nsegs + 1), + BUS_DMASYNC_PREWRITE); + + /* Load DMA Descriptor list */ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ndbp, + sc->sc_desc_pa); +} + +void +hpcdma_cntl(struct hpc_dma_softc *sc, uint32_t mode) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, mode); +} + +void +hpcdma_reset(struct hpc_dma_softc *sc) +{ + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, + sc->hpc->scsi_dmactl_reset); + delay(100); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 0); + delay(1000); +} + +void +hpcdma_flush(struct hpc_dma_softc *sc) +{ + uint32_t mode; + + mode = bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl); + bus_space_write_4(sc->sc_bst, sc->sc_bsh, + sc->hpc->scsi0_ctl, mode | sc->hpc->scsi_dmactl_flush); + + /* Wait for Active bit to drop */ + while (bus_space_read_4(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl) & + sc->hpc->scsi_dmactl_active) { + bus_space_barrier(sc->sc_bst, sc->sc_bsh, sc->hpc->scsi0_ctl, 4, + BUS_SPACE_BARRIER_READ); + } +} diff --git a/sys/arch/sgi/hpc/hpcdma.h b/sys/arch/sgi/hpc/hpcdma.h new file mode 100644 index 00000000000..bca5fc5b171 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcdma.h @@ -0,0 +1,63 @@ +/* $OpenBSD: hpcdma.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcdma.h,v 1.11 2011/07/01 18:53:46 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +struct hpc_dma_softc { + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + bus_dma_tag_t sc_dmat; + + uint32_t sc_flags; +#define HPCDMA_READ 0x20 /* direction of transfer */ +#define HPCDMA_LOADED 0x40 /* bus_dmamap loaded */ +#define HPCDMA_ACTIVE 0x80 /* DMA engine is busy */ + uint32_t sc_dmacmd; + int sc_ndesc; + bus_dmamap_t sc_dmamap; + struct hpc_dma_desc *sc_desc_kva; /* Virtual address */ + bus_addr_t sc_desc_pa; /* DMA address */ + ssize_t sc_dlen; /* number of bytes transfered */ + struct hpc_values *hpc; /* constants for HPC1/3 */ +}; + + +void hpcdma_init(struct hpc_attach_args *, struct hpc_dma_softc *, int); +void hpcdma_sglist_create(struct hpc_dma_softc *, bus_dmamap_t); +void hpcdma_cntl(struct hpc_dma_softc *, uint32_t); +void hpcdma_reset(struct hpc_dma_softc *); +void hpcdma_flush(struct hpc_dma_softc *); diff --git a/sys/arch/sgi/hpc/hpcreg.h b/sys/arch/sgi/hpc/hpcreg.h new file mode 100644 index 00000000000..bce6a2d2245 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcreg.h @@ -0,0 +1,450 @@ +/* $OpenBSD: hpcreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcreg.h,v 1.20 2011/01/25 12:21:04 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * 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. 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. + */ + +/* + * HPC locations are identical across all HPC-supported + * platforms. + */ +#define HPC_BASE_ADDRESS_0 0x1fb80000 /* Primary onboard */ +#define HPC_BASE_ADDRESS_1 0x1fb00000 +#define HPC_BASE_ADDRESS_2 0x1f980000 +#define HPC_BASE_ADDRESS_3 0x1f900000 /* NB: Never supported in h/w */ + +/* + * HPC3 descriptor layout. + */ +struct hpc_dma_desc { + uint32_t hdd_bufptr; /* Physical address of buffer */ + uint32_t hdd_ctl; /* Control flags and byte count */ + uint32_t hdd_descptr; /* Physical address of next descr. */ + uint32_t hdd_pad; /* Pad out to quadword alignment */ +}; + +/* + * The hdd_bufptr and hdd_ctl fields are swapped between HPC1 and + * HPC3. These fields are referenced by macro for readability. + */ +#define hpc1_hdd_ctl hdd_bufptr +#define hpc1_hdd_bufptr hdd_ctl +#define hpc3_hdd_ctl hdd_ctl +#define hpc3_hdd_bufptr hdd_bufptr + +/* + * Control flags + */ +#define HPC3_HDD_CTL_EOCHAIN 0x80000000 /* End of descriptor chain */ +#define HPC3_HDD_CTL_EOPACKET 0x40000000 /* Ethernet: end of packet */ +#define HPC3_HDD_CTL_INTR 0x20000000 /* Interrupt when finished */ +#define HPC3_HDD_CTL_XMITDONE 0x00008000 /* Ethernet transmit done */ +#define HPC3_HDD_CTL_OWN 0x00004000 /* CPU owns this frame */ + +#define HPC3_HDD_CTL_BYTECNT(x) ((x) & 0x3fff) /* Byte count: for ethernet + * rcv channel also doubles as + * length of packet received + */ + +/* + * HPC memory map, as offsets from HPC base + * + * XXXrkb: should each section be used as a base and have the specific + * registers offset from there?? + * + * XXX: define register values as well as their offsets. + * + */ +#define HPC3_PBUS_DMAREGS 0x00000000 /* DMA registers for PBus */ +#define HPC3_PBUS_DMAREGS_SIZE 0x0000ffff /* channels 0 - 7 */ + +#define HPC3_PBUS_CH0_BP 0x00000000 /* Chan 0 Buffer Ptr */ +#define HPC3_PBUS_CH0_DP 0x00000004 /* Chan 0 Descriptor Ptr */ +#define HPC3_PBUS_CH0_CTL 0x00001000 /* Chan 0 Control Register */ + +#define HPC3_PBUS_CH1_BP 0x00002000 /* Chan 1 Buffer Ptr */ +#define HPC3_PBUS_CH1_DP 0x00002004 /* Chan 1 Descriptor Ptr */ +#define HPC3_PBUS_CH1_CTL 0x00003000 /* Chan 1 Control Register */ + +#define HPC3_PBUS_CH2_BP 0x00004000 /* Chan 2 Buffer Ptr */ +#define HPC3_PBUS_CH2_DP 0x00004004 /* Chan 2 Descriptor Ptr */ +#define HPC3_PBUS_CH2_CTL 0x00005000 /* Chan 2 Control Register */ + +#define HPC3_PBUS_CH3_BP 0x00006000 /* Chan 3 Buffer Ptr */ +#define HPC3_PBUS_CH3_DP 0x00006004 /* Chan 3 Descriptor Ptr */ +#define HPC3_PBUS_CH3_CTL 0x00007000 /* Chan 3 Control Register */ + +#define HPC3_PBUS_CH4_BP 0x00008000 /* Chan 4 Buffer Ptr */ +#define HPC3_PBUS_CH4_DP 0x00008004 /* Chan 4 Descriptor Ptr */ +#define HPC3_PBUS_CH4_CTL 0x00009000 /* Chan 4 Control Register */ + +#define HPC3_PBUS_CH5_BP 0x0000a000 /* Chan 5 Buffer Ptr */ +#define HPC3_PBUS_CH5_DP 0x0000a004 /* Chan 5 Descriptor Ptr */ +#define HPC3_PBUS_CH5_CTL 0x0000b000 /* Chan 5 Control Register */ + +#define HPC3_PBUS_CH6_BP 0x0000c000 /* Chan 6 Buffer Ptr */ +#define HPC3_PBUS_CH6_DP 0x0000c004 /* Chan 6 Descriptor Ptr */ +#define HPC3_PBUS_CH6_CTL 0x0000d000 /* Chan 6 Control Register */ + +#define HPC3_PBUS_CH7_BP 0x0000e000 /* Chan 7 Buffer Ptr */ +#define HPC3_PBUS_CH7_DP 0x0000e004 /* Chan 7 Descriptor Ptr */ +#define HPC3_PBUS_CH7_CTL 0x0000f000 /* Chan 7 Control Register */ + +#define HPC3_SCSI0_REGS 0x00010000 /* SCSI channel 0 registers */ +#define HPC3_SCSI0_REGS_SIZE 0x00001fff + +#define HPC3_SCSI0_CBP 0x00000000 /* Current buffer ptr */ +#define HPC3_SCSI0_NDBP 0x00000004 /* Next descriptor ptr */ + +#define HPC3_SCSI0_BC 0x00001000 /* DMA byte count & flags */ +#define HPC3_SCSI0_CTL 0x00001004 /* DMA control flags */ +#define HPC3_SCSI0_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC3_SCSI0_DEV 0x0000100c /* Device DMA FIFO pointer */ +#define HPC3_SCSI0_DMACFG 0x00001010 /* DMA configuration */ +#define HPC3_SCSI0_PIOCFG 0x00001014 /* PIO configuration */ + +#define HPC3_SCSI1_REGS 0x00012000 /* SCSI channel 1 registers */ +#define HPC3_SCSI1_REGS_SIZE 0x00001fff + +#define HPC3_SCSI1_CBP 0x00000000 /* Current buffer ptr */ +#define HPC3_SCSI1_NDBP 0x00000004 /* Next descriptor ptr */ + +#define HPC3_SCSI1_BC 0x00001000 /* DMA byte count & flags */ +#define HPC3_SCSI1_CTL 0x00001004 /* DMA control flags */ +#define HPC3_SCSI1_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC3_SCSI1_DEV 0x0000100c /* Device DMA FIFO pointer */ +#define HPC3_SCSI1_DMACFG 0x00001010 /* DMA configuration */ +#define HPC3_SCSI1_PIOCFG 0x00001014 /* PIO configuration */ + +/* HPC3_SCSIx_CTL "SCSI control register" flags: */ +#define HPC3_SCSI_DMACTL_IRQ 0x01 /* IRQ asserted, dma done or parity */ +#define HPC3_SCSI_DMACTL_ENDIAN 0x02 /* DMA endian mode, 0=BE, 1=LE */ +#define HPC3_SCSI_DMACTL_DIR 0x04 /* DMA direction, 0=dev->mem, 1=mem->dev */ +#define HPC3_SCSI_DMACTL_FLUSH 0x08 /* Flush DMA FIFO's */ +#define HPC3_SCSI_DMACTL_ACTIVE 0x10 /* DMA channel is active */ +#define HPC3_SCSI_DMACTL_AMASK 0x20 /* DMA active inhibits PIO */ +#define HPC3_SCSI_DMACTL_RESET 0x40 /* Reset dma channel and ext. controller */ +#define HPC3_SCSI_DMACTL_PERR 0x80 /* Parity error: interface to controller */ + +/* HPC_PBUS_CHx_CTL read: */ +#define HPC3_PBUS_DMACTL_IRQ 0x01 /* IRQ asserted, DMA done */ +#define HPC3_PBUS_DMACTL_ISACT 0x02 /* DMA channel is active */ + +/* HPC_PBUS_CHx_CTL write: */ +#define HPC3_PBUS_DMACTL_ENDIAN 0x02 /* DMA endianness, 0=BE 1=LE */ +#define HPC3_PBUS_DMACTL_RECEIVE 0x04 /* DMA direction, 1=dev->mem, 0=mem->dev*/ +#define HPC3_PBUS_DMACTL_FLUSH 0x08 /* Flush DMA FIFO */ +#define HPC3_PBUS_DMACTL_ACT 0x10 /* Activate DMA channel */ +#define HPC3_PBUS_DMACTL_ACT_LD 0x20 /* Load enable for ACT */ +#define HPC3_PBUS_DMACTL_RT 0x40 /* Enable real time GIO service for DMA */ +#define HPC3_PBUS_DMACTL_HIGHWATER_SHIFT 8 +#define HPC3_PBUS_DMACTL_FIFOBEG_SHIFT 16 +#define HPC3_PBUS_DMACTL_FIFOEND_SHIFT 24 + +#define HPC3_ENET_REGS 0x00014000 /* Ethernet registers */ +#define HPC3_ENET_REGS_SIZE 0x00003fff + +#define HPC3_ENETR_CBP 0x00000000 /* Recv: Current buffer ptr */ +#define HPC3_ENETR_NDBP 0x00000004 /* Recv: Next descriptor ptr */ + +#define HPC3_ENETR_BC 0x00001000 /* Recv: DMA byte cnt/flags */ +#define HPC3_ENETR_CTL 0x00001004 /* Recv: DMA control flags */ + +#define HPC3_ENETR_CTL_STAT_5_0 0x003f /* Seeq irq status: bits 0-5 */ +#define HPC3_ENETR_CTL_STAT_6 0x0040 /* Irq status: late_rxdc */ +#define HPC3_ENETR_CTL_STAT_7 0x0080 /* Irq status: old/new bit */ +#define HPC3_ENETR_CTL_LENDIAN 0x0100 /* DMA channel endian mode */ +#define HPC3_ENETR_CTL_ACTIVE 0x0200 /* DMA channel active? */ +#define HPC3_ENETR_CTL_ACTIVE_MSK 0x0400 /* DMA channel active? */ +#define HPC3_ENETR_CTL_RBO 0x0800 /* Recv buffer overflow */ + +#define HPC3_ENETR_GIO 0x00001008 /* Recv: GIO DMA FIFO ptr */ +#define HPC3_ENETR_DEV 0x0000100c /* Recv: Device DMA FIFO ptr */ +#define HPC3_ENETR_RESET 0x00001014 /* Recv: Ethernet chip reset */ + +#define HPC3_ENETR_RESET_CH 0x0001 /* Reset controller & chan */ +#define HPC3_ENETR_RESET_CLRINT 0x0002 /* Clear channel interrupt */ +#define HPC3_ENETR_RESET_LOOPBK 0x0004 /* External loopback enable */ +#define HPC3_ENETR_RESET_CLRRBO 0x0008 /* Clear RBO condition (??) */ + +#define HPC3_ENETR_DMACFG 0x00001018 /* Recv: DMA configuration */ + +#define HPC3_ENETR_DMACFG_D1(_x) (((_x) << 0) & 0x000f) /* D1 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_D2(_x) (((_x) << 4) & 0x00f0) /* D2 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_D3(_x) (((_x) << 8) & 0x0f00) /* D3 gio_clk cycles */ +#define HPC3_ENETR_DMACFG_WRCTL 0x01000 /* Enable IPG write */ + +/* + * The following three bits work around bugs in the Seeq 8003; if you + * don't set them, the Seeq gets wonky pretty often. + */ +#define HPC3_ENETR_DMACFG_FIX_RXDC 0x02000 /* Clear EOP bits on RXDC */ +#define HPC3_ENETR_DMACFG_FIX_EOP 0x04000 /* Enable rxintr timeout */ +#define HPC3_ENETR_DMACFG_FIX_INTR 0x08000 /* Enable EOP timeout */ +#define HPC3_ENETR_DMACFG_TIMEOUT 0x30000 /* Timeout value for above two*/ + +#define HPC3_ENETR_PIOCFG 0x0000101c /* Recv: PIO configuration */ + +#define HPC3_ENETR_PIOCFG_P1(_x) (((_x) << 0) & 0x000f) /* P1 gio_clk cycles */ +#define HPC3_ENETR_PIOCFG_P2(_x) (((_x) << 4) & 0x00f0) /* P2 gio_clk cycles */ +#define HPC3_ENETR_PIOCFG_P3(_x) (((_x) << 8) & 0x0f00) /* P3 gio_clk cycles */ + +#define HPC3_ENETX_CBP 0x00002000 /* Xmit: Current buffer ptr */ +#define HPC3_ENETX_NDBP 0x00002004 /* Xmit: Next descriptor ptr */ + +#define HPC3_ENETX_BC 0x00003000 /* Xmit: DMA byte cnt/flags */ +#define HPC3_ENETX_CTL 0x00003004 /* Xmit: DMA control flags */ + +#define HPC3_ENETX_CTL_STAT_5_0 0x003f /* Seeq irq status: bits 0-5 */ +#define HPC3_ENETX_CTL_STAT_6 0x0040 /* Irq status: late_rxdc */ +#define HPC3_ENETX_CTL_STAT_7 0x0080 /* Irq status: old/new bit */ +#define HPC3_ENETX_CTL_LENDIAN 0x0100 /* DMA channel endian mode */ +#define HPC3_ENETX_CTL_ACTIVE 0x0200 /* DMA channel active? */ +#define HPC3_ENETX_CTL_ACTIVE_MSK 0x0400 /* DMA channel active? */ +#define HPC3_ENETX_CTL_RBO 0x0800 /* Recv buffer overflow */ + +#define HPC3_ENETX_GIO 0x00003008 /* Xmit: GIO DMA FIFO ptr */ +#define HPC3_ENETX_DEV 0x0000300c /* Xmit: Device DMA FIFO ptr */ + +#define HPC3_PBUS_FIFO 0x00020000 /* PBus DMA FIFO */ +#define HPC3_PBUS_FIFO_SIZE 0x00007fff /* PBus DMA FIFO size */ + +#define HPC3_SCSI0_FIFO 0x00028000 /* SCSI0 DMA FIFO */ +#define HPC3_SCSI0_FIFO_SIZE 0x00001fff /* SCSI0 DMA FIFO size */ + +#define HPC3_SCSI1_FIFO 0x0002a000 /* SCSI1 DMA FIFO */ +#define HPC3_SCSI1_FIFO_SIZE 0x00001fff /* SCSI1 DMA FIFO size */ + +#define HPC3_ENETR_FIFO 0x0002c000 /* Ether recv DMA FIFO */ +#define HPC3_ENETR_FIFO_SIZE 0x00001fff /* Ether recv DMA FIFO size */ + +#define HPC3_ENETX_FIFO 0x0002e000 /* Ether xmit DMA FIFO */ +#define HPC3_ENETX_FIFO_SIZE 0x00001fff /* Ether xmit DMA FIFO size */ + +/* + * HPCBUG: The interrupt status is split amongst two registers, and they're + * not even consecutive in the HPC address space. This is documented as a + * bug by SGI. + */ +#define HPC3_INTRSTAT_40 0x00030000 /* Interrupt stat, bits 4:0 */ +#define HPC3_INTRSTAT_95 0x0003000c /* Interrupt stat, bits 9:5 */ + +#define HPC3_GIO_MISC 0x00030004 /* GIO64 misc register */ + +#define HPC3_EEPROM_DATA 0x0003000b /* Serial EEPROM data reg. */ + /* (byte) */ + +#define HPC3_GIO_BUSERR 0x00030010 /* GIO64 bus error intr stat */ + +#define HPC3_SCSI0_DEVREGS 0x00044000 /* SCSI channel 0 chip regs */ +#define HPC3_SCSI1_DEVREGS 0x0004c000 /* SCSI channel 1 chip regs */ + +#define HPC3_ENET_DEVREGS 0x00054000 /* Ethernet chip registers */ +#define HPC3_ENET_DEVREGS_SIZE 0x000004ff /* Size of chip registers */ + +#define HPC3_PBUS_DEVREGS 0x00054000 /* PBus PIO chip registers */ +#define HPC3_PBUS_DEVREGS_SIZE 0x000003ff /* PBus PIO chip registers */ + +#define HPC3_PBUS_CH0_DEVREGS 0x00058000 /* PBus ch. 0 chip registers */ +#define HPC3_PBUS_CH0_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH1_DEVREGS 0x00058400 /* PBus ch. 1 chip registers */ +#define HPC3_PBUS_CH1_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH2_DEVREGS 0x00058800 /* PBus ch. 2 chip registers */ +#define HPC3_PBUS_CH2_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH3_DEVREGS 0x00058c00 /* PBus ch. 3 chip registers */ +#define HPC3_PBUS_CH3_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH4_DEVREGS 0x00059000 /* PBus ch. 4 chip registers */ +#define HPC3_PBUS_CH4_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH5_DEVREGS 0x00059400 /* PBus ch. 5 chip registers */ +#define HPC3_PBUS_CH5_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH6_DEVREGS 0x00059800 /* PBus ch. 6 chip registers */ +#define HPC3_PBUS_CH6_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH7_DEVREGS 0x00059c00 /* PBus ch. 7 chip registers */ +#define HPC3_PBUS_CH7_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS 0x0005a000 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS 0x0005a400 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_2 0x0005a800 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_2_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_2 0x0005ac00 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_2_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_3 0x0005b000 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_3_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_3 0x0005b400 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_3_SIZE 0x03ff + +#define HPC3_PBUS_CH8_DEVREGS_4 0x0005b800 /* PBus ch. 8 chip registers */ +#define HPC3_PBUS_CH8_DEVREGS_4_SIZE 0x03ff + +#define HPC3_PBUS_CH9_DEVREGS_4 0x0005bc00 /* PBus ch. 9 chip registers */ +#define HPC3_PBUS_CH9_DEVREGS_4_SIZE 0x03ff + +#define HPC3_PBUS_CFGDMA_REGS 0x0005c000 /* PBus DMA config registers */ +#define HPC3_PBUS_CFGDMA_REGS_SIZE 0x0fff + +#define HPC3_PBUS_CH0_CFGDMA 0x0005c000 /* PBus Ch. 0 DMA config */ +#define HPC3_PBUS_CH0_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH1_CFGDMA 0x0005c200 /* PBus Ch. 1 DMA config */ +#define HPC3_PBUS_CH1_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH2_CFGDMA 0x0005c400 /* PBus Ch. 2 DMA config */ +#define HPC3_PBUS_CH2_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH3_CFGDMA 0x0005c600 /* PBus Ch. 3 DMA config */ +#define HPC3_PBUS_CH3_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH4_CFGDMA 0x0005c800 /* PBus Ch. 4 DMA config */ +#define HPC3_PBUS_CH4_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH5_CFGDMA 0x0005ca00 /* PBus Ch. 5 DMA config */ +#define HPC3_PBUS_CH5_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH6_CFGDMA 0x0005cc00 /* PBus Ch. 6 DMA config */ +#define HPC3_PBUS_CH6_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CH7_CFGDMA 0x0005ce00 /* PBus Ch. 7 DMA config */ +#define HPC3_PBUS_CH7_CFGDMA_SIZE 0x01ff + +#define HPC3_PBUS_CFGPIO_REGS 0x0005d000 /* PBus PIO config registers */ +#define HPC3_PBUS_CFGPIO_REGS_SIZE 0x0fff + +#define HPC3_PBUS_CH0_CFGPIO 0x0005d000 /* PBus Ch. 0 PIO config */ +#define HPC3_PBUS_CH1_CFGPIO 0x0005d100 /* PBus Ch. 1 PIO config */ +#define HPC3_PBUS_CH2_CFGPIO 0x0005d200 /* PBus Ch. 2 PIO config */ +#define HPC3_PBUS_CH3_CFGPIO 0x0005d300 /* PBus Ch. 3 PIO config */ +#define HPC3_PBUS_CH4_CFGPIO 0x0005d400 /* PBus Ch. 4 PIO config */ +#define HPC3_PBUS_CH5_CFGPIO 0x0005d500 /* PBus Ch. 5 PIO config */ +#define HPC3_PBUS_CH6_CFGPIO 0x0005d600 /* PBus Ch. 6 PIO config */ +#define HPC3_PBUS_CH7_CFGPIO 0x0005d700 /* PBus Ch. 7 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO 0x0005d800 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO 0x0005d900 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_2 0x0005da00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_2 0x0005db00 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_3 0x0005dc00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_3 0x0005dd00 /* PBus Ch. 9 PIO config */ +#define HPC3_PBUS_CH8_CFGPIO_4 0x0005de00 /* PBus Ch. 8 PIO config */ +#define HPC3_PBUS_CH9_CFGPIO_4 0x0005df00 /* PBus Ch. 9 PIO config */ + +#define HPC3_PBUS_PROM_WE 0x0005e000 /* PBus boot-prom write + * enable register + */ + +#define HPC3_PBUS_PROM_SWAP 0x0005e800 /* PBus boot-prom chip-select + * swap register + */ + +#define HPC3_PBUS_GEN_OUT 0x0005f000 /* PBus general-purpose output + * register + */ + +#define HPC3_PBUS_BBRAM 0x00060000 /* PBus battery-backed RAM + * external registers + */ + +/* HPC1/HPC1.5 differs from HPC3 in several details. */ + +#define HPC1_HDD_CTL_EOCHAIN 0x80000000 /* End of descriptor chain */ +#define HPC1_HDD_CTL_EOPACKET 0x80000000 /* Ethernet: end of packet */ +#define HPC1_HDD_CTL_INTR 0x00008000 /* Interrupt when finished */ +#define HPC1_HDD_CTL_OWN 0x40000000 /* CPU owns this frame */ +#define HPC1_HDD_CTL_BYTECNT(x) ((x) & 0x1fff) /* Byte count: for ethernet */ +#define HPC1_BIGENDIAN 0x000000c0 /* Endianness:5 revision:2 */ +#define HPC1_REVSHIFT 0x00000006 /* Revision rshft */ +#define HPC1_REVMASK 0x00000003 /* Revision mask */ +#define HPC1_REV15 0x00000001 /* HPC Revision 1.5 */ +#define HPC1_SCSI0_REGS 0x00000088 +#define HPC1_SCSI0_REGS_SIZE 0x00000018 +#define HPC1_SCSI0_CBP 0x00000004 /* Current buffer ptr */ +#define HPC1_SCSI0_NDBP 0x00000008 /* Next descriptor ptr */ +#define HPC1_SCSI0_BC 0x00000000 /* DMA byte count & flags */ +#define HPC1_SCSI0_CTL 0x0000000c /* DMA control flags */ +#define HPC1_SCSI0_DEV 0x00000014 /* Device DMA FIFO pointer */ +#define HPC1_SCSI0_DMACFG 0x00000010 /* DMA configuration */ +#define HPC1_SCSI0_GIO 0x00001008 /* GIO DMA FIFO pointer */ +#define HPC1_SCSI0_PIOCFG 0x00001014 /* PIO configuration */ +#define HPC1_SCSI_DMACTL_RESET 0x01 /* Reset dma channel and ext. controller */ +#define HPC1_SCSI_DMACTL_FLUSH 0x02 /* Flush DMA FIFO's */ +#define HPC1_SCSI_DMACTL_DIR 0x10 /* DMA direction: 1=dev->mem, 0=mem->dev */ +#define HPC1_SCSI_DMACTL_ACTIVE 0x80 /* DMA channel is active */ +#define HPC1_ENET_REGS 0x00000000 /* Ethernet registers */ +#define HPC1_ENET_REGS_SIZE 0x00000100 +#define HPC1_ENET_INTDELAY 0x0000002c /* Interrupt Delay Count */ +#define HPC1_ENET_INTDELAY_OFF 0x01000000 /* Disable Interrupt Delay */ +#define HPC1_ENETR_CBP 0x00000054 /* Recv: Current buffer ptr */ +#define HPC1_ENETR_NDBP 0x00000050 /* Recv: Next descriptor ptr */ +#define HPC1_ENETR_BC 0x00000048 /* Recv: DMA byte cnt/flags */ +#define HPC1_ENETR_CTL 0x00000038 /* Recv: DMA control flags */ +#define HPC1_ENETR_CTL_ACTIVE 0x00004000 /* DMA channel active? */ +#define HPC1_ENETR_RESET 0x0000003c /* Recv: Ethernet chip reset */ +#define HPC1_ENETR_RESET_CH 0x0001 /* Reset controller & chan */ +#define HPC1_ENETR_RESET_CLRINT 0x0002 /* Clear channel interrupt */ +#define HPC1_ENETR_RESET_LOOPBK 0x0004 /* External loopback enable */ +#define HPC1_ENETR_RESET_CLRRBO 0x0008 /* Clear RBO condition (??) */ +#define HPC1_ENETX_CBP 0x00000020 /* Xmit: Current buffer ptr */ +#define HPC1_ENETX_NDBP 0x00000010 /* Xmit: Next descriptor ptr */ +#define HPC1_ENETX_CFXBP 0x00000024 /* Xmit: Current first buf */ +#define HPC1_ENETX_PFXBP 0x00000028 /* Xmit: Prev. first buf */ +#define HPC1_ENETX_BC 0x00000014 /* Xmit: DMA byte cnt/flags */ +#define HPC1_ENETX_CTL 0x00000034 /* Xmit: DMA control flags */ +#define HPC1_ENETX_CTL_ACTIVE 0x00400000 +#define HPC1_ENETR_FIFO 0x0002c000 /* Ether recv DMA FIFO */ +#define HPC1_ENETR_FIFO_SIZE 0x00001fff /* Ether recv DMA FIFO size */ +#define HPC1_ENETX_FIFO 0x0002e000 /* Ether xmit DMA FIFO */ +#define HPC1_ENETX_FIFO_SIZE 0x00001fff /* Ether xmit DMA FIFO size */ +#define HPC1_SCSI0_DEVREGS 0x0000011f /* (actually 122) */ +#define HPC1_ENET_DEVREGS 0x00000100 /* Ethernet chip registers */ +#define HPC1_ENET_DEVREGS_SIZE 0x00000020 /* Size of chip registers */ +#define HPC1_PBUS_BBRAM 0x00000e00 /* PBus battery-backed RAM */ +#define HPC1_LPT_REGS 0x000000a8 /* LPT HPC Registers */ +#define HPC1_LPT_REGS_SIZE 0x00000018 +#define HPC1_LPT_BC 0x00000000 /* Byte Count */ +#define HPC1_LPT_CBP 0x00000004 /* Current Buffer Ptr */ +#define HPC1_LPT_NDBP 0x00000008 /* Next Buffer Ptr */ +#define HPC1_LPT_CTL 0x0000000c /* DMA Control Flags */ +#define HPC1_LPT_DEV 0x00000010 /* DMA Fifo Ptr */ +#define HPC1_LPT_DMACFG 0x00000014 /* DMA Configuration */ +#define HPC1_LPT_DEVREGS 0x00000132 /* Ext. Parallel Registers */ +#define HPC1_LPT_DEVREGS_SIZE 0x00000001 /* Size of External Registers */ + +/* AUX regs on the primary HPC */ +#define HPC1_AUX_REGS 0x000001bf /* EEPROM/LED Control (byte) */ +#define HPC1_AUX_CONSLED 0x01 /* Console LED */ diff --git a/sys/arch/sgi/hpc/hpcvar.h b/sys/arch/sgi/hpc/hpcvar.h new file mode 100644 index 00000000000..c79f284ff87 --- /dev/null +++ b/sys/arch/sgi/hpc/hpcvar.h @@ -0,0 +1,109 @@ +/* $OpenBSD: hpcvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: hpcvar.h,v 1.12 2011/01/25 12:21:04 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * 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. 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. + */ + +#define HPCDEV_IP20 (1U << 1) /* Indigo R4k */ +#define HPCDEV_IP22 (1U << 2) /* Indigo2 */ +#define HPCDEV_IP24 (1U << 3) /* Indy */ + +/* HPC 1.5/3 differ a bit, thus we need an abstraction layer */ + +struct hpc_values { + int revision; + uint32_t scsi0_regs; + uint32_t scsi0_regs_size; + uint32_t scsi0_cbp; + uint32_t scsi0_ndbp; + uint32_t scsi0_bc; + uint32_t scsi0_ctl; + uint32_t scsi0_gio; + uint32_t scsi0_dev; + uint32_t scsi0_dmacfg; + uint32_t scsi0_piocfg; + uint32_t scsi1_regs; + uint32_t scsi1_regs_size; + uint32_t scsi1_cbp; + uint32_t scsi1_ndbp; + uint32_t scsi1_bc; + uint32_t scsi1_ctl; + uint32_t scsi1_gio; + uint32_t scsi1_dev; + uint32_t scsi1_dmacfg; + uint32_t scsi1_piocfg; + uint32_t enet_regs; + uint32_t enet_regs_size; + uint32_t enet_intdelay; + uint32_t enet_intdelayval; + uint32_t enetr_cbp; + uint32_t enetr_ndbp; + uint32_t enetr_bc; + uint32_t enetr_ctl; + uint32_t enetr_ctl_active; + uint32_t enetr_reset; + uint32_t enetr_dmacfg; + uint32_t enetr_piocfg; + uint32_t enetx_cbp; + uint32_t enetx_ndbp; + uint32_t enetx_bc; + uint32_t enetx_ctl; + uint32_t enetx_ctl_active; + uint32_t enetx_dev; + uint32_t enetr_fifo; + uint32_t enetr_fifo_size; + uint32_t enetx_fifo; + uint32_t enetx_fifo_size; + uint32_t enet_dma_boundary; + uint32_t enet_devregs; + uint32_t enet_devregs_size; + uint32_t pbus_fifo; + uint32_t pbus_fifo_size; + uint32_t pbus_bbram; + uint32_t scsi_dma_segs; + uint32_t scsi_dma_segs_size; + uint32_t scsi_dma_datain_cmd; + uint32_t scsi_dma_dataout_cmd; + uint32_t scsi_dmactl_flush; + uint32_t scsi_dmactl_active; + uint32_t scsi_dmactl_reset; +}; + +struct hpc_attach_args { + const char *ha_name; /* name of device */ + bus_addr_t ha_devoff; /* offset of device */ + bus_addr_t ha_dmaoff; /* offset of DMA regs */ + int ha_irq; /* interrupt line */ + + bus_space_tag_t ha_st; /* HPC space tag */ + bus_space_handle_t ha_sh; /* HPC space handle XXX */ + bus_dma_tag_t ha_dmat; /* HPC DMA tag */ + + struct hpc_values *hpc_regs; /* HPC register definitions */ + + uint8_t hpc_eeprom[256];/* HPC eeprom contents */ +}; diff --git a/sys/arch/sgi/hpc/if_sq.c b/sys/arch/sgi/hpc/if_sq.c new file mode 100644 index 00000000000..44521062c38 --- /dev/null +++ b/sys/arch/sgi/hpc/if_sq.c @@ -0,0 +1,1348 @@ +/* $OpenBSD: if_sq.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: if_sq.c,v 1.42 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * Copyright (c) 1998, 1999, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * Portions of this code are derived from software contributed to The + * NetBSD Foundation by Jason R. Thorpe of the Numerical Aerospace + * Simulation Facility, NASA Ames Research Center. + * + * 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. 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 "bpfilter.h" + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <sys/pool.h> +#include <sys/kernel.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> + +#include <uvm/uvm_extern.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#endif + +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#include <netinet/if_ether.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> /* guarded_read_4 */ +#include <machine/intr.h> +#include <mips64/arcbios.h> /* bios_enaddr */ +#include <sgi/localbus/intvar.h> + +#include <dev/ic/seeq8003reg.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/if_sqvar.h> + +/* + * Short TODO list: + * (1) Do counters for bad-RX packets. + * (2) Allow multi-segment transmits, instead of copying to a single, + * contiguous mbuf. + * (3) Verify sq_stop() turns off enough stuff; I was still getting + * seeq interrupts after sq_stop(). + * (4) Implement EDLC modes: especially packet auto-pad and simplex + * mode. + * (5) Should the driver filter out its own transmissions in non-EDLC + * mode? + * (6) Multicast support -- multicast filter, address management, ... + * (7) Deal with RB0 (recv buffer overflow) on reception. Will need + * to figure out if RB0 is read-only as stated in one spot in the + * HPC spec or read-write (ie, is the 'write a one to clear it') + * the correct thing? + */ + +#if defined(SQ_DEBUG) +int sq_debug = 0; +#define SQ_DPRINTF(x) do { if (sq_debug) printf x; } while (0) +#else +#define SQ_DPRINTF(x) do { } while (0) +#endif + +int sq_match(struct device *, void *, void *); +void sq_attach(struct device *, struct device *, void *); +int sq_init(struct ifnet *); +void sq_start(struct ifnet *); +void sq_stop(struct ifnet *); +void sq_watchdog(struct ifnet *); +int sq_ioctl(struct ifnet *, u_long, caddr_t); + +void sq_set_filter(struct sq_softc *); +int sq_intr(void *); +void sq_rxintr(struct sq_softc *); +void sq_txintr(struct sq_softc *); +void sq_txring_hpc1(struct sq_softc *); +void sq_txring_hpc3(struct sq_softc *); +void sq_reset(struct sq_softc *); +int sq_add_rxbuf(struct sq_softc *, int); +#ifdef SQ_DEBUG +void sq_trace_dump(struct sq_softc *); +#endif + +const struct cfattach sq_ca = { + sizeof(struct sq_softc), sq_match, sq_attach +}; + +struct cfdriver sq_cd = { + NULL, "sq", DV_IFNET +}; + +/* XXX these values should be moved to <net/if_ether.h> ? */ +#define ETHER_PAD_LEN (ETHER_MIN_LEN - ETHER_CRC_LEN) + +#define sq_seeq_read(sc, off) \ + bus_space_read_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3) +#define sq_seeq_write(sc, off, val) \ + bus_space_write_1(sc->sc_regt, sc->sc_regh, ((off) << 2) | 3, val) + +#define sq_hpc_read(sc, off) \ + bus_space_read_4(sc->sc_hpct, sc->sc_hpch, off) +#define sq_hpc_write(sc, off, val) \ + bus_space_write_4(sc->sc_hpct, sc->sc_hpch, off, val) + +/* MAC address offset for non-onboard implementations */ +#define SQ_HPC_EEPROM_ENADDR 250 + +#define SGI_OUI_0 0x08 +#define SGI_OUI_1 0x00 +#define SGI_OUI_2 0x69 + +int +sq_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *ha = aux; + struct cfdata *cf = vcf; + vaddr_t reset, txstat; + uint32_t dummy; + + if (strcmp(ha->ha_name, cf->cf_driver->cd_name) != 0) + return 0; + + reset = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_dmaoff + + ha->hpc_regs->enetr_reset, CCA_NC); + txstat = PHYS_TO_XKPHYS(ha->ha_sh + ha->ha_devoff + (SEEQ_TXSTAT << 2), + CCA_NC); + + if (guarded_read_4(reset, &dummy) != 0) + return 0; + + *(volatile uint32_t *)reset = 0x1; + delay(20); + *(volatile uint32_t *)reset = 0x0; + + if (guarded_read_4(txstat, &dummy) != 0) + return 0; + + if ((*(volatile uint32_t *)txstat & 0xff) != TXSTAT_OLDNEW) + return 0; + + return 1; +} + +void +sq_attach(struct device *parent, struct device *self, void *aux) +{ + struct sq_softc *sc = (struct sq_softc *)self; + struct hpc_attach_args *haa = aux; + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i, rc; + + sc->sc_hpct = haa->ha_st; + sc->hpc_regs = haa->hpc_regs; /* HPC register definitions */ + + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_dmaoff, sc->hpc_regs->enet_regs_size, + &sc->sc_hpch)) != 0) { + printf(": can't HPC DMA registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_regt = haa->ha_st; + if ((rc = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff, sc->hpc_regs->enet_devregs_size, + &sc->sc_regh)) != 0) { + printf(": can't map Seeq registers, error = %d\n", rc); + goto fail_0; + } + + sc->sc_dmat = haa->ha_dmat; + + if ((rc = bus_dmamem_alloc(sc->sc_dmat, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, + sc->hpc_regs->enet_dma_boundary, &sc->sc_cdseg, 1, + &sc->sc_ncdseg, BUS_DMA_NOWAIT)) != 0) { + printf(": unable to allocate control data, error = %d\n", rc); + goto fail_0; + } + + if ((rc = bus_dmamem_map(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg, + sizeof(struct sq_control), (caddr_t *)&sc->sc_control, + BUS_DMA_NOWAIT | BUS_DMA_COHERENT)) != 0) { + printf(": unable to map control data, error = %d\n", rc); + goto fail_1; + } + + if ((rc = bus_dmamap_create(sc->sc_dmat, + sizeof(struct sq_control), 1, sizeof(struct sq_control), + sc->hpc_regs->enet_dma_boundary, BUS_DMA_NOWAIT, + &sc->sc_cdmap)) != 0) { + printf(": unable to create DMA map for control data, error " + "= %d\n", rc); + goto fail_2; + } + + if ((rc = bus_dmamap_load(sc->sc_dmat, sc->sc_cdmap, + sc->sc_control, sizeof(struct sq_control), NULL, + BUS_DMA_NOWAIT)) != 0) { + printf(": unable to load DMA map for control data, error " + "= %d\n", rc); + goto fail_3; + } + + memset(sc->sc_control, 0, sizeof(struct sq_control)); + + /* Create transmit buffer DMA maps */ + for (i = 0; i < SQ_NTXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_txmap[i])) != 0) { + printf(": unable to create tx DMA map %d, error = %d\n", + i, rc); + goto fail_4; + } + } + + /* Create receive buffer DMA maps */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = bus_dmamap_create(sc->sc_dmat, + MCLBYTES, 1, MCLBYTES, 0, + BUS_DMA_NOWAIT, &sc->sc_rxmap[i])) != 0) { + printf(": unable to create rx DMA map %d, error = %d\n", + i, rc); + goto fail_5; + } + } + + /* Pre-allocate the receive buffers. */ + for (i = 0; i < SQ_NRXDESC; i++) { + if ((rc = sq_add_rxbuf(sc, i)) != 0) { + printf(": unable to allocate or map rx buffer %d\n," + " error = %d\n", i, rc); + goto fail_6; + } + } + + bcopy(&haa->hpc_eeprom[SQ_HPC_EEPROM_ENADDR], sc->sc_ac.ac_enaddr, + ETHER_ADDR_LEN); + + /* + * If our mac address is bogus, obtain it from ARCBIOS. This will + * be true of the onboard HPC3 on IP22, since there is no eeprom, + * but rather the DS1386 RTC's battery-backed ram is used. + */ + if (sc->sc_ac.ac_enaddr[0] != SGI_OUI_0 || + sc->sc_ac.ac_enaddr[1] != SGI_OUI_1 || + sc->sc_ac.ac_enaddr[2] != SGI_OUI_2) + enaddr_aton(bios_enaddr, sc->sc_ac.ac_enaddr); + + if ((int2_intr_establish(haa->ha_irq, IPL_NET, sq_intr, sc, + self->dv_xname)) == NULL) { + printf(": unable to establish interrupt!\n"); + goto fail_6; + } + + /* Reset the chip to a known state. */ + sq_reset(sc); + + /* + * Determine if we're an 8003 or 80c03 by setting the first + * MAC address register to non-zero, and then reading it back. + * If it's zero, we have an 80c03, because we will have read + * the TxCollLSB register. + */ + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0xa5); + if (sq_seeq_read(sc, SEEQ_TXCOLLS0) == 0) + sc->sc_type = SQ_TYPE_80C03; + else + sc->sc_type = SQ_TYPE_8003; + sq_seeq_write(sc, SEEQ_TXCOLLS0, 0x00); + + printf(": Seeq %s, address %s\n", + sc->sc_type == SQ_TYPE_80C03 ? "80c03" : "8003", + ether_sprintf(sc->sc_ac.ac_enaddr)); + + bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ); + ifp->if_softc = sc; + ifp->if_mtu = ETHERMTU; + ifp->if_start = sq_start; + ifp->if_ioctl = sq_ioctl; + ifp->if_watchdog = sq_watchdog; + ifp->if_flags = IFF_BROADCAST | IFF_NOTRAILERS | IFF_MULTICAST; + IFQ_SET_READY(&ifp->if_snd); + + if_attach(ifp); + IFQ_SET_MAXLEN(&ifp->if_snd, SQ_NTXDESC - 1); + ether_ifattach(ifp); + + /* Done! */ + return; + + /* + * Free any resources we've allocated during the failed attach + * attempt. Do this in reverse order and fall through. + */ + fail_6: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[i]); + m_freem(sc->sc_rxmbuf[i]); + } + } + fail_5: + for (i = 0; i < SQ_NRXDESC; i++) { + if (sc->sc_rxmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_rxmap[i]); + } + fail_4: + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmap[i] != NULL) + bus_dmamap_destroy(sc->sc_dmat, sc->sc_txmap[i]); + } + bus_dmamap_unload(sc->sc_dmat, sc->sc_cdmap); + fail_3: + bus_dmamap_destroy(sc->sc_dmat, sc->sc_cdmap); + fail_2: + bus_dmamem_unmap(sc->sc_dmat, + (void *)sc->sc_control, sizeof(struct sq_control)); + fail_1: + bus_dmamem_free(sc->sc_dmat, &sc->sc_cdseg, sc->sc_ncdseg); + fail_0: + return; +} + +/* Set up data to get the interface up and running. */ +int +sq_init(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + /* Cancel any in-progress I/O */ + sq_stop(ifp); + + sc->sc_nextrx = 0; + + sc->sc_nfreetx = SQ_NTXDESC; + sc->sc_nexttx = sc->sc_prevtx = 0; + + SQ_TRACE(SQ_RESET, sc, 0, 0); + + /* Set into 8003 mode, bank 0 to program ethernet address */ + sq_seeq_write(sc, SEEQ_TXCMD, TXCMD_BANK0); + + /* Now write the address */ + for (i = 0; i < ETHER_ADDR_LEN; i++) + sq_seeq_write(sc, i, sc->sc_ac.ac_enaddr[i]); + + sc->sc_rxcmd = + RXCMD_IE_CRC | + RXCMD_IE_DRIB | + RXCMD_IE_SHORT | + RXCMD_IE_END | + RXCMD_IE_GOOD; + + /* + * Set the receive filter -- this will add some bits to the + * prototype RXCMD register. Do this before setting the + * transmit config register, since we might need to switch + * banks. + */ + sq_set_filter(sc); + + /* Set up Seeq transmit command register */ + sq_seeq_write(sc, SEEQ_TXCMD, + TXCMD_IE_UFLOW | TXCMD_IE_COLL | TXCMD_IE_16COLL | TXCMD_IE_GOOD); + + /* Now write the receive command register. */ + sq_seeq_write(sc, SEEQ_RXCMD, sc->sc_rxcmd); + + /* + * Set up HPC ethernet PIO and DMA configurations. + * + * The PROM appears to do most of this for the onboard HPC3, but + * not for the Challenge S's IOPLUS chip. We copy how the onboard + * chip is configured and assume that it's correct for both. + */ + if (sc->hpc_regs->revision == 3) { + uint32_t dmareg, pioreg; + + pioreg = + HPC3_ENETR_PIOCFG_P1(1) | + HPC3_ENETR_PIOCFG_P2(6) | + HPC3_ENETR_PIOCFG_P3(1); + + dmareg = + HPC3_ENETR_DMACFG_D1(6) | + HPC3_ENETR_DMACFG_D2(2) | + HPC3_ENETR_DMACFG_D3(0) | + HPC3_ENETR_DMACFG_FIX_RXDC | + HPC3_ENETR_DMACFG_FIX_INTR | + HPC3_ENETR_DMACFG_FIX_EOP | + HPC3_ENETR_DMACFG_TIMEOUT; + + sq_hpc_write(sc, HPC3_ENETR_PIOCFG, pioreg); + sq_hpc_write(sc, HPC3_ENETR_DMACFG, dmareg); + } + + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, SQ_CDRXADDR(sc, 0)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + + /* + * Turn off delayed receive interrupts on HPC1. + * (see Hollywood HPC Specification 2.1.4.3) + */ + if (sc->hpc_regs->revision != 3) + sq_hpc_write(sc, HPC1_ENET_INTDELAY, HPC1_ENET_INTDELAY_OFF); + + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + sq_start(ifp); + + return 0; +} + +void +sq_set_filter(struct sq_softc *sc) +{ + struct arpcom *ac = &sc->sc_ac; + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct ether_multi *enm; + struct ether_multistep step; + + /* + * Check for promiscuous mode. Also implies + * all-multicast. + */ + if (ifp->if_flags & IFF_PROMISC) { + sc->sc_rxcmd |= RXCMD_REC_ALL; + ifp->if_flags |= IFF_ALLMULTI; + return; + } + + /* + * The 8003 has no hash table. If we have any multicast + * addresses on the list, enable reception of all multicast + * frames. + * + * XXX The 80c03 has a hash table. We should use it. + */ + + ETHER_FIRST_MULTI(step, ac, enm); + + if (enm == NULL) { + sc->sc_rxcmd &= ~RXCMD_REC_MASK; + sc->sc_rxcmd |= RXCMD_REC_BROAD; + + ifp->if_flags &= ~IFF_ALLMULTI; + return; + } + + sc->sc_rxcmd |= RXCMD_REC_MULTI; + ifp->if_flags |= IFF_ALLMULTI; +} + +int +sq_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct sq_softc *sc = ifp->if_softc; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + SQ_TRACE(SQ_IOCTL, sc, 0, 0); + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + sq_init(ifp); +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&sc->sc_ac, ifa); +#endif + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + sq_init(ifp); + } else { + if (ifp->if_flags & IFF_RUNNING) + sq_stop(ifp); + } + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + break; + } + + if (error == ENETRESET) { + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + if (ifp->if_flags & IFF_RUNNING) + error = sq_init(ifp); + else + error = 0; + } + + splx(s); + return error; +} + +void +sq_start(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + struct mbuf *m0, *m; + bus_dmamap_t dmamap; + int err, len, totlen, nexttx, firsttx, lasttx = -1, ofree, seg; + + if ((ifp->if_flags & (IFF_RUNNING|IFF_OACTIVE)) != IFF_RUNNING) + return; + + /* + * Remember the previous number of free descriptors and + * the first descriptor we'll use. + */ + ofree = sc->sc_nfreetx; + firsttx = sc->sc_nexttx; + + /* + * Loop through the send queue, setting up transmit descriptors + * until we drain the queue, or use up all available transmit + * descriptors. + */ + while (sc->sc_nfreetx != 0) { + /* + * Grab a packet off the queue. + */ + IFQ_POLL(&ifp->if_snd, m0); + if (m0 == NULL) + break; + m = NULL; + + dmamap = sc->sc_txmap[sc->sc_nexttx]; + + /* + * Load the DMA map. If this fails, the packet either + * didn't fit in the alloted number of segments, or we were + * short on resources. In this case, we'll copy and try + * again. + * Also copy it if we need to pad, so that we are sure there + * is room for the pad buffer. + * XXX the right way of doing this is to use a static buffer + * for padding and adding it to the transmit descriptor (see + * sys/dev/pci/if_tl.c for example). We can't do this here yet + * because we can't send packets with more than one fragment. + */ + len = m0->m_pkthdr.len; + if (len < ETHER_PAD_LEN || + bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, m0, + BUS_DMA_NOWAIT) != 0) { + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) { + printf("%s: unable to allocate Tx mbuf\n", + sc->sc_dev.dv_xname); + break; + } + if (len > MHLEN) { + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + printf("%s: unable to allocate Tx " + "cluster\n", + sc->sc_dev.dv_xname); + m_freem(m); + break; + } + } + + m_copydata(m0, 0, len, mtod(m, void *)); + if (len < ETHER_PAD_LEN) { + memset(mtod(m, char *) + len, 0, + ETHER_PAD_LEN - len); + len = ETHER_PAD_LEN; + } + m->m_pkthdr.len = m->m_len = len; + + if ((err = bus_dmamap_load_mbuf(sc->sc_dmat, dmamap, + m, BUS_DMA_NOWAIT)) != 0) { + printf("%s: unable to load Tx buffer, " + "error = %d\n", + sc->sc_dev.dv_xname, err); + break; + } + } + + /* + * Ensure we have enough descriptors free to describe + * the packet. + */ + if (dmamap->dm_nsegs > sc->sc_nfreetx) { + /* + * Not enough free descriptors to transmit this + * packet. We haven't committed to anything yet, + * so just unload the DMA map, put the packet + * back on the queue, and punt. Notify the upper + * layer that there are no more slots left. + * + * XXX We could allocate an mbuf and copy, but + * XXX it is worth it? + */ + ifp->if_flags |= IFF_OACTIVE; + bus_dmamap_unload(sc->sc_dmat, dmamap); + if (m != NULL) + m_freem(m); + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m0); +#if NBPFILTER > 0 + /* + * Pass the packet to any BPF listeners. + */ + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m0, BPF_DIRECTION_OUT); +#endif + if (m != NULL) { + m_freem(m0); + m0 = m; + } + + /* + * WE ARE NOW COMMITTED TO TRANSMITTING THE PACKET. + */ + + SQ_TRACE(SQ_ENQUEUE, sc, sc->sc_nexttx, 0); + + /* Sync the DMA map. */ + bus_dmamap_sync(sc->sc_dmat, dmamap, 0, dmamap->dm_mapsize, + BUS_DMASYNC_PREWRITE); + + /* + * Initialize the transmit descriptors. + */ + for (nexttx = sc->sc_nexttx, seg = 0, totlen = 0; + seg < dmamap->dm_nsegs; + seg++, nexttx = SQ_NEXTTX(nexttx)) { + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[nexttx].hpc3_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc3_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } else { + sc->sc_txdesc[nexttx].hpc1_hdd_bufptr = + dmamap->dm_segs[seg].ds_addr; + sc->sc_txdesc[nexttx].hpc1_hdd_ctl = + dmamap->dm_segs[seg].ds_len; + } + sc->sc_txdesc[nexttx].hdd_descptr = + SQ_CDTXADDR(sc, SQ_NEXTTX(nexttx)); + lasttx = nexttx; + totlen += dmamap->dm_segs[seg].ds_len; + } + + /* Last descriptor gets end-of-packet */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_EOPACKET; + else + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= + HPC1_HDD_CTL_EOPACKET; + + SQ_DPRINTF(("%s: transmit %d-%d, len %d\n", + sc->sc_dev.dv_xname, sc->sc_nexttx, lasttx, totlen)); + + if (ifp->if_flags & IFF_DEBUG) { + printf(" transmit chain:\n"); + for (seg = sc->sc_nexttx;; seg = SQ_NEXTTX(seg)) { + printf(" descriptor %d:\n", seg); + printf(" hdd_bufptr: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_bufptr : + sc->sc_txdesc[seg].hpc1_hdd_bufptr); + printf(" hdd_ctl: 0x%08x\n", + (sc->hpc_regs->revision == 3) ? + sc->sc_txdesc[seg].hpc3_hdd_ctl: + sc->sc_txdesc[seg].hpc1_hdd_ctl); + printf(" hdd_descptr: 0x%08x\n", + sc->sc_txdesc[seg].hdd_descptr); + + if (seg == lasttx) + break; + } + } + + /* Sync the descriptors we're using. */ + SQ_CDTXSYNC(sc, sc->sc_nexttx, dmamap->dm_nsegs, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* Store a pointer to the packet so we can free it later */ + sc->sc_txmbuf[sc->sc_nexttx] = m0; + + /* Advance the tx pointer. */ + sc->sc_nfreetx -= dmamap->dm_nsegs; + sc->sc_nexttx = nexttx; + } + + /* All transmit descriptors used up, let upper layers know */ + if (sc->sc_nfreetx == 0) + ifp->if_flags |= IFF_OACTIVE; + + if (sc->sc_nfreetx != ofree) { + SQ_DPRINTF(("%s: %d packets enqueued, first %d, INTR on %d\n", + sc->sc_dev.dv_xname, lasttx - firsttx + 1, + firsttx, lasttx)); + + /* + * Cause a transmit interrupt to happen on the + * last packet we enqueued, mark it as the last + * descriptor. + * + * HPC1_HDD_CTL_INTR will generate an interrupt on + * HPC1. HPC3 requires HPC3_HDD_CTL_EOPACKET in + * addition to HPC3_HDD_CTL_INTR to interrupt. + */ + KASSERT(lasttx != -1); + if (sc->hpc_regs->revision == 3) { + sc->sc_txdesc[lasttx].hpc3_hdd_ctl |= + HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOCHAIN; + } else { + sc->sc_txdesc[lasttx].hpc1_hdd_ctl |= HPC1_HDD_CTL_INTR; + sc->sc_txdesc[lasttx].hpc1_hdd_bufptr |= + HPC1_HDD_CTL_EOCHAIN; + } + + SQ_CDTXSYNC(sc, lasttx, 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + /* + * There is a potential race condition here if the HPC + * DMA channel is active and we try and either update + * the 'next descriptor' pointer in the HPC PIO space + * or the 'next descriptor' pointer in a previous desc- + * riptor. + * + * To avoid this, if the channel is active, we rely on + * the transmit interrupt routine noticing that there + * are more packets to send and restarting the HPC DMA + * engine, rather than mucking with the DMA state here. + */ + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + + if ((status & sc->hpc_regs->enetx_ctl_active) != 0) { + SQ_TRACE(SQ_ADD_TO_DMA, sc, firsttx, status); + + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc3_hdd_ctl &= + ~HPC3_HDD_CTL_EOCHAIN; + + if (sc->hpc_regs->revision != 3) + sc->sc_txdesc[SQ_PREVTX(firsttx)].hpc1_hdd_ctl + &= ~HPC1_HDD_CTL_INTR; + + SQ_CDTXSYNC(sc, SQ_PREVTX(firsttx), 1, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + } else if (sc->hpc_regs->revision == 3) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, SQ_CDTXADDR(sc, + firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, HPC3_ENETX_CTL_ACTIVE); + } else { + /* + * In the HPC1 case where transmit DMA is + * inactive, we can either kick off if + * the ring was previously empty, or call + * our transmit interrupt handler to + * figure out if the ring stopped short + * and restart at the right place. + */ + if (ofree == SQ_NTXDESC) { + SQ_TRACE(SQ_START_DMA, sc, firsttx, status); + + sq_hpc_write(sc, HPC1_ENETX_NDBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CFXBP, + SQ_CDTXADDR(sc, firsttx)); + sq_hpc_write(sc, HPC1_ENETX_CBP, + SQ_CDTXADDR(sc, firsttx)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, + HPC1_ENETX_CTL_ACTIVE); + } else + sq_txring_hpc1(sc); + } + + /* Set a watchdog timer in case the chip flakes out. */ + ifp->if_timer = 5; + } +} + +void +sq_stop(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + int i; + + ifp->if_timer = 0; + ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE); + + for (i = 0; i < SQ_NTXDESC; i++) { + if (sc->sc_txmbuf[i] != NULL) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + } + } + + /* Clear Seeq transmit/receive command registers */ + sq_seeq_write(sc, SEEQ_TXCMD, 0); + sq_seeq_write(sc, SEEQ_RXCMD, 0); + + sq_reset(sc); +} + +/* Device timeout/watchdog routine. */ +void +sq_watchdog(struct ifnet *ifp) +{ + struct sq_softc *sc = ifp->if_softc; + uint32_t status; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl); + log(LOG_ERR, "%s: device timeout (prev %d, next %d, free %d, " + "status %08x)\n", sc->sc_dev.dv_xname, sc->sc_prevtx, + sc->sc_nexttx, sc->sc_nfreetx, status); + +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + + ++ifp->if_oerrors; + + sq_init(ifp); +} + +#ifdef SQ_DEBUG +void +sq_trace_dump(struct sq_softc *sc) +{ + int i; + const char *act; + + for (i = 0; i < sc->sq_trace_idx; i++) { + switch (sc->sq_trace[i].action) { + case SQ_RESET: act = "SQ_RESET"; break; + case SQ_ADD_TO_DMA: act = "SQ_ADD_TO_DMA"; break; + case SQ_START_DMA: act = "SQ_START_DMA"; break; + case SQ_DONE_DMA: act = "SQ_DONE_DMA"; break; + case SQ_RESTART_DMA: act = "SQ_RESTART_DMA"; break; + case SQ_TXINTR_ENTER: act = "SQ_TXINTR_ENTER"; break; + case SQ_TXINTR_EXIT: act = "SQ_TXINTR_EXIT"; break; + case SQ_TXINTR_BUSY: act = "SQ_TXINTR_BUSY"; break; + case SQ_IOCTL: act = "SQ_IOCTL"; break; + case SQ_ENQUEUE: act = "SQ_ENQUEUE"; break; + default: act = "UNKNOWN"; + } + + printf("%s: [%03d] action %-16s buf %03d free %03d " + "status %08x line %d\n", sc->sc_dev.dv_xname, i, act, + sc->sq_trace[i].bufno, sc->sq_trace[i].freebuf, + sc->sq_trace[i].status, sc->sq_trace[i].line); + } + + memset(&sc->sq_trace, 0, sizeof(sc->sq_trace)); + sc->sq_trace_idx = 0; +} +#endif + +int +sq_intr(void *arg) +{ + struct sq_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t stat; + + stat = sq_hpc_read(sc, sc->hpc_regs->enetr_reset); + + if ((stat & 2) == 0) { + SQ_DPRINTF(("%s: Unexpected interrupt!\n", + sc->sc_dev.dv_xname)); + } else + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, (stat | 2)); + + /* + * If the interface isn't running, the interrupt couldn't + * possibly have come from us. + */ + if ((ifp->if_flags & IFF_RUNNING) == 0) + return 0; + + /* Always check for received packets */ + sq_rxintr(sc); + + /* Only handle transmit interrupts if we actually sent something */ + if (sc->sc_nfreetx < SQ_NTXDESC) + sq_txintr(sc); + + /* + * XXX Always claim the interrupt, even if we did nothing. + * XXX There seem to be extra interrupts when the receiver becomes + * XXX idle. + */ + return 1; +} + +void +sq_rxintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + struct mbuf* m; + int i, framelen; + uint8_t pktstat; + uint32_t status; + uint32_t ctl_reg; + int new_end, orig_end; + + for (i = sc->sc_nextrx; ; i = SQ_NEXTRX(i)) { + SQ_CDRXSYNC(sc, i, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* + * If this is a CPU-owned buffer, we're at the end of the list. + */ + if (sc->hpc_regs->revision == 3) + ctl_reg = + sc->sc_rxdesc[i].hpc3_hdd_ctl & HPC3_HDD_CTL_OWN; + else + ctl_reg = + sc->sc_rxdesc[i].hpc1_hdd_ctl & HPC1_HDD_CTL_OWN; + + if (ctl_reg) { +#if defined(SQ_DEBUG) + uint32_t reg; + + reg = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + SQ_DPRINTF(("%s: rxintr: done at %d (ctl %08x)\n", + sc->sc_dev.dv_xname, i, reg)); +#endif + break; + } + + m = sc->sc_rxmbuf[i]; + framelen = m->m_ext.ext_size - 3; + if (sc->hpc_regs->revision == 3) + framelen -= + HPC3_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc3_hdd_ctl); + else + framelen -= + HPC1_HDD_CTL_BYTECNT(sc->sc_rxdesc[i].hpc1_hdd_ctl); + + /* Now sync the actual packet data */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_POSTREAD); + + pktstat = *((uint8_t *)m->m_data + framelen + 2); + + if ((pktstat & RXSTAT_GOOD) == 0) { + ifp->if_ierrors++; + + if (pktstat & RXSTAT_OFLOW) + printf("%s: receive FIFO overflow\n", + sc->sc_dev.dv_xname); + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d no RXSTAT_GOOD\n", + sc->sc_dev.dv_xname, i)); + continue; + } + + if (sq_add_rxbuf(sc, i) != 0) { + ifp->if_ierrors++; + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[i], 0, + sc->sc_rxmap[i]->dm_mapsize, BUS_DMASYNC_PREREAD); + SQ_INIT_RXDESC(sc, i); + SQ_DPRINTF(("%s: sq_rxintr: buf %d sq_add_rxbuf() " + "failed\n", sc->sc_dev.dv_xname, i)); + continue; + } + + + m->m_data += 2; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = framelen; + + ifp->if_ipackets++; + + SQ_DPRINTF(("%s: sq_rxintr: buf %d len %d\n", + sc->sc_dev.dv_xname, i, framelen)); + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + + ether_input_mbuf(ifp, m); + } + + + /* If anything happened, move ring start/end pointers to new spot */ + if (i != sc->sc_nextrx) { + /* + * NB: hpc3_hdd_ctl == hpc1_hdd_bufptr, and + * HPC1_HDD_CTL_EOCHAIN == HPC3_HDD_CTL_EOCHAIN + */ + + new_end = SQ_PREVRX(i); + sc->sc_rxdesc[new_end].hpc3_hdd_ctl |= HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, new_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + orig_end = SQ_PREVRX(sc->sc_nextrx); + sc->sc_rxdesc[orig_end].hpc3_hdd_ctl &= ~HPC3_HDD_CTL_EOCHAIN; + SQ_CDRXSYNC(sc, orig_end, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + sc->sc_nextrx = i; + } + + status = sq_hpc_read(sc, sc->hpc_regs->enetr_ctl); + + /* If receive channel is stopped, restart it... */ + if ((status & sc->hpc_regs->enetr_ctl_active) == 0) { + /* Pass the start of the receive ring to the HPC */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ndbp, + SQ_CDRXADDR(sc, sc->sc_nextrx)); + + /* And turn on the HPC ethernet receive channel */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, + sc->hpc_regs->enetr_ctl_active); + } +} + +void +sq_txintr(struct sq_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint shift = 0; + uint32_t status, tmp; + + if (sc->hpc_regs->revision != 3) + shift = 16; + + status = sq_hpc_read(sc, sc->hpc_regs->enetx_ctl) >> shift; + + SQ_TRACE(SQ_TXINTR_ENTER, sc, sc->sc_prevtx, status); + + tmp = (sc->hpc_regs->enetx_ctl_active >> shift) | TXSTAT_GOOD; + if ((status & tmp) == 0) { + if (status & TXSTAT_COLL) + ifp->if_collisions++; + + if (status & TXSTAT_UFLOW) { + printf("%s: transmit underflow\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; +#ifdef SQ_DEBUG + sq_trace_dump(sc); +#endif + sq_init(ifp); + return; + } + + if (status & TXSTAT_16COLL) { + printf("%s: max collisions reached\n", + sc->sc_dev.dv_xname); + ifp->if_oerrors++; + ifp->if_collisions += 16; + } + } + + /* prevtx now points to next xmit packet not yet finished */ + if (sc->hpc_regs->revision == 3) + sq_txring_hpc3(sc); + else + sq_txring_hpc1(sc); + + /* If we have buffers free, let upper layers know */ + if (sc->sc_nfreetx > 0) + ifp->if_flags &= ~IFF_OACTIVE; + + /* If all packets have left the coop, cancel watchdog */ + if (sc->sc_nfreetx == SQ_NTXDESC) + ifp->if_timer = 0; + + SQ_TRACE(SQ_TXINTR_EXIT, sc, sc->sc_prevtx, status); + sq_start(ifp); +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc1(struct sq_softc *sc) +{ + /* + * HPC1 doesn't tag transmitted descriptors, however, + * the NDBP register points to the next descriptor that + * has not yet been processed. If DMA is not in progress, + * we can safely reclaim all descriptors up to NDBP, and, + * if necessary, restart DMA at NDBP. Otherwise, if DMA + * is active, we can only safely reclaim up to CBP. + * + * For now, we'll only reclaim on inactive DMA and assume + * that a sufficiently large ring keeps us out of trouble. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + uint32_t reclaimto, status; + int reclaimall, i = sc->sc_prevtx; + + status = sq_hpc_read(sc, HPC1_ENETX_CTL); + if (status & HPC1_ENETX_CTL_ACTIVE) { + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + return; + } else + reclaimto = sq_hpc_read(sc, HPC1_ENETX_NDBP); + + if (sc->sc_nfreetx == 0 && SQ_CDTXADDR(sc, i) == reclaimto) + reclaimall = 1; + else + reclaimall = 0; + + while (sc->sc_nfreetx < SQ_NTXDESC) { + if (SQ_CDTXADDR(sc, i) == reclaimto && !reclaimall) + break; + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + + i = SQ_NEXTTX(i); + } + + if (sc->sc_nfreetx < SQ_NTXDESC) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + KASSERT(reclaimto == SQ_CDTXADDR(sc, i)); + + sq_hpc_write(sc, HPC1_ENETX_CFXBP, reclaimto); + sq_hpc_write(sc, HPC1_ENETX_CBP, reclaimto); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC1_ENETX_CTL, HPC1_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } + + sc->sc_prevtx = i; +} + +/* + * Reclaim used transmit descriptors and restart the transmit DMA + * engine if necessary. + */ +void +sq_txring_hpc3(struct sq_softc *sc) +{ + /* + * HPC3 tags descriptors with a bit once they've been + * transmitted. We need only free each XMITDONE'd + * descriptor, and restart the DMA engine if any + * descriptors are left over. + */ + struct ifnet *ifp = &sc->sc_ac.ac_if; + int i; + uint32_t status = 0; + + i = sc->sc_prevtx; + while (sc->sc_nfreetx < SQ_NTXDESC) { + /* + * Check status first so we don't end up with a case of + * the buffer not being finished while the DMA channel + * has gone idle. + */ + status = sq_hpc_read(sc, HPC3_ENETX_CTL); + + SQ_CDTXSYNC(sc, i, sc->sc_txmap[i]->dm_nsegs, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + /* Check for used descriptor and restart DMA chain if needed */ + if ((sc->sc_txdesc[i].hpc3_hdd_ctl & + HPC3_HDD_CTL_XMITDONE) == 0) { + if ((status & HPC3_ENETX_CTL_ACTIVE) == 0) { + SQ_TRACE(SQ_RESTART_DMA, sc, i, status); + + sq_hpc_write(sc, HPC3_ENETX_NDBP, + SQ_CDTXADDR(sc, i)); + + /* Kick DMA channel into life */ + sq_hpc_write(sc, HPC3_ENETX_CTL, + HPC3_ENETX_CTL_ACTIVE); + + /* + * Set a watchdog timer in case the chip + * flakes out. + */ + ifp->if_timer = 5; + } else + SQ_TRACE(SQ_TXINTR_BUSY, sc, i, status); + break; + } + + /* Sync the packet data, unload DMA map, free mbuf */ + bus_dmamap_sync(sc->sc_dmat, sc->sc_txmap[i], + 0, sc->sc_txmap[i]->dm_mapsize, BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(sc->sc_dmat, sc->sc_txmap[i]); + m_freem(sc->sc_txmbuf[i]); + sc->sc_txmbuf[i] = NULL; + + ifp->if_opackets++; + sc->sc_nfreetx++; + + SQ_TRACE(SQ_DONE_DMA, sc, i, status); + i = SQ_NEXTTX(i); + } + + sc->sc_prevtx = i; +} + +void +sq_reset(struct sq_softc *sc) +{ + /* Stop HPC dma channels */ + sq_hpc_write(sc, sc->hpc_regs->enetr_ctl, 0); + sq_hpc_write(sc, sc->hpc_regs->enetx_ctl, 0); + + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 3); + delay(20); + sq_hpc_write(sc, sc->hpc_regs->enetr_reset, 0); +} + +/* sq_add_rxbuf: Add a receive buffer to the indicated descriptor. */ +int +sq_add_rxbuf(struct sq_softc *sc, int idx) +{ + int err; + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return ENOBUFS; + + MCLGET(m, M_DONTWAIT); + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + return ENOBUFS; + } + + if (sc->sc_rxmbuf[idx] != NULL) + bus_dmamap_unload(sc->sc_dmat, sc->sc_rxmap[idx]); + + sc->sc_rxmbuf[idx] = m; + + if ((err = bus_dmamap_load(sc->sc_dmat, sc->sc_rxmap[idx], + m->m_ext.ext_buf, m->m_ext.ext_size, NULL, BUS_DMA_NOWAIT)) != 0) { + printf("%s: can't load rx DMA map %d, error = %d\n", + sc->sc_dev.dv_xname, idx, err); + panic("sq_add_rxbuf"); /* XXX */ + } + + bus_dmamap_sync(sc->sc_dmat, sc->sc_rxmap[idx], + 0, sc->sc_rxmap[idx]->dm_mapsize, BUS_DMASYNC_PREREAD); + + SQ_INIT_RXDESC(sc, idx); + + return 0; +} diff --git a/sys/arch/sgi/hpc/if_sqvar.h b/sys/arch/sgi/hpc/if_sqvar.h new file mode 100644 index 00000000000..657025771f7 --- /dev/null +++ b/sys/arch/sgi/hpc/if_sqvar.h @@ -0,0 +1,200 @@ +/* $OpenBSD: if_sqvar.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: sqvar.h,v 1.12 2011/01/25 13:12:39 tsutsui Exp $ */ + +/* + * Copyright (c) 2001 Rafal K. Boni + * 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. 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. + */ + +/* Note, these must be powers of two for the magic NEXT/PREV macros to work */ +#define SQ_NRXDESC 64 +#define SQ_NTXDESC 64 + +#define SQ_NRXDESC_MASK (SQ_NRXDESC - 1) +#define SQ_NEXTRX(x) ((x + 1) & SQ_NRXDESC_MASK) +#define SQ_PREVRX(x) ((x - 1) & SQ_NRXDESC_MASK) + +#define SQ_NTXDESC_MASK (SQ_NTXDESC - 1) +#define SQ_NEXTTX(x) ((x + 1) & SQ_NTXDESC_MASK) +#define SQ_PREVTX(x) ((x - 1) & SQ_NTXDESC_MASK) + +/* + * We pack all DMA control structures into one container so we can alloc just + * one chunk of DMA-safe memory and pack them into it. Otherwise, we'd have to + * allocate a page for each descriptor, since the bus_dmamem_alloc() interface + * does not allow us to allocate smaller chunks. + */ +struct sq_control { + /* Receive descriptors */ + struct hpc_dma_desc rx_desc[SQ_NRXDESC]; + + /* Transmit descriptors */ + struct hpc_dma_desc tx_desc[SQ_NTXDESC]; +}; + +#define SQ_CDOFF(x) offsetof(struct sq_control, x) +#define SQ_CDTXOFF(x) SQ_CDOFF(tx_desc[(x)]) +#define SQ_CDRXOFF(x) SQ_CDOFF(rx_desc[(x)]) + +#define SQ_TYPE_8003 0 +#define SQ_TYPE_80C03 1 + +/* Trace Actions */ +#define SQ_RESET 1 +#define SQ_ADD_TO_DMA 2 +#define SQ_START_DMA 3 +#define SQ_DONE_DMA 4 +#define SQ_RESTART_DMA 5 +#define SQ_TXINTR_ENTER 6 +#define SQ_TXINTR_EXIT 7 +#define SQ_TXINTR_BUSY 8 +#define SQ_IOCTL 9 +#define SQ_ENQUEUE 10 + +struct sq_action_trace { + int action; + int line; + int bufno; + int status; + int freebuf; +}; + +#ifdef SQ_DEBUG +#define SQ_TRACEBUF_SIZE 100 + +#define SQ_TRACE(act, sc, buf, stat) do { \ + (sc)->sq_trace[(sc)->sq_trace_idx].action = (act); \ + (sc)->sq_trace[(sc)->sq_trace_idx].line = __LINE__; \ + (sc)->sq_trace[(sc)->sq_trace_idx].bufno = (buf); \ + (sc)->sq_trace[(sc)->sq_trace_idx].status = (stat); \ + (sc)->sq_trace[(sc)->sq_trace_idx].freebuf = (sc)->sc_nfreetx; \ + if (++(sc)->sq_trace_idx == SQ_TRACEBUF_SIZE) \ + (sc)->sq_trace_idx = 0; \ +} while (/* CONSTCOND */0) +#else +#define SQ_TRACE(act, sc, buf, stat) do { } while (/* CONSTCOND */0) +#endif + +struct sq_softc { + struct device sc_dev; + + /* HPC registers */ + bus_space_tag_t sc_hpct; + bus_space_handle_t sc_hpch; + + /* HPC external Ethernet registers: aka Seeq 8003 registers */ + bus_space_tag_t sc_regt; + bus_space_handle_t sc_regh; + + bus_dma_tag_t sc_dmat; + + struct arpcom sc_ac; + uint8_t sc_enaddr[ETHER_ADDR_LEN]; + + int sc_type; + + struct sq_control* sc_control; +#define sc_rxdesc sc_control->rx_desc +#define sc_txdesc sc_control->tx_desc + + /* DMA structures for control data (DMA RX/TX descriptors) */ + int sc_ncdseg; + bus_dma_segment_t sc_cdseg; + bus_dmamap_t sc_cdmap; +#define sc_cddma sc_cdmap->dm_segs[0].ds_addr + + int sc_nextrx; + + /* DMA structures for RX packet data */ + bus_dma_segment_t sc_rxseg[SQ_NRXDESC]; + bus_dmamap_t sc_rxmap[SQ_NRXDESC]; + struct mbuf* sc_rxmbuf[SQ_NRXDESC]; + + int sc_nexttx; + int sc_prevtx; + int sc_nfreetx; + + /* DMA structures for TX packet data */ + bus_dma_segment_t sc_txseg[SQ_NTXDESC]; + bus_dmamap_t sc_txmap[SQ_NTXDESC]; + struct mbuf* sc_txmbuf[SQ_NTXDESC]; + + uint8_t sc_rxcmd; /* prototype rxcmd */ + + struct hpc_values *hpc_regs; /* HPC register definitions */ + +#ifdef SQ_DEBUG + int sq_trace_idx; + struct sq_action_trace sq_trace[SQ_TRACEBUF_SIZE]; +#endif +}; + +#define SQ_CDTXADDR(sc, x) ((sc)->sc_cddma + SQ_CDTXOFF((x))) +#define SQ_CDRXADDR(sc, x) ((sc)->sc_cddma + SQ_CDRXOFF((x))) + +static inline void +SQ_CDTXSYNC(struct sq_softc *sc, int __x, int __n, int ops) +{ + /* If it will wrap around, sync to the end of the ring. */ + if ((__x + __n) > SQ_NTXDESC) { + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, + SQ_CDTXOFF(__x), sizeof(struct hpc_dma_desc) * + (SQ_NTXDESC - __x), (ops)); + __n -= (SQ_NTXDESC - __x); + __x = 0; + } + + /* Now sync whatever is left. */ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, + SQ_CDTXOFF(__x), sizeof(struct hpc_dma_desc) * __n, (ops)); +} + +#define SQ_CDRXSYNC(sc, x, ops) \ + bus_dmamap_sync((sc)->sc_dmat, (sc)->sc_cdmap, \ + SQ_CDRXOFF((x)), sizeof(struct hpc_dma_desc), (ops)) + +static inline void +SQ_INIT_RXDESC(struct sq_softc *sc, unsigned int x) +{ + struct hpc_dma_desc* __rxd = &(sc)->sc_rxdesc[(x)]; + struct mbuf *__m = (sc)->sc_rxmbuf[(x)]; + + __m->m_data = __m->m_ext.ext_buf; + if (sc->hpc_regs->revision == 3) { + __rxd->hpc3_hdd_bufptr = + (sc)->sc_rxmap[(x)]->dm_segs[0].ds_addr; + __rxd->hpc3_hdd_ctl = __m->m_ext.ext_size | HPC3_HDD_CTL_OWN | + HPC3_HDD_CTL_INTR | HPC3_HDD_CTL_EOPACKET | + ((x) == (SQ_NRXDESC - 1) ? HPC3_HDD_CTL_EOCHAIN : 0); + } else { + __rxd->hpc1_hdd_bufptr = + (sc)->sc_rxmap[(x)]->dm_segs[0].ds_addr | + ((x) == (SQ_NRXDESC - 1) ? HPC1_HDD_CTL_EOCHAIN : 0); + __rxd->hpc1_hdd_ctl = __m->m_ext.ext_size | HPC1_HDD_CTL_OWN | + HPC1_HDD_CTL_INTR | HPC1_HDD_CTL_EOPACKET; + } + __rxd->hdd_descptr = SQ_CDRXADDR((sc), SQ_NEXTRX((x))); + SQ_CDRXSYNC((sc), (x), BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); +} diff --git a/sys/arch/sgi/hpc/iocreg.h b/sys/arch/sgi/hpc/iocreg.h new file mode 100644 index 00000000000..274454e4ddf --- /dev/null +++ b/sys/arch/sgi/hpc/iocreg.h @@ -0,0 +1,122 @@ +/* $OpenBSD: iocreg.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: iocreg.h,v 1.2 2005/12/11 12:18:53 christos Exp $ */ + +/* + * Copyright (c) 2003 Christopher Sekiya + * Copyright (c) 2001 Rafal K. Boni + * 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. 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. + */ + +/* + * IOC1/2 memory map. + * + * The IOC1/2 is connected to the HPC#0, PBus channel 6, so these registers + * are based from the external register window for PBus channel 6 on HPC#0. + * + */ + +#define IOC_BASE HPC3_PBUS_CH6_DEVREGS + +#define IOC_PLP_REGS 0x00 /* Parallel port registers */ +#define IOC_PLP_REGS_SIZE 0x2c + +#define IOC_PLP_DATA 0x00 /* Data register */ +#define IOC_PLP_CTL 0x04 /* Control register */ +#define IOC_PLP_STAT 0x08 /* Status register */ +#define IOC_PLP_DMACTL 0x0c /* DMA control register */ +#define IOC_PLP_INTSTAT 0x10 /* Interrupt status register */ +#define IOC_PLP_INTMASK 0x14 /* Interrupt mask register */ +#define IOC_PLP_TIMER1 0x18 /* Timer 1 register */ +#define IOC_PLP_TIMER2 0x1c /* Timer 2 register */ +#define IOC_PLP_TIMER3 0x20 /* Timer 3 register */ +#define IOC_PLP_TIMER4 0x24 /* Timer 4 register */ + +#define IOC_SERIAL_REGS 0x30 /* Serial port registers */ +#define IOC_SERIAL_REGS_SIZE 0x0c + +#define IOC_SERIAL_PORT1_CMD 0x00 /* Port 1 command transfer */ +#define IOC_SERIAL_PORT1_DATA 0x04 /* Port 1 data transfer */ +#define IOC_SERIAL_PORT2_CMD 0x08 /* Port 2 command transfer */ +#define IOC_SERIAL_PORT2_DATA 0x0c /* Port 2 data transfer */ + +#define IOC_KB_REGS 0x40 /* Keyboard/mouse registers */ +#define IOC_KB_REGS_SIZE 0x08 + +/* Miscellaneous registers */ + +#define IOC_MISC_REGS 0x48 /* Misc. IOC regs */ +#define IOC_MISC_REGS_SIZE 0x34 + +#define IOC_GCSEL 0x48 /* General select register */ + +#define IOC_GCREG 0x4c /* General control register */ + +#define IOC_PANEL 0x50 /* Front Panel register */ +#define IOC_PANEL_POWER_STATE 0x01 +#define IOC_PANEL_POWER_IRQ 0x02 +#define IOC_PANEL_VDOWN_IRQ 0x10 +#define IOC_PANEL_VDOWN_HOLD 0x20 +#define IOC_PANEL_VUP_IRQ 0x40 +#define IOC_PANEL_VUP_HOLD 0x80 + +#define IOC_SYSID 0x58 /* System ID register */ +#define IOC_SYSID_SYSTYPE 0x01 /* 0: Sapphire, 1: Full House */ +#define IOC_SYSID_BOARDREV 0x1e +#define IOC_SYSID_BOARDREV_SHIFT 1 +#define IOC_SYSID_CHIPREV 0xe0 +#define IOC_SYSID_CHIPREV_SHIFT 5 + +#define IOC_READ 0x60 /* Read register */ +#define IOC_READ_SCSI0_POWER 0x10 +#define IOC_READ_SCSI1_POWER 0x20 +#define IOC_READ_ENET_POWER 0x40 +#define IOC_READ_ENET_LINK 0x80 + +#define IOC_DMASEL 0x68 /* DMA select register */ +#define IOC_DMASEL_ISDN_B 0x01 +#define IOC_DMASEL_ISDN_A 0x02 +#define IOC_DMASEL_PARALLEL 0x04 +#define IOC_DMASEL_SERIAL_10MHZ 0x00 +#define IOC_DMASEL_SERIAL_6MHZ 0x10 +#define IOC_DMASEL_SERIAL_EXTERNAL 0x20 + +#define IOC_RESET 0x70 /* Reset register */ +#define IOC_RESET_PARALLEL 0x01 +#define IOC_RESET_PCKBC 0x02 +#define IOC_RESET_EISA 0x04 +#define IOC_RESET_ISDN 0x08 +#define IOC_RESET_LED_GREEN 0x10 +#define IOC_RESET_LED_RED 0x20 +#define IOC_RESET_LED_ORANGE 0x40 + +#define IOC_WRITE 0x78 /* Write register */ +#define IOC_WRITE_ENET_NTH 0x01 +#define IOC_WRITE_ENET_UTP 0x02 +#define IOC_WRITE_ENET_AUI 0x04 +#define IOC_WRITE_ENET_AUTO 0x08 +#define IOC_WRITE_PC_UART2 0x10 +#define IOC_WRITE_PC_UART1 0x20 +#define IOC_WRITE_MARGIN_LOW 0x40 +#define IOC_WRITE_MARGIN_HIGH 0x80 diff --git a/sys/arch/sgi/hpc/wdsc.c b/sys/arch/sgi/hpc/wdsc.c new file mode 100644 index 00000000000..101b5178819 --- /dev/null +++ b/sys/arch/sgi/hpc/wdsc.c @@ -0,0 +1,290 @@ +/* $OpenBSD: wdsc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: wdsc.c,v 1.32 2011/07/01 18:53:47 dyoung Exp $ */ + +/* + * Copyright (c) 2001 Wayne Knowles + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Wayne Knowles + * + * 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 NetBSD + * Foundation, Inc. and its contributors. + * 4. Neither the name of The NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/device.h> +#include <sys/buf.h> +#include <sys/timeout.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#include <mips64/archtype.h> + +#include <machine/autoconf.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <sgi/localbus/intvar.h> + +#include <sgi/hpc/hpcreg.h> +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcdma.h> + +#include <dev/ic/wd33c93reg.h> +#include <dev/ic/wd33c93var.h> + +struct wdsc_softc { + struct wd33c93_softc sc_wd33c93; /* Must be first */ + bus_dma_tag_t sc_dmat; + bus_dmamap_t sc_dmamap; + int sc_flags; +#define WDSC_DMA_ACTIVE 0x1 +#define WDSC_DMA_MAPLOADED 0x2 + struct hpc_dma_softc sc_hpcdma; +}; + +int wdsc_match(struct device *, void *, void *); +void wdsc_attach(struct device *, struct device *, void *); + +const struct cfattach wdsc_ca = { + sizeof(struct wdsc_softc), wdsc_match, wdsc_attach +}; + +struct cfdriver wdsc_cd = { + NULL, "wdsc", DV_DULL +}; + +int wdsc_dmasetup(struct wd33c93_softc *, void ** ,size_t *, int, size_t *); +int wdsc_dmago(struct wd33c93_softc *); +void wdsc_dmastop(struct wd33c93_softc *); +void wdsc_reset(struct wd33c93_softc *); + +struct scsi_adapter wdsc_switch = { + wd33c93_scsi_cmd, + scsi_minphys, + NULL, + NULL, +}; + +/* + * Match for SCSI devices on the onboard and GIO32 adapter WD33C93 chips + */ +int +wdsc_match(struct device *parent, void *vcf, void *aux) +{ + struct hpc_attach_args *haa = aux; + struct cfdata *cf = vcf; + vaddr_t reset, asr; + uint32_t dummy; + uint8_t reg; + + if (strcmp(haa->ha_name, cf->cf_driver->cd_name) != 0) + return 0; + + reset = PHYS_TO_XKPHYS(haa->ha_sh + haa->ha_dmaoff + + haa->hpc_regs->scsi0_ctl, CCA_NC); + if (guarded_read_4(reset, &dummy) != 0) + return 0; + *(volatile uint32_t *)reset = haa->hpc_regs->scsi_dmactl_reset; + delay(1000); + *(volatile uint32_t *)reset = 0x0; + delay(1000); + + asr = PHYS_TO_XKPHYS(haa->ha_sh + haa->ha_devoff + 3, CCA_NC); + if (guarded_read_1(asr, ®) != 0) + return 0; + if ((reg & 0xff) != SBIC_ASR_INT) + return 0; + + return 1; +} + +/* + * Attach the wdsc driver + */ +void +wdsc_attach(struct device *parent, struct device *self, void *aux) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)self; + struct wd33c93_softc *sc = &wsc->sc_wd33c93; + struct hpc_attach_args *haa = aux; + int err; + + sc->sc_regt = haa->ha_st; + wsc->sc_dmat = haa->ha_dmat; + + wsc->sc_hpcdma.hpc = haa->hpc_regs; + + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff + 3, 1, &sc->sc_asr_regh)) != 0) { + printf(": unable to map asr reg, err=%d\n", err); + return; + } + + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff + 3 + 4, 1, &sc->sc_data_regh)) != 0) { + printf(": unable to map asr reg, err=%d\n", err); + return; + } + + if (bus_dmamap_create(wsc->sc_dmat, MAXPHYS, + wsc->sc_hpcdma.hpc->scsi_dma_segs, + wsc->sc_hpcdma.hpc->scsi_dma_segs_size, + wsc->sc_hpcdma.hpc->scsi_dma_segs_size, + BUS_DMA_WAITOK, &wsc->sc_dmamap) != 0) { + printf(": failed to create dmamap\n"); + return; + } + + sc->sc_dmasetup = wdsc_dmasetup; + sc->sc_dmago = wdsc_dmago; + sc->sc_dmastop = wdsc_dmastop; + sc->sc_reset = wdsc_reset; + + sc->sc_id = 0; /* Host ID = 0 */ + sc->sc_clkfreq = 200; /* 20MHz */ + sc->sc_dmamode = SBIC_CTL_BURST_DMA; + + if (int2_intr_establish(haa->ha_irq, IPL_BIO, + wd33c93_intr, wsc, self->dv_xname) == NULL) { + printf(": unable to establish interrupt!\n"); + return; + } + + hpcdma_init(haa, &wsc->sc_hpcdma, wsc->sc_hpcdma.hpc->scsi_dma_segs); + wd33c93_attach(sc, &wdsc_switch); +} + +/* + * Prime the hardware for a DMA transfer + * + * Requires splbio() interrupts to be disabled by the caller + */ +int +wdsc_dmasetup(struct wd33c93_softc *sc, void **addr, size_t *len, int datain, + size_t *dmasize) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + int count, err; + void *vaddr; + + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); + + vaddr = *addr; + count = dsc->sc_dlen = *len; + if (count) { + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED) == 0); + + /* Build list of physical addresses for this transfer */ + if ((err = bus_dmamap_load(wsc->sc_dmat, wsc->sc_dmamap, + vaddr, count, NULL /* kernel address */, + BUS_DMA_NOWAIT)) != 0) + panic("%s: bus_dmamap_load err=%d", + sc->sc_dev.dv_xname, err); + + hpcdma_sglist_create(dsc, wsc->sc_dmamap); + wsc->sc_flags |= WDSC_DMA_MAPLOADED; + + if (datain) { + dsc->sc_dmacmd = + wsc->sc_hpcdma.hpc->scsi_dma_datain_cmd; + dsc->sc_flags |= HPCDMA_READ; + } else { + dsc->sc_dmacmd = + wsc->sc_hpcdma.hpc->scsi_dma_dataout_cmd; + dsc->sc_flags &= ~HPCDMA_READ; + } + } + return count; +} + +/* + * Prime the hardware for the next DMA transfer + */ +int +wdsc_dmago(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + if (dsc->sc_dlen == 0) + return 0; + + KASSERT((wsc->sc_flags & WDSC_DMA_ACTIVE) == 0); + KASSERT((wsc->sc_flags & WDSC_DMA_MAPLOADED)); + + wsc->sc_flags |= WDSC_DMA_ACTIVE; + + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, + 0, wsc->sc_dmamap->dm_mapsize, + (dsc->sc_flags & HPCDMA_READ) ? + BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE); + + hpcdma_cntl(dsc, dsc->sc_dmacmd); /* Thunderbirds are go! */ + + return wsc->sc_dmamap->dm_mapsize; +} + +/* + * Stop DMA and unload active DMA maps + */ +void +wdsc_dmastop(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + if (wsc->sc_flags & WDSC_DMA_ACTIVE) { + if (dsc->sc_flags & HPCDMA_READ) + hpcdma_flush(dsc); + hpcdma_cntl(dsc, 0); /* Stop DMA */ + bus_dmamap_sync(wsc->sc_dmat, wsc->sc_dmamap, + 0, wsc->sc_dmamap->dm_mapsize, + (dsc->sc_flags & HPCDMA_READ) ? + BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE); + } + if (wsc->sc_flags & WDSC_DMA_MAPLOADED) + bus_dmamap_unload(wsc->sc_dmat, wsc->sc_dmamap); + wsc->sc_flags &= ~(WDSC_DMA_ACTIVE | WDSC_DMA_MAPLOADED); +} + +/* + * Reset the controller. + */ +void +wdsc_reset(struct wd33c93_softc *sc) +{ + struct wdsc_softc *wsc = (struct wdsc_softc *)sc; + struct hpc_dma_softc *dsc = &wsc->sc_hpcdma; + + hpcdma_reset(dsc); +} diff --git a/sys/arch/sgi/hpc/z8530sc.c b/sys/arch/sgi/hpc/z8530sc.c new file mode 100644 index 00000000000..fbec53846b4 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530sc.c @@ -0,0 +1,409 @@ +/* $OpenBSD: z8530sc.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530sc.c,v 1.30 2009/05/22 03:51:30 mrg 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. 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. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * 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. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (common part) + * + * This file contains the machine-independent parts of the + * driver common to tty and keyboard/mouse sub-drivers. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/ic/z8530reg.h> +#include <machine/z8530var.h> + +void +zs_break(struct zs_chanstate *cs, int set) +{ + + if (set) { + cs->cs_preg[5] |= ZSWR5_BREAK; + cs->cs_creg[5] |= ZSWR5_BREAK; + } else { + cs->cs_preg[5] &= ~ZSWR5_BREAK; + cs->cs_creg[5] &= ~ZSWR5_BREAK; + } + zs_write_reg(cs, 5, cs->cs_creg[5]); +} + + +/* + * drain on-chip fifo + */ +void +zs_iflush(struct zs_chanstate *cs) +{ + uint8_t c, rr0, rr1; + int i; + + /* + * Count how many times we loop. Some systems, such as some + * Apple PowerBooks, claim to have SCC's which they really don't. + */ + for (i = 0; i < 32; i++) { + /* Is there input available? */ + rr0 = zs_read_csr(cs); + if ((rr0 & ZSRR0_RX_READY) == 0) + break; + + /* + * First read the status, because reading the data + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (rr1 & (ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + } +} + + +/* + * Write the given register set to the given zs channel in the proper order. + * The channel must not be transmitting at the time. The receiver will + * be disabled for the time it takes to write all the registers. + * Call this with interrupts disabled. + */ +void +zs_loadchannelregs(struct zs_chanstate *cs) +{ + uint8_t *reg, v; + + zs_write_csr(cs, ZSM_RESET_ERR); /* XXX: reset error condition */ + +#if 1 + /* + * XXX: Is this really a good idea? + * XXX: Should go elsewhere! -gwr + */ + zs_iflush(cs); /* XXX */ +#endif + + if (cs->cs_ctl_chan != NULL) + v = ((cs->cs_ctl_chan->cs_creg[5] & (ZSWR5_RTS | ZSWR5_DTR)) != + (cs->cs_ctl_chan->cs_preg[5] & (ZSWR5_RTS | ZSWR5_DTR))); + else + v = 0; + + if (memcmp((void *)cs->cs_preg, (void *)cs->cs_creg, 16) == 0 && !v) + return; /* only change if values are different */ + + /* Copy "pending" regs to "current" */ + memcpy((void *)cs->cs_creg, (void *)cs->cs_preg, 16); + reg = cs->cs_creg; /* current regs */ + + /* disable interrupts */ + zs_write_reg(cs, 1, reg[1] & ~ZSWR1_IMASK); + + /* baud clock divisor, stop bits, parity */ + zs_write_reg(cs, 4, reg[4]); + + /* misc. TX/RX control bits */ + zs_write_reg(cs, 10, reg[10]); + + /* char size, enable (RX/TX) */ + zs_write_reg(cs, 3, reg[3] & ~ZSWR3_RX_ENABLE); + zs_write_reg(cs, 5, reg[5] & ~ZSWR5_TX_ENABLE); + + /* synchronous mode stuff */ + zs_write_reg(cs, 6, reg[6]); + zs_write_reg(cs, 7, reg[7]); + +#if 0 + /* + * Registers 2 and 9 are special because they are + * actually common to both channels, but must be + * programmed through channel A. The "zsc" attach + * function takes care of setting these registers + * and they should not be touched thereafter. + */ + /* interrupt vector */ + zs_write_reg(cs, 2, reg[2]); + /* master interrupt control */ + zs_write_reg(cs, 9, reg[9]); +#endif + + /* Shut down the BRG */ + zs_write_reg(cs, 14, reg[14] & ~ZSWR14_BAUD_ENA); + +#ifdef ZS_MD_SETCLK + /* Let the MD code setup any external clock. */ + ZS_MD_SETCLK(cs); +#endif /* ZS_MD_SETCLK */ + + /* clock mode control */ + zs_write_reg(cs, 11, reg[11]); + + /* baud rate (lo/hi) */ + zs_write_reg(cs, 12, reg[12]); + zs_write_reg(cs, 13, reg[13]); + + /* Misc. control bits */ + zs_write_reg(cs, 14, reg[14]); + + /* which lines cause status interrupts */ + zs_write_reg(cs, 15, reg[15]); + + /* + * Zilog docs recommend resetting external status twice at this + * point. Mainly as the status bits are latched, and the first + * interrupt clear might unlatch them to new values, generating + * a second interrupt request. + */ + zs_write_csr(cs, ZSM_RESET_STINT); + zs_write_csr(cs, ZSM_RESET_STINT); + + /* char size, enable (RX/TX)*/ + zs_write_reg(cs, 3, reg[3]); + zs_write_reg(cs, 5, reg[5]); + + /* Write the status bits on the alternate channel also. */ + if (cs->cs_ctl_chan != NULL) { + v = cs->cs_ctl_chan->cs_preg[5]; + cs->cs_ctl_chan->cs_creg[5] = v; + zs_write_reg(cs->cs_ctl_chan, 5, v); + } + + /* interrupt enables: RX, TX, STATUS */ + zs_write_reg(cs, 1, reg[1]); +} + +/* + * ZS hardware interrupt. Scan all ZS channels. NB: we know here that + * channels are kept in (A,B) pairs. + * + * Do just a little, then get out; set a software interrupt if more + * work is needed. + * + * We deliberately ignore the vectoring Zilog gives us, and match up + * only the number of `reset interrupt under service' operations, not + * the order. + */ +int +zsc_intr_hard(void *arg) +{ + struct zsc_softc *zsc = arg; + struct zs_chanstate *cs0, *cs1; + int handled; + uint8_t rr3; + + handled = 0; + + /* First look at channel A. */ + cs0 = zsc->zsc_cs[0]; + cs1 = zsc->zsc_cs[1]; + + /* + * We have to clear interrupt first to avoid a race condition, + * but it will be done in each MD handler. + */ + for (;;) { + /* Note: only channel A has an RR3 */ + rr3 = zs_read_reg(cs0, 3); + + if ((rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT | + ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) == 0) { + break; + } + handled = 1; + + /* First look at channel A. */ + if (rr3 & (ZSRR3_IP_A_RX | ZSRR3_IP_A_TX | ZSRR3_IP_A_STAT)) + zs_write_csr(cs0, ZSWR0_CLR_INTR); + + if (rr3 & ZSRR3_IP_A_RX) + (*cs0->cs_ops->zsop_rxint)(cs0); + if (rr3 & ZSRR3_IP_A_STAT) + (*cs0->cs_ops->zsop_stint)(cs0, 0); + if (rr3 & ZSRR3_IP_A_TX) + (*cs0->cs_ops->zsop_txint)(cs0); + + /* Now look at channel B. */ + if (rr3 & (ZSRR3_IP_B_RX | ZSRR3_IP_B_TX | ZSRR3_IP_B_STAT)) + zs_write_csr(cs1, ZSWR0_CLR_INTR); + + if (rr3 & ZSRR3_IP_B_RX) + (*cs1->cs_ops->zsop_rxint)(cs1); + if (rr3 & ZSRR3_IP_B_STAT) + (*cs1->cs_ops->zsop_stint)(cs1, 0); + if (rr3 & ZSRR3_IP_B_TX) + (*cs1->cs_ops->zsop_txint)(cs1); + } + + /* Note: caller will check cs_x->cs_softreq and DTRT. */ + return handled; +} + + +/* + * ZS software interrupt. Scan all channels for deferred interrupts. + */ +int +zsc_intr_soft(void *arg) +{ + struct zsc_softc *zsc = arg; + struct zs_chanstate *cs; + int rval, chan; + + rval = 0; + for (chan = 0; chan < 2; chan++) { + cs = zsc->zsc_cs[chan]; + + /* + * The softint flag can be safely cleared once + * we have decided to call the softint routine. + * (No need to do splzs() first.) + */ + if (cs->cs_softreq) { + cs->cs_softreq = 0; + (*cs->cs_ops->zsop_softint)(cs); + rval++; + } + } + return (rval); +} + +/* + * Provide a null zs "ops" vector. + */ + +static void zsnull_rxint (struct zs_chanstate *); +static void zsnull_stint (struct zs_chanstate *, int); +static void zsnull_txint (struct zs_chanstate *); +static void zsnull_softint(struct zs_chanstate *); + +static void +zsnull_rxint(struct zs_chanstate *cs) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_stint(struct zs_chanstate *cs, int force) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_txint(struct zs_chanstate *cs) +{ + + /* Ask for softint() call. */ + cs->cs_softreq = 1; +} + +static void +zsnull_softint(struct zs_chanstate *cs) +{ + + zs_write_reg(cs, 1, 0); + zs_write_reg(cs, 15, 0); +} + +struct zsops zsops_null = { + zsnull_rxint, /* receive char available */ + zsnull_stint, /* external/status */ + zsnull_txint, /* xmit buffer empty */ + zsnull_softint, /* process software interrupt */ +}; diff --git a/sys/arch/sgi/hpc/z8530sc.h b/sys/arch/sgi/hpc/z8530sc.h new file mode 100644 index 00000000000..d4b707ad518 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530sc.h @@ -0,0 +1,199 @@ +/* $OpenBSD: z8530sc.h,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530sc.h,v 1.26 2009/05/22 03:51:30 mrg 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. 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. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * 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. + * + * @(#)zsvar.h 8.1 (Berkeley) 6/11/93 + */ + +/* + * Function vector - per channel + */ +struct zs_chanstate; +struct zsops { + void (*zsop_rxint)(struct zs_chanstate *); + /* receive char available */ + void (*zsop_stint)(struct zs_chanstate *, int); + /* external/status */ + void (*zsop_txint)(struct zs_chanstate *); + /* xmit buffer empty */ + void (*zsop_softint)(struct zs_chanstate *); + /* process software interrupt */ +}; + +extern struct zsops zsops_null; + + +/* + * Software state, per zs channel. + */ +struct zs_chanstate { + + /* Pointers to the device registers. */ + volatile uint8_t *cs_reg_csr; /* ctrl, status, and reg. number. */ + volatile uint8_t *cs_reg_data; /* data or numbered register */ + + int cs_channel; /* sub-unit number */ + void *cs_private; /* sub-driver data pointer */ + struct zsops *cs_ops; + + int cs_brg_clk; /* BAUD Rate Generator clock + * (usually PCLK / 16) */ + int cs_defspeed; /* default baud rate */ + int cs_defcflag; /* default cflag */ + + /* + * We must keep a copy of the write registers as they are + * mostly write-only and we sometimes need to set and clear + * individual bits (e.g., in WR3). Not all of these are + * needed but 16 bytes is cheap and this makes the addressing + * simpler. Unfortunately, we can only write to some registers + * when the chip is not actually transmitting, so whenever + * we are expecting a `transmit done' interrupt the preg array + * is allowed to `get ahead' of the current values. In a + * few places we must change the current value of a register, + * rather than (or in addition to) the pending value; for these + * cs_creg[] contains the current value. + */ + uint8_t cs_creg[16]; /* current values */ + uint8_t cs_preg[16]; /* pending values */ + int cs_heldchange; /* change pending (creg != preg) */ + + uint8_t cs_rr0; /* last rr0 processed */ + uint8_t cs_rr0_delta; /* rr0 changes at status intr. */ + uint8_t cs_rr0_mask; /* rr0 bits that stop output */ + uint8_t cs_rr0_dcd; /* which bit to read as DCD */ + uint8_t cs_rr0_cts; /* which bit to read as CTS */ + uint8_t cs_rr0_pps; /* which bit to use for PPS */ + /* the above is set only while CRTSCTS is enabled. */ + + uint8_t cs_wr5_dtr; /* which bit to write as DTR */ + uint8_t cs_wr5_rts; /* which bit to write as RTS */ + /* the above is set only while CRTSCTS is enabled. */ + + volatile uint8_t cs_softreq; /* need soft interrupt call */ + char cs_cua; /* CUA mode flag */ + + /* + * For strange systems that have oddly wired serial ports, we + * provide a pointer to the channel state of the port that has + * our status lines on it. + */ + struct zs_chanstate *cs_ctl_chan; + + /* power management hooks */ + int (*enable)(struct zs_chanstate *); + void (*disable)(struct zs_chanstate *); + int enabled; + + /* MD code might define a larger variant of this. */ +}; + +struct consdev; +struct zsc_attach_args { + int channel; /* two serial channels per zsc */ + int hwflags; /* see definitions below */ + /* `consdev' is only valid if ZS_HWFLAG_USE_CONSDEV is set */ + struct consdev *consdev; +}; + +/* In case of split console devices, use these: */ +#define ZS_HWFLAG_CONSOLE_INPUT 1 +#define ZS_HWFLAG_CONSOLE_OUTPUT 2 +#define ZS_HWFLAG_CONSOLE \ + (ZS_HWFLAG_CONSOLE_INPUT | ZS_HWFLAG_CONSOLE_OUTPUT) +#define ZS_HWFLAG_NO_DCD 4 /* Ignore the DCD bit */ +#define ZS_HWFLAG_NO_CTS 8 /* Ignore the CTS bit */ +#define ZS_HWFLAG_RAW 16 /* advise raw mode */ +#define ZS_HWFLAG_USE_CONSDEV 32 /* Use console ops from `consdev' */ +#define ZS_HWFLAG_NORESET 64 /* Don't reset at attach time */ + +extern int zs_major; + +int zsc_intr_soft(void *); +int zsc_intr_hard(void *); + +void zs_abort(struct zs_chanstate *); +void zs_break(struct zs_chanstate *, int); +void zs_iflush(struct zs_chanstate *); +void zs_loadchannelregs(struct zs_chanstate *); +int zs_set_speed(struct zs_chanstate *, int); +int zs_set_modes(struct zs_chanstate *, int); diff --git a/sys/arch/sgi/hpc/z8530tty.c b/sys/arch/sgi/hpc/z8530tty.c new file mode 100644 index 00000000000..4108ed6ea10 --- /dev/null +++ b/sys/arch/sgi/hpc/z8530tty.c @@ -0,0 +1,1663 @@ +/* $OpenBSD: z8530tty.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: z8530tty.c,v 1.128 2011/04/24 16:27:00 rmind Exp $ */ + +/*- + * Copyright (c) 1993, 1994, 1995, 1996, 1997, 1998, 1999 + * Charles M. Hannum. 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 Charles M. Hannum. + * 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. + */ + +/* + * 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. 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. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Copyright (c) 1994 Gordon W. Ross + * + * 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. + * + * @(#)zs.c 8.1 (Berkeley) 7/19/93 + */ + +/* + * Zilog Z8530 Dual UART driver (tty interface) + * + * This is the "slave" driver that will be attached to + * the "zsc" driver for plain "tty" async. serial lines. + * + * Credits, history: + * + * The original version of this code was the sparc/dev/zs.c driver + * as distributed with the Berkeley 4.4 Lite release. Since then, + * Gordon Ross reorganized the code into the current parent/child + * driver scheme, separating the Sun keyboard and mouse support + * into independent child drivers. + * + * RTS/CTS flow-control support was a collaboration of: + * Gordon Ross <gwr@NetBSD.org>, + * Bill Studenmund <wrstuden@loki.stanford.edu> + * Ian Dall <Ian.Dall@dsto.defence.gov.au> + * + * The driver was massively overhauled in November 1997 by Charles Hannum, + * fixing *many* bugs, and substantially improving performance. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/proc.h> +#include <sys/device.h> +#include <sys/conf.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/kernel.h> +#include <sys/syslog.h> + +#include <dev/ic/z8530reg.h> +#include <machine/z8530var.h> + +#include <dev/cons.h> + +/* + * How many input characters we can buffer. + * The port-specific var.h may override this. + * Note: must be a power of two! + */ +#ifndef ZSTTY_RING_SIZE +#define ZSTTY_RING_SIZE 2048 +#endif + +struct cfdriver zstty_cd = { + NULL, "zstty", DV_TTY +}; + +/* + * Make this an option variable one can patch. + * But be warned: this must be a power of 2! + */ +u_int zstty_rbuf_size = ZSTTY_RING_SIZE; + +/* Stop input when 3/4 of the ring is full; restart when only 1/4 is full. */ +u_int zstty_rbuf_hiwat = (ZSTTY_RING_SIZE * 1) / 4; +u_int zstty_rbuf_lowat = (ZSTTY_RING_SIZE * 3) / 4; + +struct zstty_softc { + struct device zst_dev; /* required first: base device */ + struct tty *zst_tty; + struct zs_chanstate *zst_cs; + + struct timeout zst_diag_ch; + + u_int zst_overflows, + zst_floods, + zst_errors; + + int zst_hwflags, /* see z8530var.h */ + zst_swflags; /* TIOCFLAG_SOFTCAR, ... <ttycom.h> */ + + u_int zst_r_hiwat, + zst_r_lowat; + uint8_t *volatile zst_rbget, + *volatile zst_rbput; + volatile u_int zst_rbavail; + uint8_t *zst_rbuf, + *zst_ebuf; + + /* + * The transmit byte count and address are used for pseudo-DMA + * output in the hardware interrupt code. PDMA can be suspended + * to get pending changes done; heldtbc is used for this. It can + * also be stopped for ^S; this sets TS_TTSTOP in tp->t_state. + */ + uint8_t *zst_tba; /* transmit buffer address */ + u_int zst_tbc, /* transmit byte count */ + zst_heldtbc; /* held tbc while xmission stopped */ + + /* Flags to communicate with zstty_softint() */ + volatile uint8_t zst_rx_flags, /* receiver blocked */ +#define RX_TTY_BLOCKED 0x01 +#define RX_TTY_OVERFLOWED 0x02 +#define RX_IBUF_BLOCKED 0x04 +#define RX_IBUF_OVERFLOWED 0x08 +#define RX_ANY_BLOCK 0x0f + zst_tx_busy, /* working on an output chunk */ + zst_tx_done, /* done with one output chunk */ + zst_tx_stopped, /* H/W level stop (lost CTS) */ + zst_st_check, /* got a status interrupt */ + zst_rx_ready; + + /* PPS signal on DCD, with or without inkernel clock disciplining */ + uint8_t zst_ppsmask; /* pps signal mask */ + uint8_t zst_ppsassert; /* pps leading edge */ + uint8_t zst_ppsclear; /* pps trailing edge */ +}; + +/* Definition of the driver for autoconfig. */ +int zstty_match(struct device *, void *, void *); +void zstty_attach(struct device *, struct device *, void *); + +const struct cfattach zstty_ca = { + sizeof(struct zstty_softc), zstty_match, zstty_attach +}; + +cdev_decl(zs); + +struct zsops zsops_tty; + +void zs_shutdown(struct zstty_softc *); +void zsstart(struct tty *); +int zsparam(struct tty *, struct termios *); +void zs_modem(struct zstty_softc *, int); +void tiocm_to_zs(struct zstty_softc *, u_long, int); +int zs_to_tiocm(struct zstty_softc *); +int zshwiflow(struct tty *, int); +void zs_hwiflow(struct zstty_softc *); +void zs_maskintr(struct zstty_softc *); + +struct zstty_softc *zs_device_lookup(struct cfdriver *, int); + +/* Low-level routines. */ +void zstty_rxint (struct zs_chanstate *); +void zstty_stint (struct zs_chanstate *, int); +void zstty_txint (struct zs_chanstate *); +void zstty_softint(struct zs_chanstate *); +void zstty_diag(void *); + +#define ZSUNIT(x) (minor(x) & 0x7f) +#define ZSDIALOUT(x) (minor(x) & 0x80) + +struct zstty_softc * +zs_device_lookup(struct cfdriver *cf, int unit) +{ + return (struct zstty_softc *)device_lookup(cf, unit); +} + +/* + * zstty_match: how is this zs channel configured? + */ +int +zstty_match(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = vcf; + struct zsc_attach_args *args = aux; + + /* Exact match is better than wildcard. */ + if (cf->cf_loc[0] == args->channel) + return 2; + + /* This driver accepts wildcard. */ + if (cf->cf_loc[0] == -1) + return 1; + + return 0; +} + +void +zstty_attach(struct device *parent, struct device *self, void *aux) +{ + struct zsc_softc *zsc = (struct zsc_softc *)parent; + struct zstty_softc *zst = (struct zstty_softc *)self; + struct cfdata *cf = self->dv_cfdata; + struct zsc_attach_args *args = aux; + struct zs_chanstate *cs; + struct tty *tp; + int channel, s, tty_unit; + dev_t dev; + const char *i, *o; + int dtr_on; + int resetbit; + + timeout_set(&zst->zst_diag_ch, zstty_diag, zst); + + tty_unit = zst->zst_dev.dv_unit; + channel = args->channel; + cs = zsc->zsc_cs[channel]; + cs->cs_private = zst; + cs->cs_ops = &zsops_tty; + + zst->zst_cs = cs; + zst->zst_swflags = cf->cf_flags; /* softcar, etc. */ + zst->zst_hwflags = args->hwflags; + dev = makedev(zs_major, tty_unit); + + if (zst->zst_swflags) + printf(" flags 0x%x", zst->zst_swflags); + + /* + * Check whether we serve as a console device. + * XXX - split console input/output channels aren't + * supported yet on /dev/console + */ + i = o = NULL; + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) != 0) { + i = " input"; + if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) { + args->consdev->cn_dev = dev; + cn_tab->cn_pollc = args->consdev->cn_pollc; + cn_tab->cn_getc = args->consdev->cn_getc; + } + cn_tab->cn_dev = dev; + } + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_OUTPUT) != 0) { + o = " output"; + if ((args->hwflags & ZS_HWFLAG_USE_CONSDEV) != 0) { + cn_tab->cn_putc = args->consdev->cn_putc; + } + cn_tab->cn_dev = dev; + } + if (i != NULL || o != NULL) + printf(": console%s", i ? (o ? "" : i) : o); + +#ifdef KGDB + if (zs_check_kgdb(cs, dev)) { + /* + * Allow kgdb to "take over" this port. Returns true + * if this serial port is in-use by kgdb. + */ + printf(" (kgdb)\n"); + /* + * This is the kgdb port (exclusive use) + * so skip the normal attach code. + */ + return; + } +#endif + + printf("\n"); + + tp = ttymalloc(0); + tp->t_dev = dev; + tp->t_oproc = zsstart; + tp->t_param = zsparam; + tp->t_hwiflow = zshwiflow; + + zst->zst_tty = tp; + zst->zst_rbuf = malloc(zstty_rbuf_size << 1, M_DEVBUF, M_WAITOK); + zst->zst_ebuf = zst->zst_rbuf + (zstty_rbuf_size << 1); + /* Disable the high water mark. */ + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + + /* if there are no enable/disable functions, assume the device + is always enabled */ + if (!cs->enable) + cs->enabled = 1; + + /* + * Hardware init + */ + dtr_on = 0; + resetbit = 0; + if (ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + /* Call zsparam similar to open. */ + struct termios t; + + /* Wait a while for previous console output to complete */ + DELAY(10000); + + /* Setup the "new" parameters in t. */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + + s = splzs(); + + /* + * Turn on receiver and status interrupts. + * We defer the actual write of the register to zsparam(), + * but we must make sure status interrupts are turned on by + * the time zsparam() reads the initial rr0 state. + */ + SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); + + splx(s); + + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void)zsparam(tp, &t); + + /* Make sure DTR is on now. */ + dtr_on = 1; + } else if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_NORESET)) { + /* Not the console; may need reset. */ + resetbit = (channel == 0) ? ZSWR9_A_RESET : ZSWR9_B_RESET; + } + + s = splzs(); + if (resetbit) + zs_write_reg(cs, 9, resetbit); + zs_modem(zst, dtr_on); + splx(s); +} + + +/* + * Return pointer to our tty. + */ +struct tty * +zstty(dev_t dev) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + + return (zst->zst_tty); +} + + +void +zs_shutdown(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int s; + + s = splzs(); + + /* If we were asserting flow control, then deassert it. */ + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + + /* Clear any break condition set with TIOCSBRK. */ + zs_break(cs, 0); + + /* Turn off PPS capture on last close. */ + zst->zst_ppsmask = 0; + + /* + * Hang up if necessary. Wait a bit, so the other side has time to + * notice even if we immediately open the port again. + */ + if (ISSET(tp->t_cflag, HUPCL) || ISSET(tp->t_state, TS_WOPEN)) { + zs_modem(zst, 0); + /* hold low for 1 second */ + (void)tsleep(cs, TTIPRI, ttclos, hz); + } + + /* Turn off interrupts if not the console. */ + if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + CLR(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_TIE | ZSWR1_SIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + + /* Call the power management hook. */ + if (cs->disable) { +#ifdef DIAGNOSTIC + if (!cs->enabled) + panic("%s: not enabled?", __func__); +#endif + (*cs->disable)(zst->zst_cs); + } + + splx(s); +} + +/* + * Open a zs serial (tty) port. + */ +int +zsopen(dev_t dev, int flags, int mode, struct proc *p) +{ + struct zstty_softc *zst; + struct zs_chanstate *cs; + struct tty *tp; + int s, s2; + int error; + + zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + if (zst == NULL) + return (ENXIO); + + tp = zst->zst_tty; + cs = zst->zst_cs; + + /* If KGDB took the line, then tp==NULL */ + if (tp == NULL) + return (EBUSY); + + if (ISSET(tp->t_state, TS_ISOPEN) && + ISSET(tp->t_state, TS_XCLUDE) && + suser(p, 0) != 0) + return (EBUSY); + + s = spltty(); + + /* + * Do the following iff this is a first open. + */ + if (!ISSET(tp->t_state, TS_ISOPEN)) { + struct termios t; + + tp->t_dev = dev; + + /* Call the power management hook. */ + if (cs->enable) { + if ((*cs->enable)(cs)) { + splx(s); + printf("%s: device enable failed\n", + zst->zst_dev.dv_xname); + return (EIO); + } + } + + /* + * Initialize the termios status to the defaults. Add in the + * sticky bits from TIOCSFLAGS. + */ + t.c_ispeed = 0; + t.c_ospeed = cs->cs_defspeed; + t.c_cflag = cs->cs_defcflag; + if (ISSET(zst->zst_swflags, TIOCFLAG_CLOCAL)) + SET(t.c_cflag, CLOCAL); + if (ISSET(zst->zst_swflags, TIOCFLAG_CRTSCTS)) + SET(t.c_cflag, CRTSCTS); + if (ISSET(zst->zst_swflags, TIOCFLAG_MDMBUF)) + SET(t.c_cflag, MDMBUF); + + s2 = splzs(); + + /* + * Turn on receiver and status interrupts. + * We defer the actual write of the register to zsparam(), + * but we must make sure status interrupts are turned on by + * the time zsparam() reads the initial rr0 state. + */ + SET(cs->cs_preg[1], ZSWR1_RIE | ZSWR1_SIE); + + /* Clear PPS capture state on first open. */ + zst->zst_ppsmask = 0; + + splx(s2); + + /* Make sure zsparam will see changes. */ + tp->t_ospeed = 0; + (void)zsparam(tp, &t); + + /* + * Note: zsparam has done: cflag, ispeed, ospeed + * so we just need to do: iflag, oflag, lflag, cc + * For "raw" mode, just leave all zeros. + */ + if (!ISSET(zst->zst_hwflags, ZS_HWFLAG_RAW)) { + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + tp->t_lflag = TTYDEF_LFLAG; + } else { + tp->t_iflag = 0; + tp->t_oflag = 0; + tp->t_lflag = 0; + } + ttychars(tp); + ttsetwater(tp); + + if (ZSDIALOUT(dev)) + SET(tp->t_state, TS_CARR_ON); + else + CLR(tp->t_state, TS_CARR_ON); + + s2 = splzs(); + + /* Clear the input ring, and unblock. */ + zst->zst_rbget = zst->zst_rbput = zst->zst_rbuf; + zst->zst_rbavail = zstty_rbuf_size; + zs_iflush(cs); + CLR(zst->zst_rx_flags, RX_ANY_BLOCK); + zs_hwiflow(zst); + + splx(s2); + } + + if (ZSDIALOUT(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + /* someone already is dialed in... */ + splx(s); + return EBUSY; + } + cs->cs_cua = 1; + } + + error = 0; + /* wait for carrier if necessary */ + if (ISSET(flags, O_NONBLOCK)) { + if (!ZSDIALOUT(dev) && cs->cs_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + error = EBUSY; + } + } else + while (cs->cs_cua || + (!ISSET(tp->t_cflag, CLOCAL) && !ISSET(tp->t_state, TS_CARR_ON))) { + int rr0; + + error = 0; + SET(tp->t_state, TS_WOPEN); + + if (!ZSDIALOUT(dev) && !cs->cs_cua) { + /* + * Turn on DTR. We must always do this on non-CUA + * devices, even if carrier is not present, because + * otherwise we'd have to use TIOCSDTR immediately + * after setting CLOCAL, which applications do not + * expect. We always assert DTR while the device is + * open unless explicitly requested to deassert it. + */ + s2 = splzs(); + zs_modem(zst, 1); + rr0 = zs_read_csr(cs); + splx(s2); + + /* loop, turning on the device, until carrier present */ + if (ISSET(rr0, ZSRR0_DCD) || + ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR)) + SET(tp->t_state, TS_CARR_ON); + } + + if ((ISSET(tp->t_cflag, CLOCAL) || + ISSET(tp->t_state, TS_CARR_ON)) && !cs->cs_cua) + break; + + error = ttysleep(tp, (caddr_t)&tp->t_rawq, TTIPRI | PCATCH, + ttopen, 0); + + if (!ZSDIALOUT(dev) && cs->cs_cua && error == EINTR) { + error = 0; + continue; + } + + if (error) { + if (!ISSET(tp->t_state, TS_ISOPEN)) { + s2 = splzs(); + zs_modem(zst, 0); + splx(s2); + CLR(tp->t_state, TS_WOPEN); + ttwakeup(tp); + } + if (ZSDIALOUT(dev)) + cs->cs_cua = 0; + CLR(tp->t_state, TS_WOPEN); + break; + } + if (!ZSDIALOUT(dev) && cs->cs_cua) + continue; + } + + splx(s); + + if (error == 0) + error = ((*linesw[tp->t_line].l_open)(dev, tp, p)); + if (error) + goto bad; + + return (0); + +bad: + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * We failed to open the device, and nobody else had it opened. + * Clean up the state as appropriate. + */ + zs_shutdown(zst); + } + + return (error); +} + +/* + * Close a zs serial port. + */ +int +zsclose(dev_t dev, int flags, int mode, struct proc *p) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flags, p); + + s = spltty(); + cs->cs_cua = 0; + ttyclose(tp); + splx(s); + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + /* + * Although we got a last close, the device may still be in + * use; e.g. if this was the dialout node, and there are still + * processes waiting for carrier on the non-dialout node. + */ + zs_shutdown(zst); + } + + return (0); +} + +/* + * Read/write zs serial port. + */ +int +zsread(dev_t dev, struct uio *uio, int flags) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct tty *tp = zst->zst_tty; + + return (*linesw[tp->t_line].l_read)(tp, uio, flags); +} + +int +zswrite(dev_t dev, struct uio *uio, int flags) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct tty *tp = zst->zst_tty; + + return (*linesw[tp->t_line].l_write)(tp, uio, flags); +} + +int +zsioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(dev)); + struct zs_chanstate *cs = zst->zst_cs; + struct tty *tp = zst->zst_tty; + int error; + int s; + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + +#ifdef ZS_MD_IOCTL + error = ZS_MD_IOCTL; + if (error >= 0) + return (error); +#endif /* ZS_MD_IOCTL */ + + error = 0; + + s = splzs(); + + switch (cmd) { + case TIOCSBRK: + zs_break(cs, 1); + break; + + case TIOCCBRK: + zs_break(cs, 0); + break; + + case TIOCGFLAGS: + *(int *)data = zst->zst_swflags; + break; + + case TIOCSFLAGS: + error = suser(p, 0); + if (error) + break; + zst->zst_swflags = *(int *)data; + break; + + case TIOCSDTR: + zs_modem(zst, 1); + break; + + case TIOCCDTR: + zs_modem(zst, 0); + break; + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + tiocm_to_zs(zst, cmd, *(int *)data); + break; + + case TIOCMGET: + *(int *)data = zs_to_tiocm(zst); + break; + + default: + error = ENOTTY; + break; + } + + splx(s); + + return (error); +} + +/* + * Start or restart transmission. + */ +void +zsstart(struct tty *tp) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + u_char *tba; + int tbc; + int s; + + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY | TS_TIMEOUT | TS_TTSTOP)) + goto out; + if (zst->zst_tx_stopped) + goto out; + + ttwakeupwr(tp); + if (tp->t_outq.c_cc == 0) + goto out; + + /* Grab the first contiguous region of buffer space. */ + tba = tp->t_outq.c_cf; + tbc = ndqb(&tp->t_outq, 0); + + (void)splzs(); + + zst->zst_tba = tba; + zst->zst_tbc = tbc; + SET(tp->t_state, TS_BUSY); + zst->zst_tx_busy = 1; + + /* Enable transmit completion interrupts if necessary. */ + if (!ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + SET(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + + /* Output the first character of the contiguous buffer. */ + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + +out: + splx(s); +} + +/* + * Stop output, e.g., for ^S or output flush. + */ +int +zsstop(struct tty *tp, int flag) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + int s; + + s = splzs(); + if (ISSET(tp->t_state, TS_BUSY)) { + /* Stop transmitting at the next chunk. */ + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + if (!ISSET(tp->t_state, TS_TTSTOP)) + SET(tp->t_state, TS_FLUSH); + } + splx(s); + return 0; +} + +/* + * Set ZS tty parameters from termios. + * XXX - Should just copy the whole termios after + * making sure all the changes could be done. + */ +int +zsparam(struct tty *tp, struct termios *t) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + int ospeed; + tcflag_t cflag; + uint8_t tmp3, tmp4, tmp5; + int s, error; + + ospeed = t->c_ospeed; + cflag = t->c_cflag; + + /* Check requested parameters. */ + if (ospeed < 0) + return (EINVAL); + if (t->c_ispeed && t->c_ispeed != ospeed) + return (EINVAL); + + /* + * For the console, always force CLOCAL and !HUPCL, so that the port + * is always active. + */ + if (ISSET(zst->zst_swflags, TIOCFLAG_SOFTCAR) || + ISSET(zst->zst_hwflags, ZS_HWFLAG_CONSOLE)) { + SET(cflag, CLOCAL); + CLR(cflag, HUPCL); + } + + /* + * Only whack the UART when params change. + * Some callers need to clear tp->t_ospeed + * to make sure initialization gets done. + */ + if (tp->t_ospeed == ospeed && + tp->t_cflag == cflag) + return (0); + + /* + * Call MD functions to deal with changed + * clock modes or H/W flow control modes. + * The BRG divisor is set now. (reg 12,13) + */ + error = zs_set_speed(cs, ospeed); + if (error) + return (error); + error = zs_set_modes(cs, cflag); + if (error) + return (error); + + /* + * Block interrupts so that state will not + * be altered until we are done setting it up. + * + * Initial values in cs_preg are set before + * our attach routine is called. The master + * interrupt enable is handled by zsc.c + * + */ + s = splzs(); + + /* + * Recalculate which status ints to enable. + */ + zs_maskintr(zst); + + /* Recompute character size bits. */ + tmp3 = cs->cs_preg[3]; + tmp5 = cs->cs_preg[5]; + CLR(tmp3, ZSWR3_RXSIZE); + CLR(tmp5, ZSWR5_TXSIZE); + switch (ISSET(cflag, CSIZE)) { + case CS5: + SET(tmp3, ZSWR3_RX_5); + SET(tmp5, ZSWR5_TX_5); + break; + case CS6: + SET(tmp3, ZSWR3_RX_6); + SET(tmp5, ZSWR5_TX_6); + break; + case CS7: + SET(tmp3, ZSWR3_RX_7); + SET(tmp5, ZSWR5_TX_7); + break; + case CS8: + SET(tmp3, ZSWR3_RX_8); + SET(tmp5, ZSWR5_TX_8); + break; + } + cs->cs_preg[3] = tmp3; + cs->cs_preg[5] = tmp5; + + /* + * Recompute the stop bits and parity bits. Note that + * zs_set_speed() may have set clock selection bits etc. + * in wr4, so those must preserved. + */ + tmp4 = cs->cs_preg[4]; + CLR(tmp4, ZSWR4_SBMASK | ZSWR4_PARMASK); + if (ISSET(cflag, CSTOPB)) + SET(tmp4, ZSWR4_TWOSB); + else + SET(tmp4, ZSWR4_ONESB); + if (!ISSET(cflag, PARODD)) + SET(tmp4, ZSWR4_EVENP); + if (ISSET(cflag, PARENB)) + SET(tmp4, ZSWR4_PARENB); + cs->cs_preg[4] = tmp4; + + /* And copy to tty. */ + tp->t_ispeed = 0; + tp->t_ospeed = ospeed; + tp->t_cflag = cflag; + + /* + * If nothing is being transmitted, set up new current values, + * else mark them as pending. + */ + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } + + /* + * If hardware flow control is disabled, turn off the buffer water + * marks and unblock any soft flow control state. Otherwise, enable + * the water marks. + */ + if (!ISSET(cflag, CHWFLOW)) { + zst->zst_r_hiwat = 0; + zst->zst_r_lowat = 0; + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED|RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } else { + zst->zst_r_hiwat = zstty_rbuf_hiwat; + zst->zst_r_lowat = zstty_rbuf_lowat; + } + + /* + * Force a recheck of the hardware carrier and flow control status, + * since we may have changed which bits we're looking at. + */ + zstty_stint(cs, 1); + + splx(s); + + /* + * If hardware flow control is disabled, unblock any hard flow control + * state. + */ + if (!ISSET(cflag, CHWFLOW)) { + if (zst->zst_tx_stopped) { + zst->zst_tx_stopped = 0; + zsstart(tp); + } + } + + zstty_softint(cs); + + return (0); +} + +/* + * Compute interrupt enable bits and set in the pending bits. Called both + * in zsparam() and when PPS (pulse per second timing) state changes. + * Must be called at splzs(). + */ +void +zs_maskintr(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs; + uint8_t tmp15; + + cs->cs_rr0_mask = cs->cs_rr0_cts | cs->cs_rr0_dcd; + if (zst->zst_ppsmask != 0) + cs->cs_rr0_mask |= cs->cs_rr0_pps; + tmp15 = cs->cs_preg[15]; + if (ISSET(cs->cs_rr0_mask, ZSRR0_DCD)) + SET(tmp15, ZSWR15_DCD_IE); + else + CLR(tmp15, ZSWR15_DCD_IE); + if (ISSET(cs->cs_rr0_mask, ZSRR0_CTS)) + SET(tmp15, ZSWR15_CTS_IE); + else + CLR(tmp15, ZSWR15_CTS_IE); + cs->cs_preg[15] = tmp15; +} + + +/* + * Raise or lower modem control (DTR/RTS) signals. If a character is + * in transmission, the change is deferred. + * Called at splzs(). + */ +void +zs_modem(struct zstty_softc *zst, int onoff) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + + if (cs->cs_wr5_dtr == 0) + return; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + if (onoff) + SET(ccs->cs_preg[5], cs->cs_wr5_dtr); + else + CLR(ccs->cs_preg[5], cs->cs_wr5_dtr); + + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } +} + +/* + * Set modem bits. + * Called at splzs(). + */ +void +tiocm_to_zs(struct zstty_softc *zst, u_long how, int ttybits) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + uint8_t zsbits; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + zsbits = 0; + if (ISSET(ttybits, TIOCM_DTR)) + SET(zsbits, ZSWR5_DTR); + if (ISSET(ttybits, TIOCM_RTS)) + SET(zsbits, ZSWR5_RTS); + + switch (how) { + case TIOCMBIC: + CLR(ccs->cs_preg[5], zsbits); + break; + + case TIOCMBIS: + SET(ccs->cs_preg[5], zsbits); + break; + + case TIOCMSET: + CLR(ccs->cs_preg[5], ZSWR5_RTS | ZSWR5_DTR); + SET(ccs->cs_preg[5], zsbits); + break; + } + + if (!cs->cs_heldchange) { + if (zst->zst_tx_busy) { + zst->zst_heldtbc = zst->zst_tbc; + zst->zst_tbc = 0; + cs->cs_heldchange = 1; + } else + zs_loadchannelregs(cs); + } +} + +/* + * Get modem bits. + * Called at splzs(). + */ +int +zs_to_tiocm(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + uint8_t zsbits; + int ttybits = 0; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + zsbits = ccs->cs_preg[5]; + if (ISSET(zsbits, ZSWR5_DTR)) + SET(ttybits, TIOCM_DTR); + if (ISSET(zsbits, ZSWR5_RTS)) + SET(ttybits, TIOCM_RTS); + + zsbits = cs->cs_rr0; + if (ISSET(zsbits, ZSRR0_DCD)) + SET(ttybits, TIOCM_CD); + if (ISSET(zsbits, ZSRR0_CTS)) + SET(ttybits, TIOCM_CTS); + + return (ttybits); +} + +/* + * Try to block or unblock input using hardware flow-control. + * This is called by kern/tty.c if MDMBUF|CRTSCTS is set, and + * if this function returns non-zero, the TS_TBLOCK flag will + * be set or cleared according to the "block" arg passed. + */ +int +zshwiflow(struct tty *tp, int block) +{ + struct zstty_softc *zst = zs_device_lookup(&zstty_cd, ZSUNIT(tp->t_dev)); + struct zs_chanstate *cs = zst->zst_cs; + int s; + + if (cs->cs_wr5_rts == 0) + return (0); + + s = splzs(); + if (block) { + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + SET(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } else { + if (ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + if (ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_TTY_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + return (1); +} + +/* + * Internal version of zshwiflow + * Called at splzs() + */ +void +zs_hwiflow(struct zstty_softc *zst) +{ + struct zs_chanstate *cs = zst->zst_cs, *ccs; + + if (cs->cs_wr5_rts == 0) + return; + + ccs = (cs->cs_ctl_chan != NULL ? cs->cs_ctl_chan : cs); + + if (ISSET(zst->zst_rx_flags, RX_ANY_BLOCK)) { + CLR(ccs->cs_preg[5], cs->cs_wr5_rts); + CLR(ccs->cs_creg[5], cs->cs_wr5_rts); + } else { + SET(ccs->cs_preg[5], cs->cs_wr5_rts); + SET(ccs->cs_creg[5], cs->cs_wr5_rts); + } + zs_write_reg(ccs, 5, ccs->cs_creg[5]); +} + + +/**************************************************************** + * Interface to the lower layer (zscc) + ****************************************************************/ + +#define integrate static inline +integrate void zstty_rxsoft(struct zstty_softc *, struct tty *); +integrate void zstty_txsoft(struct zstty_softc *, struct tty *); +integrate void zstty_stsoft(struct zstty_softc *, struct tty *); +void zstty_diag(void *); + +/* + * Receiver Ready interrupt. + * Called at splzs(). + */ +void +zstty_rxint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + uint8_t *put, *end; + u_int cc; + uint8_t rr0, rr1, c; + + end = zst->zst_ebuf; + put = zst->zst_rbput; + cc = zst->zst_rbavail; + + while (cc > 0) { + /* + * First read the status, because reading the received char + * destroys the status of this char. + */ + rr1 = zs_read_reg(cs, 1); + c = zs_read_data(cs); + + if (ISSET(rr1, ZSRR1_FE | ZSRR1_DO | ZSRR1_PE)) { + /* Clear the receive error. */ + zs_write_csr(cs, ZSWR0_RESET_ERRORS); + } + + put[0] = c; + put[1] = rr1; + put += 2; + if (put >= end) + put = zst->zst_rbuf; + cc--; + + rr0 = zs_read_csr(cs); + if (!ISSET(rr0, ZSRR0_RX_READY)) + break; + } + + /* + * Current string of incoming characters ended because + * no more data was available or we ran out of space. + * Schedule a receive event if any data was received. + * If we're out of space, turn off receive interrupts. + */ + zst->zst_rbput = put; + zst->zst_rbavail = cc; + if (!ISSET(zst->zst_rx_flags, RX_TTY_OVERFLOWED)) { + zst->zst_rx_ready = 1; + cs->cs_softreq = 1; + } + + /* + * See if we are in danger of overflowing a buffer. If + * so, use hardware flow control to ease the pressure. + */ + if (!ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED) && + cc < zst->zst_r_hiwat) { + SET(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + + /* + * If we're out of space, disable receive interrupts + * until the queue has drained a bit. + */ + if (!cc) { + SET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + CLR(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } +} + +/* + * Transmitter Ready interrupt. + * Called at splzs(). + */ +void +zstty_txint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + + zs_write_csr(cs, ZSWR0_RESET_TXINT); + + /* + * If we've delayed a parameter change, do it now, and restart + * output. + */ + if (cs->cs_heldchange) { + zs_loadchannelregs(cs); + cs->cs_heldchange = 0; + zst->zst_tbc = zst->zst_heldtbc; + zst->zst_heldtbc = 0; + } + + /* Output the next character in the buffer, if any. */ + if (zst->zst_tbc > 0) { + zs_write_data(cs, *zst->zst_tba); + zst->zst_tbc--; + zst->zst_tba++; + } else { + /* Disable transmit completion interrupts if necessary. */ + if (ISSET(cs->cs_preg[1], ZSWR1_TIE)) { + CLR(cs->cs_preg[1], ZSWR1_TIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (zst->zst_tx_busy) { + zst->zst_tx_busy = 0; + zst->zst_tx_done = 1; + cs->cs_softreq = 1; + } + } +} + +#ifdef DDB +#include <ddb/db_var.h> +#define DB_CONSOLE db_console +#else +#define DB_CONSOLE 1 +#endif + +/* + * Status Change interrupt. + * Called at splzs(). + */ +void +zstty_stint(struct zs_chanstate *cs, int force) +{ + struct zstty_softc *zst = cs->cs_private; + struct tty *tp = zst->zst_tty; + uint8_t rr0, delta; + + rr0 = zs_read_csr(cs); + zs_write_csr(cs, ZSWR0_RESET_STATUS); + + /* + * Check here for console break, so that we can abort + * even when interrupts are locking up the machine. + */ + if ((zst->zst_hwflags & ZS_HWFLAG_CONSOLE_INPUT) && + ISSET(rr0, ZSRR0_BREAK) && DB_CONSOLE) + zs_abort(cs); + + if (!force) + delta = rr0 ^ cs->cs_rr0; + else + delta = cs->cs_rr0_mask; + + ttytstamp(tp, cs->cs_rr0 & ZSRR0_CTS, rr0 & ZSRR0_CTS, + cs->cs_rr0 & ZSRR0_DCD, rr0 & ZSRR0_DCD); + + cs->cs_rr0 = rr0; + + if (ISSET(delta, cs->cs_rr0_mask)) { + SET(cs->cs_rr0_delta, delta); + + /* + * Stop output immediately if we lose the output + * flow control signal or carrier detect. + */ + if (ISSET(~rr0, cs->cs_rr0_mask)) { + zst->zst_tbc = 0; + zst->zst_heldtbc = 0; + } + + zst->zst_st_check = 1; + cs->cs_softreq = 1; + } +} + +void +zstty_diag(void *arg) +{ + struct zstty_softc *zst = arg; + int overflows, floods; + int s; + + s = splzs(); + overflows = zst->zst_overflows; + zst->zst_overflows = 0; + floods = zst->zst_floods; + zst->zst_floods = 0; + zst->zst_errors = 0; + splx(s); + + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf flood%s\n", + zst->zst_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +integrate void +zstty_rxsoft(struct zstty_softc *zst, struct tty *tp) +{ + struct zs_chanstate *cs = zst->zst_cs; + int (*rint)(int, struct tty *) = linesw[tp->t_line].l_rint; + uint8_t *get, *end; + u_int cc, scc; + uint8_t rr1; + int code; + int s; + + end = zst->zst_ebuf; + get = zst->zst_rbget; + scc = cc = zstty_rbuf_size - zst->zst_rbavail; + + if (cc == zstty_rbuf_size) { + zst->zst_floods++; + if (zst->zst_errors++ == 0) + timeout_add_sec(&zst->zst_diag_ch, 60); + } + + /* If not yet open, drop the entire buffer content here */ + if (!ISSET(tp->t_state, TS_ISOPEN)) { + get += cc << 1; + if (get >= end) + get -= zstty_rbuf_size << 1; + cc = 0; + } + while (cc) { + code = get[0]; + rr1 = get[1]; + if (ISSET(rr1, ZSRR1_DO | ZSRR1_FE | ZSRR1_PE)) { + if (ISSET(rr1, ZSRR1_DO)) { + zst->zst_overflows++; + if (zst->zst_errors++ == 0) + timeout_add_sec(&zst->zst_diag_ch, 60); + } + if (ISSET(rr1, ZSRR1_FE)) + SET(code, TTY_FE); + if (ISSET(rr1, ZSRR1_PE)) + SET(code, TTY_PE); + } + if ((*rint)(code, tp) == -1) { + /* + * The line discipline's buffer is out of space. + */ + if (!ISSET(zst->zst_rx_flags, RX_TTY_BLOCKED)) { + /* + * We're either not using flow control, or the + * line discipline didn't tell us to block for + * some reason. Either way, we have no way to + * know when there's more space available, so + * just drop the rest of the data. + */ + get += cc << 1; + if (get >= end) + get -= zstty_rbuf_size << 1; + cc = 0; + } else { + /* + * Don't schedule any more receive processing + * until the line discipline tells us there's + * space available (through comhwiflow()). + * Leave the rest of the data in the input + * buffer. + */ + SET(zst->zst_rx_flags, RX_TTY_OVERFLOWED); + } + break; + } + get += 2; + if (get >= end) + get = zst->zst_rbuf; + cc--; + } + + if (cc != scc) { + zst->zst_rbget = get; + s = splzs(); + cc = zst->zst_rbavail += scc - cc; + /* Buffers should be ok again, release possible block. */ + if (cc >= zst->zst_r_lowat) { + if (ISSET(zst->zst_rx_flags, RX_IBUF_OVERFLOWED)) { + CLR(zst->zst_rx_flags, RX_IBUF_OVERFLOWED); + SET(cs->cs_preg[1], ZSWR1_RIE); + cs->cs_creg[1] = cs->cs_preg[1]; + zs_write_reg(cs, 1, cs->cs_creg[1]); + } + if (ISSET(zst->zst_rx_flags, RX_IBUF_BLOCKED)) { + CLR(zst->zst_rx_flags, RX_IBUF_BLOCKED); + zs_hwiflow(zst); + } + } + splx(s); + } +} + +integrate void +zstty_txsoft(struct zstty_softc *zst, struct tty *tp) +{ + int s; + + CLR(tp->t_state, TS_BUSY); + if (ISSET(tp->t_state, TS_FLUSH)) + CLR(tp->t_state, TS_FLUSH); + else { + s = splzs(); + ndflush(&tp->t_outq, (int)(zst->zst_tba - tp->t_outq.c_cf)); + splx(s); + } + (*linesw[tp->t_line].l_start)(tp); +} + +integrate void +zstty_stsoft(struct zstty_softc *zst, struct tty *tp) +{ + struct zs_chanstate *cs = zst->zst_cs; + uint8_t rr0, delta; + int s; + + s = splzs(); + rr0 = cs->cs_rr0; + delta = cs->cs_rr0_delta; + cs->cs_rr0_delta = 0; + splx(s); + + if (ISSET(delta, cs->cs_rr0_dcd)) { + /* + * Inform the tty layer that carrier detect changed. + */ + (void)(*linesw[tp->t_line].l_modem)(tp, ISSET(rr0, ZSRR0_DCD)); + } + + if (ISSET(delta, cs->cs_rr0_cts)) { + /* Block or unblock output according to flow control. */ + if (ISSET(rr0, cs->cs_rr0_cts)) { + zst->zst_tx_stopped = 0; + (*linesw[tp->t_line].l_start)(tp); + } else { + zst->zst_tx_stopped = 1; + } + } +} + +/* + * Software interrupt. Called at zssoft + * + * The main job to be done here is to empty the input ring + * by passing its contents up to the tty layer. The ring is + * always emptied during this operation, therefore the ring + * must not be larger than the space after "high water" in + * the tty layer, or the tty layer might drop our input. + * + * Note: an "input blockage" condition is assumed to exist if + * EITHER the TS_TBLOCK flag or zst_rx_blocked flag is set. + */ +void +zstty_softint(struct zs_chanstate *cs) +{ + struct zstty_softc *zst = cs->cs_private; + struct tty *tp = zst->zst_tty; + int s; + + s = spltty(); + + if (zst->zst_rx_ready) { + zst->zst_rx_ready = 0; + zstty_rxsoft(zst, tp); + } + + if (zst->zst_st_check) { + zst->zst_st_check = 0; + zstty_stsoft(zst, tp); + } + + if (zst->zst_tx_done) { + zst->zst_tx_done = 0; + zstty_txsoft(zst, tp); + } + + splx(s); +} + +struct zsops zsops_tty = { + zstty_rxint, /* receive char available */ + zstty_stint, /* external/status */ + zstty_txint, /* xmit buffer empty */ + zstty_softint, /* process software interrupt */ +}; diff --git a/sys/arch/sgi/hpc/zs.c b/sys/arch/sgi/hpc/zs.c new file mode 100644 index 00000000000..c8ce8b74102 --- /dev/null +++ b/sys/arch/sgi/hpc/zs.c @@ -0,0 +1,712 @@ +/* $OpenBSD: zs.c,v 1.1 2012/03/28 20:44:23 miod Exp $ */ +/* $NetBSD: zs.c,v 1.37 2011/02/20 07:59:50 matt Exp $ */ + +/*- + * Copyright (c) 1996, 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Gordon W. Ross and Wayne Knowles + * + * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/* + * Zilog Z8530 Dual UART driver (machine-dependent part) + * + * Runs two serial lines per chip using slave drivers. + * Plain tty/async lines use the zs_async slave. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/conf.h> +#include <sys/device.h> +#include <sys/file.h> +#include <sys/ioctl.h> +#include <sys/kernel.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/time.h> +#include <sys/syslog.h> + +#include <mips64/archtype.h> +#include <mips64/arcbios.h> + +#include <machine/autoconf.h> +#include <machine/cpu.h> +#include <machine/z8530var.h> + +#include <dev/cons.h> + +#include <dev/ic/z8530reg.h> + +#include <sgi/hpc/hpcvar.h> +#include <sgi/hpc/hpcreg.h> +#include <sgi/localbus/intvar.h> + +/* + * Some warts needed by z8530tty.c - + * The default parity REALLY needs to be the same as the PROM uses, + * or you can not see messages done with printf during boot-up... + */ +int zs_def_cflag = (CREAD | CS8 | HUPCL); +int zs_major = 19; + +#define PCLK 3672000 /* PCLK pin input clock rate */ + +#ifndef ZS_DEFSPEED +#define ZS_DEFSPEED 9600 +#endif + +/* + * Define interrupt levels. + */ +#define ZSHARD_PRI 64 + +/* SGI shouldn't need ZS_DELAY() as recovery time is done in hardware? */ +#define ZS_DELAY() delay(3) + +/* The layout of this is hardware-dependent (padding, order). */ +struct zschan { + uint8_t pad1[3]; + volatile uint8_t zc_csr; /* ctrl,status, and indirect access */ + uint8_t pad2[3]; + volatile uint8_t zc_data; /* data */ +}; + +struct zsdevice { + struct zschan zs_chan_b; + struct zschan zs_chan_a; +}; + +/* Return the byte offset of element within a structure */ +#define OFFSET(struct_def, el) ((size_t)&((struct_def *)0)->el) + +#define ZS_CHAN_A OFFSET(struct zsdevice, zs_chan_a) +#define ZS_CHAN_B OFFSET(struct zsdevice, zs_chan_b) +#define ZS_REG_CSR 3 +#define ZS_REG_DATA 7 +static int zs_chan_offset[] = {ZS_CHAN_A, ZS_CHAN_B}; + +cons_decl(zs); +struct consdev zs_cn = { + zscnprobe, + zscninit, + zscngetc, + zscnputc, + zscnpollc, + NULL +}; + + +/* Flags from cninit() */ +static int zs_consunit = -1; +static int zs_conschan = -1; + +/* Default speed for all channels */ +static int zs_defspeed = ZS_DEFSPEED; + +static uint8_t zs_init_reg[16] = { + 0, /* 0: CMD (reset, etc.) */ + 0, /* 1: No interrupts yet. */ + ZSHARD_PRI, /* 2: IVECT */ + ZSWR3_RX_8 | ZSWR3_RX_ENABLE, + ZSWR4_CLK_X16 | ZSWR4_ONESB, + ZSWR5_TX_8 | ZSWR5_TX_ENABLE, + 0, /* 6: TXSYNC/SYNCLO */ + 0, /* 7: RXSYNC/SYNCHI */ + 0, /* 8: alias for data port */ + ZSWR9_MASTER_IE, + 0, /*10: Misc. TX/RX control bits */ + ZSWR11_TXCLK_BAUD | ZSWR11_RXCLK_BAUD | ZSWR11_TRXC_OUT_ENA, + BPS_TO_TCONST(PCLK/16, ZS_DEFSPEED), /*12: BAUDLO (default=9600) */ + 0, /*13: BAUDHI (default=9600) */ + ZSWR14_BAUD_ENA, + ZSWR15_BREAK_IE, +}; + + +/**************************************************************** + * Autoconfig + ****************************************************************/ + +/* Definition of the driver for autoconfig. */ +int zs_hpc_match(struct device *, void *, void *); +void zs_hpc_attach(struct device *, struct device *, void *); +int zs_print(void *, const char *name); + +struct cfdriver zs_cd = { + NULL, "zs", DV_TTY +}; + +struct cfattach zs_hpc_ca = { + sizeof(struct zsc_softc), zs_hpc_match, zs_hpc_attach +}; + +int zshard(void *); +void zssoft(void *); +struct zschan *zs_get_chan_addr(int, int); +int zs_getc(void *); +void zs_putc(void *, int); + +/* + * Is the zs chip present? + */ +int +zs_hpc_match(struct device *parent, void *vcf, void *aux) +{ + struct cfdata *cf = vcf; + struct hpc_attach_args *ha = aux; + + if (strcmp(ha->ha_name, cf->cf_driver->cd_name) == 0) + return (1); + + return (0); +} + +/* + * Attach a found zs. + * + * Match slave number to zs unit number, so that misconfiguration will + * not set up the keyboard as ttya, etc. + */ +void +zs_hpc_attach(struct device *parent, struct device *self, void *aux) +{ + struct zsc_softc *zsc = (void *)self; + struct hpc_attach_args *haa = aux; + struct zsc_attach_args zsc_args; + struct zs_chanstate *cs; + struct zs_channel *ch; + int zs_unit, channel, err, s; + + zsc->zsc_bustag = haa->ha_st; + if ((err = bus_space_subregion(haa->ha_st, haa->ha_sh, + haa->ha_devoff, 0x10, + &zsc->zsc_base)) != 0) { + printf(": unable to map 85c30 registers, error = %d\n", + err); + return; + } + + zs_unit = zsc->zsc_dev.dv_unit; + printf("\n"); + + /* + * Initialize software state for each channel. + * + * Done in reverse order of channels since the first serial port + * is actually attached to the *second* channel, and vice versa. + * Doing it this way should force a 'zstty*' to attach zstty0 to + * channel 1 and zstty1 to channel 0. They couldn't have wired + * it up in a more sensible fashion, could they? + */ + for (channel = 1; channel >= 0; channel--) { + zsc_args.channel = channel; + ch = &zsc->zsc_cs_store[channel]; + cs = zsc->zsc_cs[channel] = (struct zs_chanstate *)ch; + + cs->cs_reg_csr = NULL; + cs->cs_reg_data = NULL; + cs->cs_channel = channel; + cs->cs_private = NULL; + cs->cs_ops = &zsops_null; + cs->cs_brg_clk = PCLK / 16; + + if (bus_space_subregion(zsc->zsc_bustag, zsc->zsc_base, + zs_chan_offset[channel], + sizeof(struct zschan), + &ch->cs_regs) != 0) { + printf("%s: cannot map regs\n", self->dv_xname); + return; + } + ch->cs_bustag = zsc->zsc_bustag; + + memcpy(cs->cs_creg, zs_init_reg, 16); + memcpy(cs->cs_preg, zs_init_reg, 16); + + /* If console, don't stomp speed, let zstty know */ + if (zs_unit == zs_consunit && channel == zs_conschan) { + zsc_args.consdev = &zs_cn; + zsc_args.hwflags = ZS_HWFLAG_CONSOLE; + cs->cs_defspeed = bios_consrate; + } else { + zsc_args.consdev = NULL; + zsc_args.hwflags = 0; + cs->cs_defspeed = zs_defspeed; + } + + cs->cs_defcflag = zs_def_cflag; + + /* Make these correspond to cs_defcflag (-crtscts) */ + cs->cs_rr0_dcd = ZSRR0_DCD; + cs->cs_rr0_cts = 0; + cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS; + cs->cs_wr5_rts = 0; + + /* + * Clear the master interrupt enable. + * The INTENA is common to both channels, + * so just do it on the A channel. + */ + if (channel == 0) { + zs_write_reg(cs, 9, 0); + } + /* + * Look for a child driver for this channel. + * The child attach will setup the hardware. + */ + if (!config_found(self, (void *)&zsc_args, zs_print)) { + /* No sub-driver. Just reset it. */ + uint8_t reset = (channel == 0) ? + ZSWR9_A_RESET : ZSWR9_B_RESET; + + s = splhigh(); + zs_write_reg(cs, 9, reset); + splx(s); + } + } + + + zsc->sc_si = softintr_establish(SI_SOFTTTY, zssoft, zsc); + int2_intr_establish(haa->ha_irq, IPL_TTY, zshard, zsc, self->dv_xname); + + /* + * Set the master interrupt enable and interrupt vector. + * (common to both channels, do it on A) + */ + cs = zsc->zsc_cs[0]; + s = splhigh(); + /* interrupt vector */ + zs_write_reg(cs, 2, zs_init_reg[2]); + /* master interrupt control (enable) */ + zs_write_reg(cs, 9, zs_init_reg[9]); + splx(s); +} + +int +zs_print(void *aux, const char *name) +{ + struct zsc_attach_args *args = aux; + + if (name != NULL) + printf("%s: ", name); + + if (args->channel != -1) + printf(" channel %d", args->channel); + + return UNCONF; +} + +/* + * Our ZS chips all share a common, autovectored interrupt, + * so we have to look at all of them on each interrupt. + */ +int +zshard(void *arg) +{ + struct zsc_softc *zsc = arg; + int rr3, rval; + + rval = 0; + while ((rr3 = zsc_intr_hard(zsc))) { + rval |= rr3; + } + + if (zsc->zsc_cs[0]->cs_softreq || + zsc->zsc_cs[1]->cs_softreq) + softintr_schedule(zsc->sc_si); + + return rval; +} + +/* + * Similar scheme as for zshard (look at all of them) + */ +void +zssoft(void *arg) +{ + struct zsc_softc *zsc = arg; + int s; + + /* Make sure we call the tty layer at spltty. */ + s = spltty(); + (void) zsc_intr_soft(zsc); + splx(s); +} + + +/* + * MD functions for setting the baud rate and control modes. + */ +int +zs_set_speed(struct zs_chanstate *cs, int bps) +{ + int tconst, real_bps; + + if (bps == 0) + return (0); + +#ifdef DIAGNOSTIC + if (cs->cs_brg_clk == 0) + panic("zs_set_speed"); +#endif + + tconst = BPS_TO_TCONST(cs->cs_brg_clk, bps); + if (tconst < 0) + return (EINVAL); + + /* Convert back to make sure we can do it. */ + real_bps = TCONST_TO_BPS(cs->cs_brg_clk, tconst); + +#if 0 /* PCLK is too small, 9600bps really yields 9562 */ + /* XXX - Allow some tolerance here? */ + if (real_bps != bps) + return (EINVAL); +#endif + + cs->cs_preg[12] = tconst; + cs->cs_preg[13] = tconst >> 8; + + /* Caller will stuff the pending registers. */ + return (0); +} + +int +zs_set_modes(struct zs_chanstate *cs, int cflag) +{ + int s; + + /* + * Output hardware flow control on the chip is horrendous: + * if carrier detect drops, the receiver is disabled, and if + * CTS drops, the transmitter is stoped IN MID CHARACTER! + * Therefore, NEVER set the HFC bit, and instead use the + * status interrupt to detect CTS changes. + */ + s = splzs(); + cs->cs_rr0_pps = 0; + if ((cflag & (CLOCAL | MDMBUF)) != 0) { + cs->cs_rr0_dcd = 0; + if ((cflag & MDMBUF) == 0) + cs->cs_rr0_pps = ZSRR0_DCD; + } else + cs->cs_rr0_dcd = ZSRR0_DCD; + if ((cflag & CRTSCTS) != 0) { + cs->cs_wr5_dtr = ZSWR5_DTR; + cs->cs_wr5_rts = ZSWR5_RTS; + cs->cs_rr0_cts = ZSRR0_CTS; + } else if ((cflag & MDMBUF) != 0) { + cs->cs_wr5_dtr = 0; + cs->cs_wr5_rts = ZSWR5_DTR; + cs->cs_rr0_cts = ZSRR0_DCD; + } else { + cs->cs_wr5_dtr = ZSWR5_DTR | ZSWR5_RTS; + cs->cs_wr5_rts = 0; + cs->cs_rr0_cts = 0; + } + splx(s); + + /* Caller will stuff the pending registers. */ + return (0); +} + + +/* + * Read or write the chip with suitable delays. + */ + +uint8_t +zs_read_reg(struct zs_chanstate *cs, uint8_t reg) +{ + uint8_t val; + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, reg); + ZS_DELAY(); + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR); + ZS_DELAY(); + return val; +} + +void +zs_write_reg(struct zs_chanstate *cs, uint8_t reg, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, reg); + ZS_DELAY(); + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, val); + ZS_DELAY(); +} + +uint8_t +zs_read_csr(struct zs_chanstate *cs) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + uint8_t val; + + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR); + ZS_DELAY(); + return val; +} + +void +zs_write_csr(struct zs_chanstate *cs, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_CSR, val); + ZS_DELAY(); +} + +uint8_t +zs_read_data(struct zs_chanstate *cs) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + uint8_t val; + + val = bus_space_read_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_DATA); + ZS_DELAY(); + return val; +} + +void +zs_write_data(struct zs_chanstate *cs, uint8_t val) +{ + struct zs_channel *zsc = (struct zs_channel *)cs; + + bus_space_write_1(zsc->cs_bustag, zsc->cs_regs, ZS_REG_DATA, val); + ZS_DELAY(); +} + +void +zs_abort(struct zs_chanstate *cs) +{ +#if defined(KGDB) + zskgdb(cs); +#elif defined(DDB) + Debugger(); +#endif +} + + +/*********************************************************/ +/* Polled character I/O functions for console and KGDB */ +/*********************************************************/ + +struct zschan * +zs_get_chan_addr(int zs_unit, int channel) +{ +#if 0 + static int dumped_addr = 0; +#endif + struct zsdevice *addr = NULL; + struct zschan *zc; + + switch (sys_config.system_type) { + case SGI_IP20: + switch (zs_unit) { + case 0: + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fb80d00, CCA_NC); + break; + case 1: + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fb80d10, CCA_NC); + break; + } + break; + + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (zs_unit == 0) + addr = (struct zsdevice *) + PHYS_TO_XKPHYS(0x1fbd9830, CCA_NC); + break; + } + if (addr == NULL) + panic("zs_get_chan_addr: bad zs_unit %d\n", zs_unit); + + /* + * We need to swap serial ports to match reality on + * non-keyboard channels. + */ + if (sys_config.system_type != SGI_IP20) { + if (channel == 0) + zc = &addr->zs_chan_b; + else + zc = &addr->zs_chan_a; + } else { + if (zs_unit == 0) { + if (channel == 0) + zc = &addr->zs_chan_a; + else + zc = &addr->zs_chan_b; + } else { + if (channel == 0) + zc = &addr->zs_chan_b; + else + zc = &addr->zs_chan_a; + } + } + +#if 0 + if (dumped_addr == 0) { + dumped_addr++; + printf("zs unit %d, channel %d had address %p\n", + zs_unit, channel, zc); + } +#endif + + return (zc); +} + +int +zs_getc(void *arg) +{ + register volatile struct zschan *zc = arg; + register int s, c, rr0; + + s = splzs(); + /* Wait for a character to arrive. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_RX_READY) == 0); + + c = zc->zc_data; + ZS_DELAY(); + splx(s); + + return (c); +} + +/* + * Polled output char. + */ +void +zs_putc(void *arg, int c) +{ + register volatile struct zschan *zc = arg; + register int s, rr0; + + s = splzs(); + /* Wait for transmitter to become ready. */ + do { + rr0 = zc->zc_csr; + ZS_DELAY(); + } while ((rr0 & ZSRR0_TX_READY) == 0); + + zc->zc_data = c; + __asm__ __volatile__ ("sync" ::: "memory"); /* wbflush(); */ + ZS_DELAY(); + splx(s); +} + +/***************************************************************/ + +static int cons_port; + +void +zscnprobe(struct consdev *cp) +{ + cp->cn_dev = makedev(zs_major, 0); + cp->cn_pri = CN_DEAD; + + switch (sys_config.system_type) { + case SGI_IP20: + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + if (strlen(bios_console) == 9 && + strncmp(bios_console, "serial", 6) == 0) + cp->cn_pri = CN_FORCED; + else + cp->cn_pri = CN_MIDPRI; + break; + } +} + +void +zscninit(struct consdev *cn) +{ + if (strlen(bios_console) == 9 && + strncmp(bios_console, "serial", 6) != 0) + cons_port = bios_console[7] - '0'; + + /* Mark this unit as the console */ + zs_consunit = 0; + + /* SGI hardware wires serial port 1 to channel B, port 2 to A */ + if (cons_port == 0) + zs_conschan = 1; + else + zs_conschan = 0; +} + +int +zscngetc(dev_t dev) +{ + struct zschan *zs; + + switch (sys_config.system_type) { + case SGI_IP20: + zs = zs_get_chan_addr(1, cons_port); + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + default: + zs = zs_get_chan_addr(0, cons_port); + break; + } + + return zs_getc(zs); +} + +void +zscnputc(dev_t dev, int c) +{ + struct zschan *zs; + + switch (sys_config.system_type) { + case SGI_IP20: + zs = zs_get_chan_addr(1, cons_port); + break; + case SGI_IP22: + case SGI_IP26: + case SGI_IP28: + default: + zs = zs_get_chan_addr(0, cons_port); + break; + } + + zs_putc(zs, c); +} + +void +zscnpollc(dev_t dev, int on) +{ +} |