summaryrefslogtreecommitdiffstats
path: root/sys/arch/sgi/hpc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sgi/hpc')
-rw-r--r--sys/arch/sgi/hpc/dpclock.c312
-rw-r--r--sys/arch/sgi/hpc/dsclock.c192
-rw-r--r--sys/arch/sgi/hpc/files.hpc56
-rw-r--r--sys/arch/sgi/hpc/hpc.c822
-rw-r--r--sys/arch/sgi/hpc/hpcdma.c208
-rw-r--r--sys/arch/sgi/hpc/hpcdma.h63
-rw-r--r--sys/arch/sgi/hpc/hpcreg.h450
-rw-r--r--sys/arch/sgi/hpc/hpcvar.h109
-rw-r--r--sys/arch/sgi/hpc/if_sq.c1348
-rw-r--r--sys/arch/sgi/hpc/if_sqvar.h200
-rw-r--r--sys/arch/sgi/hpc/iocreg.h122
-rw-r--r--sys/arch/sgi/hpc/wdsc.c290
-rw-r--r--sys/arch/sgi/hpc/z8530sc.c409
-rw-r--r--sys/arch/sgi/hpc/z8530sc.h199
-rw-r--r--sys/arch/sgi/hpc/z8530tty.c1663
-rw-r--r--sys/arch/sgi/hpc/zs.c712
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, &regs)
+ 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, &regs);
+ 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, &regs);
+ 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), &reg) != 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), &reg) != 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, &reg) != 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)
+{
+}