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